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
@@ -1,55 +1,137 @@
1
1
  require 'cucumber/initializer'
2
+ require 'cucumber/core/test/hooks'
3
+
2
4
  module Cucumber
3
5
  module Core
4
6
  module Test
5
7
  class Mapper
6
- include Cucumber.initializer(:mappings, :runner)
8
+ include Cucumber.initializer(:mapping_definition, :receiver)
7
9
 
8
10
  def test_case(test_case, &descend)
9
- mapper = CaseMapper.new(mappings)
11
+ hook_factory = HookFactory.new(test_case.source)
12
+ mapper = CaseMapper.new(mapping_definition)
13
+ test_case.describe_to mapping_definition, CaseMapper::DSL.new(mapper, hook_factory)
10
14
  descend.call(mapper)
11
- test_case.with_steps(mapper.test_steps).describe_to(runner)
15
+ test_case.
16
+ with_steps(mapper.before_hooks + mapper.test_steps + mapper.after_hooks).
17
+ with_around_hooks(mapper.around_hooks).
18
+ describe_to(receiver)
12
19
  self
13
20
  end
14
21
 
15
22
  def done
16
- runner.done
23
+ receiver.done
17
24
  self
18
25
  end
19
26
 
27
+ private
28
+
20
29
  class CaseMapper
21
- include Cucumber.initializer(:mappings)
30
+ include Cucumber.initializer(:mapping_definition)
22
31
 
23
- attr_reader :test_steps
32
+ def test_step(test_step)
33
+ hook_factory = HookFactory.new(test_step.source)
34
+ mapper = StepMapper.new(test_step)
35
+ test_step.describe_to mapping_definition, StepMapper::DSL.new(mapper, hook_factory)
36
+ test_steps.push(*[mapper.test_step] + mapper.after_step_hooks)
37
+ self
38
+ end
24
39
 
25
- def initialize(*)
26
- super
27
- @test_steps = []
40
+ def test_steps
41
+ @test_steps ||= []
28
42
  end
29
43
 
30
- def test_step(test_step)
31
- mapper = StepMapper.new(test_step)
32
- test_step.describe_to(mappings, mapper)
33
- test_steps << mapper.mapped_test_step
44
+ def around_hooks
45
+ @around_hooks ||= []
46
+ end
47
+
48
+ def before_hooks
49
+ @before_hooks ||= []
50
+ end
51
+
52
+ def after_hooks
53
+ @after_hooks ||= []
34
54
  end
35
55
 
56
+ # Passed to users in the mappings to add hooks to a scenario
57
+ class DSL
58
+ include Cucumber.initializer(:mapper, :hook_factory)
59
+
60
+ # Run this block of code before the scenario
61
+ def before(&block)
62
+ mapper.before_hooks << hook_factory.before(block)
63
+ self
64
+ end
65
+
66
+ # Run this block of code after the scenario
67
+ def after(&block)
68
+ mapper.after_hooks << hook_factory.after(block)
69
+ self
70
+ end
71
+
72
+ # Run this block of code around the scenario, with a yield in the block executing the scenario
73
+ def around(&block)
74
+ mapper.around_hooks << Hooks::AroundHook.new(&block)
75
+ self
76
+ end
77
+
78
+ end
36
79
  end
37
80
 
38
81
  class StepMapper
39
82
  include Cucumber.initializer(:test_step)
40
83
 
41
- attr_reader :mapped_test_step
84
+ attr_accessor :test_step
42
85
 
43
- def initialize(*)
44
- super
45
- @mapped_test_step = test_step
86
+ def after_step_hooks
87
+ @after_step_hooks ||= []
46
88
  end
47
89
 
