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.
- checksums.yaml +4 -4
- data/.travis.yml +1 -1
- data/cucumber-core.gemspec +1 -1
- data/lib/cucumber/core.rb +6 -6
- data/lib/cucumber/core/ast/data_table.rb +8 -0
- data/lib/cucumber/core/ast/describes_itself.rb +1 -1
- data/lib/cucumber/core/ast/examples_table.rb +8 -2
- data/lib/cucumber/core/ast/feature.rb +2 -3
- data/lib/cucumber/core/ast/location.rb +11 -6
- data/lib/cucumber/core/ast/names.rb +10 -1
- data/lib/cucumber/core/ast/outline_step.rb +6 -3
- data/lib/cucumber/core/ast/scenario.rb +0 -1
- data/lib/cucumber/core/ast/scenario_outline.rb +2 -3
- data/lib/cucumber/core/ast/step.rb +2 -2
- data/lib/cucumber/core/gherkin/ast_builder.rb +4 -1
- data/lib/cucumber/core/test/case.rb +10 -6
- data/lib/cucumber/core/test/filters/debug_filter.rb +28 -0
- data/lib/cucumber/core/test/filters/tag_filter.rb +1 -1
- data/lib/cucumber/core/test/hooks.rb +76 -0
- data/lib/cucumber/core/test/mapper.rb +101 -19
- data/lib/cucumber/core/test/mapping.rb +15 -4
- data/lib/cucumber/core/test/result.rb +39 -27
- data/lib/cucumber/core/test/runner.rb +76 -81
- data/lib/cucumber/core/test/step.rb +10 -18
- data/lib/cucumber/core/version.rb +1 -1
- data/spec/cucumber/core/ast/data_table_spec.rb +12 -0
- data/spec/cucumber/core/ast/location_spec.rb +8 -1
- data/spec/cucumber/core/ast/outline_step_spec.rb +11 -4
- data/spec/cucumber/core/ast/step_spec.rb +2 -2
- data/spec/cucumber/core/compiler_spec.rb +6 -6
- data/spec/cucumber/core/gherkin/parser_spec.rb +31 -18
- data/spec/cucumber/core/test/case_spec.rb +24 -24
- data/spec/cucumber/core/test/hooks_spec.rb +30 -0
- data/spec/cucumber/core/test/mapper_spec.rb +115 -1
- data/spec/cucumber/core/test/mapping_spec.rb +22 -6
- data/spec/cucumber/core/test/result_spec.rb +0 -8
- data/spec/cucumber/core/test/runner_spec.rb +31 -97
- data/spec/cucumber/core/test/step_spec.rb +24 -16
- data/spec/cucumber/core_spec.rb +109 -16
- data/spec/report_api_spy.rb +24 -0
- metadata +32 -28
- data/lib/cucumber/core/test/hook_compiler.rb +0 -109
- 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(:
|
8
|
+
include Cucumber.initializer(:mapping_definition, :receiver)
|
7
9
|
|
8
10
|
def test_case(test_case, &descend)
|
9
|
-
|
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.
|
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
|
-
|
23
|
+
receiver.done
|
17
24
|
self
|
18
25
|
end
|
19
26
|
|
27
|
+
private
|
28
|
+
|
20
29
|
class CaseMapper
|
21
|
-
include Cucumber.initializer(:
|
30
|
+
include Cucumber.initializer(:mapping_definition)
|
22
31
|
|
23
|
-
|
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
|
26
|
-
|
27
|
-
@test_steps = []
|
40
|
+
def test_steps
|
41
|
+
@test_steps ||= []
|
28
42
|
end
|
29
43
|
|
30
|
-
def
|
31
|
-
|
32
|
-
|
33
|
-
|
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
|
-
|
84
|
+
attr_accessor :test_step
|
42
85
|
|
43
|
-
def
|
44
|
-
|
45
|
-
@mapped_test_step = test_step
|
86
|
+
def after_step_hooks
|
87
|
+
@after_step_hooks ||= []
|
46
88
|
end
|
47
89
|
|
48
|
-
|
49
|
-
|
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::
|
25
|
-
|
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
|
-
|
45
|
-
|
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
|
-
|
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
|
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
|
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
|
-
|
76
|
-
|
77
|
-
|
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 =
|
81
|
-
|
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
|
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 <
|
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
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
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
|
-
|
19
|
-
|
20
|
-
|
14
|
+
def execute(test_step)
|
15
|
+
status.execute(test_step, self)
|
16
|
+
end
|
21
17
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
end
|
18
|
+
def result
|
19
|
+
status.result(@timer.duration)
|
20
|
+
end
|
26
21
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
22
|
+
def failed(step_result)
|
23
|
+
@status = Status::Failing.new(step_result)
|
24
|
+
self
|
25
|
+
end
|
31
26
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
27
|
+
def passed(step_result)
|
28
|
+
@status = Status::Passing.new(step_result)
|
29
|
+
self
|
30
|
+
end
|
36
31
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
32
|
+
def pending(message, step_result)
|
33
|
+
@status = Status::Pending.new(step_result)
|
34
|
+
self
|
35
|
+
end
|
41
36
|
|
42
|
-
|
43
|
-
|
44
|
-
|
37
|
+
def skipped(step_result)
|
38
|
+
@status = Status::Skipping.new(step_result)
|
39
|
+
self
|
40
|
+
end
|
45
41
|
|
46
|
-
|
47
|
-
|
48
|
-
|
42
|
+
def undefined(step_result)
|
43
|
+
failed(step_result)
|
44
|
+
self
|
45
|
+
end
|
49
46
|
|
50
|
-
|
47
|
+
def exception(step_exception, step_result)
|
48
|
+
self
|
49
|
+
end
|
51
50
|
|
52
|
-
|
53
|
-
|
54
|
-
end
|
51
|
+
def duration(step_duration, step_result)
|
52
|
+
self
|
55
53
|
end
|
56
54
|
|
57
|
-
|
58
|
-
|
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
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
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
|
-
|
70
|
-
|
71
|
-
|
72
|
-
result.describe_to(monitor, result)
|
67
|
+
def result
|
68
|
+
raise NoMethodError, "Override me"
|
69
|
+
end
|
73
70
|
end
|
74
71
|
|
75
|
-
|
76
|
-
|
72
|
+
class Unknown < Base
|
73
|
+
def result(duration)
|
74
|
+
Result::Unknown.new
|
75
|
+
end
|
77
76
|
end
|
78
|
-
end
|
79
77
|
|
80
|
-
|
81
|
-
|
82
|
-
|
78
|
+
class Passing < Base
|
79
|
+
def result(duration)
|
80
|
+
Result::Passed.new(duration)
|
81
|
+
end
|
83
82
|
end
|
84
|
-
end
|
85
83
|
|
86
|
-
|
87
|
-
|
88
|
-
|
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
|
-
|
92
|
-
|
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
|
-
|
100
|
-
|
101
|
-
|
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 ||=
|
139
|
+
@current_step_runner ||= StepRunner.new
|
145
140
|
end
|
146
141
|
end
|
147
142
|
end
|