48
- def map(&block)
49
- @mapped_test_step = test_step.map(&block)
90
+ # Passed to users in the mappings to define and add hooks to a step
91
+ class DSL
92
+ include Cucumber.initializer(:mapper, :hook_factory)
93
+
94
+ # Define the step with a block of code to be executed
95
+ def map(&block)
96
+ mapper.test_step = mapper.test_step.with_mapping(&block)
97
+ self
98
+ end
99
+
100
+ # Define a block of code to be run after the step
101
+ def after(&block)
102
+ mapper.after_step_hooks << hook_factory.after_step(block)
103
+ self
104
+ end
105
+
106
+ end
107
+ end
108
+
109
+ class HookFactory
110
+ include Cucumber.initializer(:source)
111
+
112
+ def after(block)
113
+ build_hook_step(block, Hooks::AfterHook, Test::UnskippableMapping)
114
+ end
115
+
116
+ def before(block)
117
+ build_hook_step(block, Hooks::BeforeHook, Test::UnskippableMapping)
118
+ end
119
+
120
+ def after_step(block)
121
+ build_hook_step(block, Hooks::AfterStepHook, Test::Mapping)
122
+ end
123
+
124
+ private
125
+
126
+ def build_hook_step(block, hook_type, mapping_type)
127
+ mapping = mapping_type.new(&block)
128
+ hook = hook_type.new(mapping.location)
129
+ Step.new(source + [hook], mapping)
50
130
  end
131
+
51
132
  end
52
133
 
134
+
53
135
  end
54
136
  end
55
137
  end
@@ -1,6 +1,7 @@
1
1
  require 'cucumber/core/test/result'
2
2
  require 'cucumber/core/test/timer'
3
3
  require 'cucumber/core/test/result'
4
+ require 'cucumber/core/ast/location'
4
5
 
5
6
  module Cucumber
6
7
  module Core
@@ -21,12 +22,20 @@ module Cucumber
21
22
  @timer.start
22
23
  @block.call
23
24
  passed
24
- rescue Result::Pending => exception
25
- pending(exception)
25
+ rescue Result::Raisable => exception
26
+ exception.with_duration(@timer.duration)
26
27
  rescue Exception => exception
27
28
  failed(exception)
28
29
  end
29
30
 
31
+ def location
32
+ Ast::Location.new(*@block.source_location)
33
+ end
34
+
35
+ def inspect
36
+ "<#{self.class}: #{location}>"
37
+ end
38
+
30
39
  private
31
40
 
32
41
  def passed
@@ -40,9 +49,11 @@ module Cucumber
40
49
  def skipped
41
50
  Result::Skipped.new
42
51
  end
52
+ end
43
53
 
44
- def pending(exception)
45
- exception.with_duration(@timer.duration)
54
+ class UnskippableMapping < Mapping
55
+ def skip
56
+ execute
46
57
  end
47
58
  end
48
59
 
@@ -5,6 +5,9 @@ module Cucumber
5
5
  module Core
6
6
  module Test
7
7
  module Result
8
+
9
+ # Defines predicate methods on a result class with only the given one
10
+ # returning true
8
11
  def self.status_queries(status)
9
12
  Module.new do
10
13
  [:passed, :failed, :undefined, :unknown, :skipped, :pending].each do |possible_status|
@@ -15,7 +18,8 @@ module Cucumber
15
18
  end
16
19
  end
17
20
 
18
- Unknown = Class.new do
21
+ # Null object for results. Represents the state where we haven't run anything yet
22
+ class Unknown
19
23
  include Result.status_queries :unknown
20
24
 
21
25
  def describe_to(visitor, *args)
@@ -24,7 +28,7 @@ module Cucumber
24
28
  end
25
29
 
26
30
  class Passed
27
- include Result.status_queries :passed
31
+ include Result.status_queries(:passed)
28
32
  include Cucumber.initializer(:duration)
29
33
  attr_reader :duration
30
34
 
@@ -45,7 +49,7 @@ module Cucumber
45
49
  end
46
50
 
47
51
  class Failed
48
- include Result.status_queries :failed
52
+ include Result.status_queries(:failed)
49
53
  include Cucumber.initializer(:duration, :exception)
50
54
  attr_reader :duration, :exception
51
55
 
@@ -72,52 +76,54 @@ module Cucumber
72
76
 
73
77
  end
74
78
 
75
- Undefined = Class.new do
76
- include Result.status_queries :undefined
77
- include Cucumber.initializer(:duration)
78
- attr_reader :duration
79
+ # Base class for exceptions that can be raised in a step defintion causing
80
+ # the step to have that result.
81
+ class Raisable < StandardError
82
+ attr_reader :message, :duration
79
83
 
80
- def initialize(duration = 0)
81
- super
84
+ def initialize(message = "", duration = :unknown, backtrace = nil)
85
+ @message, @duration = message, duration
86
+ super(message)
87
+ set_backtrace(backtrace) if backtrace
88
+ end
89
+
90
+ def with_duration(new_duration)
91
+ self.class.new(message, new_duration, backtrace)
82
92
  end
93
+ end
94
+
95
+ class Undefined < Raisable
96
+ include Result.status_queries :undefined
83
97
 
84
98
  def describe_to(visitor, *args)
85
99
  visitor.undefined(*args)
100
+ visitor.duration(duration, *args) unless duration == :unknown
86
101
  self
87
102
  end
88
103
 
89
104
  def to_s
90
- ""
105
+ "?"
91
106
  end
92
107
 
93
- def with_duration(new_duration)
94
- self.class.new(new_duration)
95
- end
96
108
  end
97
109
 
98
- Skipped = Class.new do
110
+ class Skipped < Raisable
99
111
  include Result.status_queries :skipped
100
112
 
101
113
  def describe_to(visitor, *args)
102
114
  visitor.skipped(*args)
115
+ visitor.duration(duration, *args) unless duration == :unknown
103
116
  self
104
117
  end
105
118
 
106
119
  def to_s
107
120
  "-"
108
121
  end
122
+
109
123
  end
110
124
 
111
- class Pending < StandardError
125
+ class Pending < Raisable
112
126
  include Result.status_queries :pending
113
- attr_reader :message, :duration
114
-
115
- def initialize(message, duration = :unknown, backtrace=nil)
116
- raise ArgumentError unless message
117
- @message, @duration = message, duration
118
- super(message)
119
- set_backtrace(backtrace) if backtrace
120
- end
121
127
 
122
128
  def describe_to(visitor, *args)
123
129
  visitor.pending(self, *args)
@@ -128,12 +134,18 @@ module Cucumber
128
134
  def to_s
129
135
  "P"
130
136
  end
131
-
132
- def with_duration(new_duration)
133
- self.class.new(message, new_duration, backtrace)
134
- end
135
137
  end
136
138
 
139
+ #
140
+ # An object that responds to the description protocol from the results
141
+ # and collects summary information.
142
+ #
143
+ # e.g.
144
+ # summary = Result::Summary.new
145
+ # Result::Passed.new(0).describe_to(summary)
146
+ # puts summary.total_passed
147
+ # => 1
148
+ #
137
149
  class Summary
138
150
  attr_reader :total_failed,
139
151
  :total_passed,
@@ -5,116 +5,111 @@ module Cucumber
5
5
  module Core
6
6
  module Test
7
7
  class Runner
8
- module StepRunner
9
- class Default
10
- def initialize
11
- @timer = Timer.new.start
12
- end
13
-
14
- def execute(test_step)
15
- status.execute(test_step, self)
16
- end
8
+ class StepRunner
9
+ def initialize
10
+ @timer = Timer.new.start
11
+ @status = Status::Unknown.new(Result::Unknown.new)
12
+ end
17
13
 
18
- def result
19
- status.result(@timer.duration)
20
- end
14
+ def execute(test_step)
15
+ status.execute(test_step, self)
16
+ end
21
17
 
22
- def failed(step_result)
23
- @status = Failing.new(step_result)
24
- self
25
- end
18
+ def result
19
+ status.result(@timer.duration)
20
+ end
26
21
 
27
- def passed(step_result)
28
- @status = Passing.new
29
- self
30
- end
22
+ def failed(step_result)
23
+ @status = Status::Failing.new(step_result)
24
+ self
25
+ end
31
26
 
32
- def pending(message, step_result)
33
- @status = Pending.new(step_result)
34
- self
35
- end
27
+ def passed(step_result)
28
+ @status = Status::Passing.new(step_result)
29
+ self
30
+ end
36
31
 
37
- def undefined(step_result)
38
- failed(step_result)
39
- self
40
- end
32
+ def pending(message, step_result)
33
+ @status = Status::Pending.new(step_result)
34
+ self
35
+ end
41
36
 
42
- def exception(step_exception, step_result)
43
- self
44
- end
37
+ def skipped(step_result)
38
+ @status = Status::Skipping.new(step_result)
39
+ self
40
+ end
45
41
 
46
- def duration(step_duration, step_result)
47
- self
48
- end
42
+ def undefined(step_result)
43
+ failed(step_result)
44
+ self
45
+ end
49
46
 
50
- private
47
+ def exception(step_exception, step_result)
48
+ self
49
+ end
51
50
 
52
- def status
53
- @status ||= Unknown.new
54
- end
51
+ def duration(step_duration, step_result)
52
+ self
55
53
  end
56
54
 
57
- class DryRun
58
- def execute(test_step)
59
- step_result = test_step.skip
60
- @case_result = Result::Undefined.new if step_result.undefined?
61
- step_result
62
- end
55
+ attr_reader :status
56
+ private :status
63
57
 
64
- def result
65
- @case_result ||= Result::Skipped.new
66
- end
67
- end
58
+ module Status
59
+ class Base
60
+ include Cucumber.initializer(:step_result)
61
+
62
+ def execute(test_step, monitor)
63
+ result = test_step.execute
64
+ result.describe_to(monitor, result)
65
+ end
68
66
 
69
- class Unknown
70
- def execute(test_step, monitor)
71
- result = test_step.execute
72
- result.describe_to(monitor, result)
67
+ def result
68
+ raise NoMethodError, "Override me"
69
+ end
73
70
  end
74
71
 
75
- def result(duration)
76
- Result::Unknown.new
72
+ class Unknown < Base
73
+ def result(duration)
74
+ Result::Unknown.new
75
+ end
77
76
  end
78
- end
79
77
 
80
- class Passing < Unknown
81
- def result(duration)
82
- Result::Passed.new(duration)
78
+ class Passing < Base
79
+ def result(duration)
80
+ Result::Passed.new(duration)
81
+ end
83
82
  end
84
- end
85
83
 
86
- Failing = Struct.new(:step_result) do
87
- def execute(test_step, monitor)
88
- test_step.skip
84
+ class Failing < Base
85
+ def execute(test_step, monitor)
86
+ test_step.skip
87
+ end
88
+
89
+ def result(duration)
90
+ step_result.with_duration(duration)
91
+ end
89
92
  end
90
93
 
91
- def result(duration)
92
- step_result.with_duration(duration)
94
+ Pending = Class.new(Failing)
95
+
96
+ class Skipping < Failing
97
+ def result(duration)
98
+ step_result.with_duration(duration)
99
+ end
93
100
  end
94
101
  end
95
-
96
- Pending = Class.new(Failing)
97
102
  end
98
103
 
99
- STEP_RUNNER_STRATEGY = {
100
- default: StepRunner::Default,
101
- dry_run: StepRunner::DryRun
102
- }
103
-
104
- attr_reader :report, :step_runner_class
105
- private :report, :step_runner_class
106
- def initialize(report, run_options = {})
104
+ attr_reader :report
105
+ private :report
106
+ def initialize(report)
107
107
  @report = report
108
-
109
- run_mode = run_options.fetch(:run_mode) { :default }
110
- @step_runner_class = STEP_RUNNER_STRATEGY.fetch(run_mode) do
111
- raise ArgumentError, "No strategy for run mode: #{run_mode.inspect}"
112
- end
113
108
  end
114
109
 
115
110
  def test_case(test_case, &descend)
116
111
  report.before_test_case(test_case)
117
- descend.call
112
+ descend.call(self)
118
113
  report.after_test_case(test_case, current_case_result)
119
114
  @current_step_runner = nil
120
115
  end
@@ -141,7 +136,7 @@ module Cucumber
141
136
  end
142
137
 
143
138
  def current_step_runner
144
- @current_step_runner ||= step_runner_class.new
139
+ @current_step_runner ||= StepRunner.new
145
140
  end
146
141
  end
147
142
  end