gurke 2.0.0.dev.1.b17 → 2.0.0.dev.1.b18
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/lib/gurke.rb +8 -0
- data/lib/gurke/background.rb +17 -6
- data/lib/gurke/builder.rb +28 -23
- data/lib/gurke/feature.rb +48 -13
- data/lib/gurke/feature_list.rb +37 -0
- data/lib/gurke/reporter.rb +224 -63
- data/lib/gurke/reporters/default_reporter.rb +95 -0
- data/lib/gurke/reporters/null_reporter.rb +12 -0
- data/lib/gurke/run_list.rb +18 -0
- data/lib/gurke/runner.rb +12 -117
- data/lib/gurke/scenario.rb +75 -14
- data/lib/gurke/step.rb +72 -0
- data/spec/spec_helper.rb +17 -0
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
MTliYzQwOGExYjAzODMxMTE3YWZhMmU1ZDhmZDM5YjZjZWM5Y2E5ZA==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
ZjdiNTE1MDQ5NTk5ZmY4YTIwZGZhZWM2MmRhZGYwMWQ1YWRhZjVjYQ==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
Y2EzNTBhOTE4OTFmOWQ2ZjU0NzI1MWY2YjhhMGE1N2EyNGJiNjNlYTA5NTNl
|
10
|
+
MmVmM2Y0NWM4NTVhYzIxNDY0OTY5ZDBiZTZkYTIwODAwMDljMzIxNTNkMTYy
|
11
|
+
Y2JmZmZiZjc3ZTYwNTdkMzlhNzgyOTMwZjk3NzEzZWIzNDRjNGU=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
NGFiZmE4ODNkMDkxODY3ZWZjYTE1ODBkYzNkYjY4MWU1NTgwNzYxZDEzY2Y4
|
14
|
+
ZTBlOWY3M2U2ZWQ2MGFjODFiYThhNDEyOWYzZmU3MWQ0M2JlMzVjMTBhZjVh
|
15
|
+
YzYwNDNhZGNmOTFjZmQ2YmYxYzRkZjU1MmM5ZDdlMDkyMzg2NTg=
|
data/lib/gurke.rb
CHANGED
@@ -8,6 +8,9 @@ module Gurke
|
|
8
8
|
require 'gurke/step'
|
9
9
|
require 'gurke/tag'
|
10
10
|
|
11
|
+
require 'gurke/run_list'
|
12
|
+
require 'gurke/feature_list'
|
13
|
+
|
11
14
|
require 'gurke/dsl'
|
12
15
|
require 'gurke/builder'
|
13
16
|
require 'gurke/configuration'
|
@@ -16,6 +19,11 @@ module Gurke
|
|
16
19
|
require 'gurke/step_definition'
|
17
20
|
require 'gurke/reporter'
|
18
21
|
|
22
|
+
module Reporters
|
23
|
+
require 'gurke/reporters/null_reporter'
|
24
|
+
require 'gurke/reporters/default_reporter'
|
25
|
+
end
|
26
|
+
|
19
27
|
class Error < StandardError; end
|
20
28
|
class StepPending < Error; end
|
21
29
|
class StepAmbiguous < Error; end
|
data/lib/gurke/background.rb
CHANGED
@@ -14,20 +14,31 @@ module Gurke
|
|
14
14
|
#
|
15
15
|
attr_reader :line
|
16
16
|
|
17
|
+
# List of steps this background specifies.
|
18
|
+
#
|
19
|
+
# @return [Array<Step>] Steps.
|
20
|
+
#
|
21
|
+
attr_reader :steps
|
22
|
+
|
17
23
|
# @api private
|
18
24
|
attr_reader :raw
|
19
25
|
|
20
26
|
# @api private
|
21
27
|
def initialize(file, line, raw)
|
22
|
-
@file
|
28
|
+
@file = file
|
29
|
+
@line = line
|
30
|
+
@raw = raw
|
31
|
+
@steps = RunList.new
|
23
32
|
end
|
24
33
|
|
25
|
-
#
|
26
|
-
#
|
27
|
-
# @return [Array<Step>] Steps.
|
34
|
+
# @api private
|
28
35
|
#
|
29
|
-
def
|
30
|
-
|
36
|
+
def run(runner, reporter, scenario, world)
|
37
|
+
reporter.invoke :start_background, self, scenario
|
38
|
+
|
39
|
+
steps.run runner, reporter, scenario, world
|
40
|
+
ensure
|
41
|
+
reporter.invoke :end_background, self, scenario
|
31
42
|
end
|
32
43
|
end
|
33
44
|
end
|
data/lib/gurke/builder.rb
CHANGED
@@ -8,7 +8,7 @@ module Gurke
|
|
8
8
|
|
9
9
|
def initialize(options)
|
10
10
|
@options = options
|
11
|
-
@features =
|
11
|
+
@features = FeatureList.new
|
12
12
|
@language = 'en'
|
13
13
|
@parser = Gherkin::Parser::Parser.new(
|
14
14
|
self, true, 'root', false, @language)
|
@@ -30,49 +30,54 @@ module Gurke
|
|
30
30
|
end
|
31
31
|
|
32
32
|
def feature(raw)
|
33
|
-
tags = raw.tags.map{|t| Tag.new
|
33
|
+
tags = raw.tags.map{|t| Tag.new @file, t.line, t }
|
34
34
|
|
35
|
-
@
|
36
|
-
@
|
35
|
+
@feature = Feature.new(@file, raw.line, tags, raw)
|
36
|
+
@scenario = nil
|
37
|
+
@type = nil
|
38
|
+
|
39
|
+
features << @feature
|
37
40
|
end
|
38
41
|
|
39
42
|
def background(raw)
|
40
|
-
@
|
41
|
-
@
|
43
|
+
@context = Background.new @file, raw.line, raw
|
44
|
+
@type = nil
|
45
|
+
|
46
|
+
@feature.backgrounds << @context
|
42
47
|
end
|
43
48
|
|
44
49
|
def scenario(raw)
|
45
|
-
tags
|
46
|
-
tags +=
|
50
|
+
tags = raw.tags.map{|t| Tag.new @file, t.line, t }
|
51
|
+
tags += features.last.tags
|
47
52
|
|
48
|
-
@
|
53
|
+
@scenario = Scenario.new @feature, @file, raw.line, tags, raw
|
54
|
+
@context = @scenario
|
55
|
+
@type = nil
|
49
56
|
|
50
|
-
unless filtered?(@
|
51
|
-
@current_feature.scenarios << @current_context
|
52
|
-
end
|
57
|
+
@feature.scenarios << @scenario unless filtered?(@scenario)
|
53
58
|
end
|
54
59
|
|
55
60
|
def step(raw)
|
56
|
-
type =
|
61
|
+
@type = lookup_type raw.keyword.strip
|
57
62
|
|
58
|
-
@
|
63
|
+
@context.steps << Step.new(@file, raw.line, @type, raw)
|
59
64
|
end
|
60
65
|
|
61
66
|
def eof(*)
|
62
67
|
@features.reject!{|f| f.scenarios.empty? }
|
63
|
-
@
|
64
|
-
@
|
65
|
-
@
|
68
|
+
@feature = nil
|
69
|
+
@scenario = nil
|
70
|
+
@context = nil
|
71
|
+
@type = nil
|
72
|
+
@file = nil
|
66
73
|
end
|
67
74
|
|
68
|
-
|
75
|
+
private
|
76
|
+
|
77
|
+
def lookup_type(keyword)
|
69
78
|
case (kw = @keywords.fetch(keyword))
|
70
79
|
when :and, :but
|
71
|
-
|
72
|
-
step.type
|
73
|
-
else
|
74
|
-
nil
|
75
|
-
end
|
80
|
+
@type
|
76
81
|
else
|
77
82
|
kw
|
78
83
|
end
|
data/lib/gurke/feature.rb
CHANGED
@@ -14,6 +14,18 @@ module Gurke
|
|
14
14
|
#
|
15
15
|
attr_reader :line
|
16
16
|
|
17
|
+
# List of scenarios this feature specifies.
|
18
|
+
#
|
19
|
+
# @return [Array<Scenario>] Scenarios.
|
20
|
+
#
|
21
|
+
attr_reader :scenarios
|
22
|
+
|
23
|
+
# List of backgrounds this feature specifies.
|
24
|
+
#
|
25
|
+
# @return [Array<Background>] Backgrounds.
|
26
|
+
#
|
27
|
+
attr_reader :backgrounds
|
28
|
+
|
17
29
|
attr_reader :tags
|
18
30
|
|
19
31
|
# @api private
|
@@ -21,7 +33,13 @@ module Gurke
|
|
21
33
|
|
22
34
|
# @api private
|
23
35
|
def initialize(file, line, tags, raw)
|
24
|
-
@
|
36
|
+
@scenarios = RunList.new
|
37
|
+
@backgrounds = RunList.new
|
38
|
+
|
39
|
+
@file = file
|
40
|
+
@line = line
|
41
|
+
@tags = tags
|
42
|
+
@raw = raw
|
25
43
|
end
|
26
44
|
|
27
45
|
def name
|
@@ -32,18 +50,6 @@ module Gurke
|
|
32
50
|
raw.description
|
33
51
|
end
|
34
52
|
|
35
|
-
# Return list of scenarios this feature specifies.
|
36
|
-
#
|
37
|
-
# @return [Array<Scenario>] Scenarios.
|
38
|
-
#
|
39
|
-
def scenarios
|
40
|
-
@scenarios ||= []
|
41
|
-
end
|
42
|
-
|
43
|
-
def backgrounds
|
44
|
-
@backgrounds ||= []
|
45
|
-
end
|
46
|
-
|
47
53
|
# Return name of this feature.
|
48
54
|
#
|
49
55
|
# @return [String] Feature name.
|
@@ -51,5 +57,34 @@ module Gurke
|
|
51
57
|
def name
|
52
58
|
raw.name
|
53
59
|
end
|
60
|
+
|
61
|
+
def failed?
|
62
|
+
scenarios.any?(&:failed?)
|
63
|
+
end
|
64
|
+
|
65
|
+
def pending?
|
66
|
+
scenarios.any?(&:pending?)
|
67
|
+
end
|
68
|
+
|
69
|
+
# @api private
|
70
|
+
def run(runner, reporter)
|
71
|
+
reporter.invoke :before_feature, self
|
72
|
+
|
73
|
+
runner.hook :feature, nil do
|
74
|
+
run_feature runner, reporter
|
75
|
+
end
|
76
|
+
ensure
|
77
|
+
reporter.invoke :after_feature
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def run_feature(runner, reporter)
|
83
|
+
reporter.invoke :start_feature, self
|
84
|
+
|
85
|
+
scenarios.run runner, reporter
|
86
|
+
ensure
|
87
|
+
reporter.invoke :end_feature, self
|
88
|
+
end
|
54
89
|
end
|
55
90
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Gurke
|
2
|
+
#
|
3
|
+
# A {FeatureList} is a list of {Feature} objects.
|
4
|
+
#
|
5
|
+
class FeatureList < Array
|
6
|
+
#
|
7
|
+
# Run all features from this list.
|
8
|
+
#
|
9
|
+
# @return [Boolean] False if any scenario has failed or is pending.
|
10
|
+
#
|
11
|
+
# @api private
|
12
|
+
#
|
13
|
+
def run(runner, reporter)
|
14
|
+
reporter.invoke :before_features, self
|
15
|
+
|
16
|
+
runner.hook(:features, nil) do
|
17
|
+
run_features runner, reporter
|
18
|
+
end
|
19
|
+
|
20
|
+
reporter.invoke :after_features, self
|
21
|
+
|
22
|
+
!any?{|s| s.failed? || s.pending? }
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def run_features(runner, reporter)
|
28
|
+
reporter.invoke :start_features, self
|
29
|
+
|
30
|
+
each do |feature|
|
31
|
+
feature.run runner, reporter
|
32
|
+
end
|
33
|
+
ensure
|
34
|
+
reporter.invoke :end_features, self
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/gurke/reporter.rb
CHANGED
@@ -1,98 +1,259 @@
|
|
1
|
-
require 'colorize'
|
2
|
-
|
3
|
-
# Colorize colors:
|
4
|
-
# :black, :red, :green, :yellow, :blue,
|
5
|
-
# :magenta, :cyan, :white, :default, :light_black,
|
6
|
-
# :light_red, :light_green, :light_yellow, :light_blue,
|
7
|
-
# :light_magenta, :light_cyan, :light_white
|
8
|
-
|
9
1
|
module Gurke
|
2
|
+
#
|
3
|
+
# A {Reporter} provides callbacks that will be executed whenever
|
4
|
+
# a specific execution step starts or ends.
|
5
|
+
#
|
6
|
+
# @api public
|
10
7
|
#
|
11
8
|
class Reporter
|
12
|
-
|
9
|
+
# rubocop:disable UnusedMethodArgument
|
10
|
+
|
11
|
+
# List of all callback methods as symbols.
|
12
|
+
#
|
13
|
+
CALLBACKS = [
|
14
|
+
:before_features,
|
15
|
+
:before_feature,
|
16
|
+
:before_scenario,
|
17
|
+
:before_step,
|
18
|
+
:start_features,
|
19
|
+
:start_feature,
|
20
|
+
:start_scenario,
|
21
|
+
:start_background,
|
22
|
+
:start_step,
|
23
|
+
:end_features,
|
24
|
+
:end_feature,
|
25
|
+
:end_scenario,
|
26
|
+
:end_background,
|
27
|
+
:end_step,
|
28
|
+
:after_features,
|
29
|
+
:after_feature,
|
30
|
+
:after_scenario,
|
31
|
+
:after_step
|
32
|
+
]
|
33
|
+
|
34
|
+
# Called before the execution of any feature and before any
|
35
|
+
# before-features hook is invoked.
|
36
|
+
#
|
37
|
+
# @param features [Array<Feature>] List of all features that
|
38
|
+
# are going to be executed.
|
39
|
+
#
|
40
|
+
# @api public
|
41
|
+
#
|
42
|
+
def before_features(features)
|
43
|
+
raise NotImplementedError.new \
|
44
|
+
"#{self.class.name}#before_features must be implemented in subclass."
|
13
45
|
end
|
14
46
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
47
|
+
# Called before the execute of any feature, but after all
|
48
|
+
# before-features hooks.
|
49
|
+
#
|
50
|
+
# @param features [Array<Feature>] List of all features that
|
51
|
+
# are going to be executed.
|
52
|
+
#
|
53
|
+
# @api public
|
54
|
+
#
|
55
|
+
def start_features(features)
|
56
|
+
raise NotImplementedError.new \
|
57
|
+
"#{self.class.name}#before_features must be implemented in subclass."
|
19
58
|
end
|
20
59
|
|
21
|
-
|
22
|
-
|
23
|
-
|
60
|
+
# Called for each feature before it starts, but before any
|
61
|
+
# before-feature hook is run.
|
62
|
+
#
|
63
|
+
# @param feature [Feature] The feature that is going to
|
64
|
+
# be executed now.
|
65
|
+
#
|
66
|
+
# @api public
|
67
|
+
#
|
68
|
+
def before_feature(feature)
|
69
|
+
raise NotImplementedError.new \
|
70
|
+
"#{self.class.name}#start_feature must be implemented in subclass."
|
24
71
|
end
|
25
72
|
|
26
|
-
|
27
|
-
|
73
|
+
# Called for each feature before it starts, but after
|
74
|
+
# all before-feature hooks.
|
75
|
+
#
|
76
|
+
# @param feature [Feature] The feature that is going to
|
77
|
+
# be executed now.
|
78
|
+
#
|
79
|
+
# @api public
|
80
|
+
#
|
81
|
+
def start_feature(feature)
|
82
|
+
raise NotImplementedError.new \
|
83
|
+
"#{self.class.name}#start_feature must be implemented in subclass."
|
28
84
|
end
|
29
85
|
|
30
|
-
|
31
|
-
|
86
|
+
# Called for each each scenario before it starts. Will be
|
87
|
+
# called before any hooks for the given scenario is executed.
|
88
|
+
#
|
89
|
+
# @param scenario [Scenario] Current scenario.
|
90
|
+
#
|
91
|
+
# @api public
|
92
|
+
#
|
93
|
+
def before_scenario(scenario)
|
94
|
+
raise NotImplementedError.new \
|
95
|
+
"#{self.class.name}#before_scenario must be implemented in subclass."
|
32
96
|
end
|
33
97
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
98
|
+
# Called for each each scenario before it starts, but after
|
99
|
+
# all before hooks for given scenario.
|
100
|
+
#
|
101
|
+
# @param scenario [Scenario] Current scenario.
|
102
|
+
#
|
103
|
+
# @api public
|
104
|
+
#
|
105
|
+
def start_scenario(scenario)
|
106
|
+
raise NotImplementedError.new \
|
107
|
+
"#{self.class.name}#start_scenario must be implemented in subclass."
|
39
108
|
end
|
40
109
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
110
|
+
# Called before each background.
|
111
|
+
#
|
112
|
+
# @param background [Background] Current background.
|
113
|
+
# @param scenario [Scenario] Current scenario.
|
114
|
+
#
|
115
|
+
# @api public
|
116
|
+
#
|
117
|
+
def start_background(background, scenario)
|
118
|
+
raise NotImplementedError.new \
|
119
|
+
"#{self.class.name}#start_background must be implemented in subclass."
|
120
|
+
end
|
49
121
|
|
50
|
-
|
51
|
-
|
122
|
+
# Called after each background.
|
123
|
+
#
|
124
|
+
# @param background [Background] Current background.
|
125
|
+
# @param scenario [Scenario] Current scenario.
|
126
|
+
#
|
127
|
+
# @api public
|
128
|
+
#
|
129
|
+
def end_background(background, scenario)
|
130
|
+
raise NotImplementedError.new \
|
131
|
+
"#{self.class.name}#end_background must be implemented in subclass."
|
132
|
+
end
|
52
133
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
134
|
+
# Called before each step and before any before-step hook.
|
135
|
+
#
|
136
|
+
# @param step [Step] Current Step.
|
137
|
+
# @param scenario [Scenario] Current scenario.
|
138
|
+
#
|
139
|
+
# @api public
|
140
|
+
#
|
141
|
+
def before_step(step, scenario)
|
142
|
+
raise NotImplementedError.new \
|
143
|
+
"#{self.class.name}#before_step must be implemented in subclass."
|
61
144
|
end
|
62
145
|
|
63
|
-
|
64
|
-
|
146
|
+
# Called before each step and after all before-step hooks.
|
147
|
+
#
|
148
|
+
# @param step [Step] Current Step.
|
149
|
+
# @param scenario [Scenario] Current scenario.
|
150
|
+
#
|
151
|
+
# @api public
|
152
|
+
#
|
153
|
+
def start_step(step, scenario)
|
154
|
+
raise NotImplementedError.new \
|
155
|
+
"#{self.class.name}#start_step must be implemented in subclass."
|
65
156
|
end
|
66
157
|
|
67
|
-
|
68
|
-
|
158
|
+
# Called after each step but before any after-step hook.
|
159
|
+
#
|
160
|
+
# @param step_result [StepResult] Result of current Step.
|
161
|
+
# @param scenario [Scenario] Current scenario.
|
162
|
+
# @param feature [Feature] Current feature.
|
163
|
+
#
|
164
|
+
# @api public
|
165
|
+
#
|
166
|
+
def end_step(step_result, scenario)
|
167
|
+
raise NotImplementedError.new \
|
168
|
+
"#{self.class.name}#end_step must be implemented in subclass."
|
69
169
|
end
|
70
170
|
|
71
|
-
|
72
|
-
|
171
|
+
# Called after each step, after all step hook.
|
172
|
+
#
|
173
|
+
# @param step_result [StepResult] Result of current Step.
|
174
|
+
# @param scenario [Scenario] Current scenario.
|
175
|
+
# @param feature [Feature] Current feature.
|
176
|
+
#
|
177
|
+
# @api public
|
178
|
+
#
|
179
|
+
def after_step(step_result, scenario)
|
180
|
+
raise NotImplementedError.new \
|
181
|
+
"#{self.class.name}#after_step must be implemented in subclass."
|
182
|
+
end
|
73
183
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
184
|
+
# Called after each scenario but before any after hook.
|
185
|
+
#
|
186
|
+
# @param scenario [Scenario] Current scenario.
|
187
|
+
# @param feature [Feature] Current feature.
|
188
|
+
#
|
189
|
+
# @api public
|
190
|
+
#
|
191
|
+
def end_scenario(scenario)
|
192
|
+
raise NotImplementedError.new \
|
193
|
+
"#{self.class.name}#end_scenario must be implemented in subclass."
|
78
194
|
end
|
79
195
|
|
80
|
-
|
196
|
+
# Called after each scenario and after all hooks.
|
197
|
+
#
|
198
|
+
# @param scenario [Scenario] Current scenario.
|
199
|
+
# @param feature [Feature] Current feature.
|
200
|
+
#
|
201
|
+
# @api public
|
202
|
+
#
|
203
|
+
def after_scenario(scenario)
|
204
|
+
raise NotImplementedError.new \
|
205
|
+
"#{self.class.name}#after_scenario must be implemented in subclass."
|
206
|
+
end
|
81
207
|
|
82
|
-
|
83
|
-
|
208
|
+
# Called after each feature but before any after hook.
|
209
|
+
#
|
210
|
+
# @param feature [Feature] Current feature.
|
211
|
+
#
|
212
|
+
# @api public
|
213
|
+
#
|
214
|
+
def end_feature(feature)
|
215
|
+
raise NotImplementedError.new \
|
216
|
+
"#{self.class.name}#end_feature must be implemented in subclass."
|
84
217
|
end
|
85
218
|
|
86
|
-
|
87
|
-
|
219
|
+
# Called after each feature and after all hooks.
|
220
|
+
#
|
221
|
+
# @param feature [Feature] Current feature.
|
222
|
+
#
|
223
|
+
# @api public
|
224
|
+
#
|
225
|
+
def after_feature(feature)
|
226
|
+
raise NotImplementedError.new \
|
227
|
+
"#{self.class.name}#after_feature must be implemented in subclass."
|
88
228
|
end
|
89
229
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
230
|
+
# Called after all features but before any after-features hook.
|
231
|
+
#
|
232
|
+
# @param features [Array<Feature>] List of all features.
|
233
|
+
#
|
234
|
+
# @api public
|
235
|
+
#
|
236
|
+
def end_features(features)
|
237
|
+
raise NotImplementedError.new \
|
238
|
+
"#{self.class.name}#end_features must be implemented in subclass."
|
239
|
+
end
|
240
|
+
|
241
|
+
# Called after all features and after all hooks.
|
242
|
+
#
|
243
|
+
# @param features [Array<Feature>] List of all features.
|
244
|
+
#
|
245
|
+
# @api public
|
246
|
+
#
|
247
|
+
def after_features(features)
|
248
|
+
raise NotImplementedError.new \
|
249
|
+
"#{self.class.name}#after_features must be implemented in subclass."
|
250
|
+
end
|
94
251
|
|
95
|
-
|
252
|
+
# @visibility private
|
253
|
+
def invoke(mth, *args)
|
254
|
+
send mth, *args
|
255
|
+
rescue => e
|
256
|
+
warn "Rescued in reporter: #{e}\n" + e.backtrace.join("\n")
|
96
257
|
end
|
97
258
|
end
|
98
259
|
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'colorize'
|
2
|
+
|
3
|
+
# Colorize colors:
|
4
|
+
# :black, :red, :green, :yellow, :blue,
|
5
|
+
# :magenta, :cyan, :white, :default, :light_black,
|
6
|
+
# :light_red, :light_green, :light_yellow, :light_blue,
|
7
|
+
# :light_magenta, :light_cyan, :light_white
|
8
|
+
|
9
|
+
module Gurke::Reporters
|
10
|
+
#
|
11
|
+
class DefaultReporter < NullReporter
|
12
|
+
def before_feature(feature)
|
13
|
+
io.puts "#{yellow('Feature')}: #{feature.name}"
|
14
|
+
io.puts ' ' + light_black(feature.description.split("\n").join("\n "))
|
15
|
+
io.puts
|
16
|
+
end
|
17
|
+
|
18
|
+
def before_scenario(scenario)
|
19
|
+
io.puts " #{yellow('Scenario')}: #{scenario.name}"
|
20
|
+
io.puts light_black(' Background:') if scenario.backgrounds.any?
|
21
|
+
end
|
22
|
+
|
23
|
+
def start_background(*)
|
24
|
+
@background = true
|
25
|
+
end
|
26
|
+
|
27
|
+
def end_background(*)
|
28
|
+
@background = false
|
29
|
+
end
|
30
|
+
|
31
|
+
def before_step(step, *)
|
32
|
+
io.print ' ' if @background
|
33
|
+
io.print ' '
|
34
|
+
io.print yellow(step.keyword)
|
35
|
+
io.print step.name.gsub(/"(.*?)"/, cyan('\0'))
|
36
|
+
end
|
37
|
+
|
38
|
+
def after_step(step, *)
|
39
|
+
case step.state
|
40
|
+
when :pending
|
41
|
+
print_braces yellow('pending')
|
42
|
+
when :failed
|
43
|
+
print_braces red('failure')
|
44
|
+
io.puts
|
45
|
+
io.puts red(" #{step.exception.class}:")
|
46
|
+
|
47
|
+
msg = step.exception.message.split("\n").join("\n ")
|
48
|
+
io.puts red(" #{msg}")
|
49
|
+
|
50
|
+
io.puts red(" #{step.exception.backtrace.join("\n ")}")
|
51
|
+
when :success
|
52
|
+
print_braces green('success')
|
53
|
+
else
|
54
|
+
print_braces cyan('skipped')
|
55
|
+
end
|
56
|
+
io.puts
|
57
|
+
io.flush
|
58
|
+
end
|
59
|
+
|
60
|
+
def after_scenario(*)
|
61
|
+
io.puts
|
62
|
+
end
|
63
|
+
|
64
|
+
def after_feature(*)
|
65
|
+
io.puts
|
66
|
+
end
|
67
|
+
|
68
|
+
def after_features(features)
|
69
|
+
scenarios = features.map(&:scenarios).flatten
|
70
|
+
|
71
|
+
io.puts " #{scenarios.size} scenarios: "\
|
72
|
+
"#{scenarios.select(&:failed?).size} failing, "\
|
73
|
+
"#{scenarios.select(&:pending?).size} pending"
|
74
|
+
io.puts
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def print_braces(str)
|
80
|
+
io.print " (#{str})"
|
81
|
+
end
|
82
|
+
|
83
|
+
def io
|
84
|
+
$stdout
|
85
|
+
end
|
86
|
+
|
87
|
+
[:black, :red, :green, :yellow, :blue,
|
88
|
+
:magenta, :cyan, :white, :default, :light_black,
|
89
|
+
:light_red, :light_green, :light_yellow, :light_blue,
|
90
|
+
:light_magenta, :light_cyan, :light_white].each do |color|
|
91
|
+
|
92
|
+
define_method(color){|str| io.tty? ? str.send(color) : str }
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Gurke
|
2
|
+
#
|
3
|
+
# A {RunList} is a list of {Background}, {Scenario}
|
4
|
+
# or {Step} objects that can be {#run}.
|
5
|
+
#
|
6
|
+
class RunList < Array
|
7
|
+
#
|
8
|
+
# Run all backgrounds from this list.
|
9
|
+
#
|
10
|
+
# @api private
|
11
|
+
#
|
12
|
+
def run(runner, reporter, *args)
|
13
|
+
each do |o|
|
14
|
+
o.run runner, reporter, *args
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/gurke/runner.rb
CHANGED
@@ -12,108 +12,32 @@ module Gurke
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def reporter
|
15
|
-
@reporter ||=
|
15
|
+
@reporter ||= Reporters::DefaultReporter.new
|
16
16
|
end
|
17
17
|
|
18
18
|
def run
|
19
19
|
files.each{|f| builder.parse(f) }
|
20
|
+
features = builder.features
|
21
|
+
features.freeze
|
20
22
|
|
21
|
-
|
22
|
-
run_features builder.features
|
23
|
-
end
|
24
|
-
|
25
|
-
!builder.features
|
26
|
-
.map(&:scenarios)
|
27
|
-
.flatten
|
28
|
-
.any?{|s| s.failed? || s.pending? }
|
29
|
-
end
|
30
|
-
|
31
|
-
def run_features(features)
|
32
|
-
reporter.start_features(features)
|
33
|
-
|
34
|
-
features.each do |feature|
|
35
|
-
run_feature(feature)
|
36
|
-
end
|
37
|
-
|
38
|
-
reporter.finish_features(features)
|
39
|
-
end
|
40
|
-
|
41
|
-
def run_feature(feature)
|
42
|
-
reporter.start_feature(feature)
|
43
|
-
|
44
|
-
feature.scenarios.each do |scenario|
|
45
|
-
run_scenario(scenario, feature)
|
46
|
-
end
|
47
|
-
|
48
|
-
reporter.finish_feature(feature)
|
49
|
-
end
|
50
|
-
|
51
|
-
def run_scenario(scenario, feature)
|
52
|
-
reporter.start_scenario(scenario, feature)
|
53
|
-
|
54
|
-
world = world_for(scenario, feature)
|
55
|
-
|
56
|
-
with_hooks(:scenario, scenario, world) do
|
57
|
-
feature.backgrounds.each do |b|
|
58
|
-
run_background(b, scenario, feature, world)
|
59
|
-
end
|
60
|
-
scenario.steps.each{|s| run_step(s, scenario, feature, world) }
|
61
|
-
end
|
62
|
-
|
63
|
-
reporter.finish_scenario(scenario, feature)
|
64
|
-
end
|
65
|
-
|
66
|
-
def run_background(background, scenario, feature, world)
|
67
|
-
reporter.start_background(background)
|
68
|
-
|
69
|
-
background.steps.each{|s| run_step(s, scenario, feature, world) }
|
70
|
-
|
71
|
-
reporter.finish_background(background)
|
72
|
-
end
|
73
|
-
|
74
|
-
def run_step(step, scenario, feature, world)
|
75
|
-
reporter.start_step(step, scenario, feature)
|
76
|
-
|
77
|
-
result = nil
|
78
|
-
with_filtered_backtrace do
|
79
|
-
match = Steps.find_step(step, world, step.type)
|
80
|
-
|
81
|
-
if scenario.pending? || scenario.failed?
|
82
|
-
result = StepResult.new(step, :skipped)
|
83
|
-
return
|
84
|
-
end
|
85
|
-
|
86
|
-
m = world.method(match.method_name)
|
87
|
-
world.send match.method_name, *(match.params + [step])[0...m.arity]
|
88
|
-
end
|
89
|
-
|
90
|
-
result = StepResult.new(step, :success)
|
91
|
-
rescue StepPending => e
|
92
|
-
scenario.pending! e
|
93
|
-
result = StepResult.new(step, :pending, e)
|
94
|
-
rescue => e
|
95
|
-
scenario.failed! e
|
96
|
-
result = StepResult.new(step, :failed, e)
|
97
|
-
ensure
|
98
|
-
reporter.finish_step(result, scenario, feature)
|
23
|
+
features.run self, reporter
|
99
24
|
end
|
100
25
|
|
101
|
-
def
|
102
|
-
Configuration::BEFORE_HOOKS.for(scope).each
|
103
|
-
hook.run(world)
|
104
|
-
end
|
26
|
+
def hook(scope, world, &block)
|
27
|
+
Configuration::BEFORE_HOOKS.for(scope).each{|hook| hook.run world }
|
105
28
|
Configuration::AROUND_HOOKS.for(scope).reduce(block) do |blk, hook|
|
106
29
|
proc { hook.run(world, blk) }
|
107
30
|
end.call
|
31
|
+
ensure
|
108
32
|
Configuration::AFTER_HOOKS.for(scope).each do |hook|
|
109
|
-
|
33
|
+
begin
|
34
|
+
hook.run world
|
35
|
+
rescue => e
|
36
|
+
warn "Rescued error in after hook: #{e}\n#{e.backtrace.join("\n")}"
|
37
|
+
end
|
110
38
|
end
|
111
39
|
end
|
112
40
|
|
113
|
-
def world_for(scenario, _feature)
|
114
|
-
scenario.send(:world)
|
115
|
-
end
|
116
|
-
|
117
41
|
def with_filtered_backtrace
|
118
42
|
yield
|
119
43
|
rescue => e
|
@@ -123,34 +47,5 @@ module Gurke
|
|
123
47
|
end
|
124
48
|
raise
|
125
49
|
end
|
126
|
-
|
127
|
-
#
|
128
|
-
class StepResult
|
129
|
-
attr_reader :step, :exception, :state
|
130
|
-
|
131
|
-
def initialize(step, state, err = nil)
|
132
|
-
@step, @state, @exception = step, state, err
|
133
|
-
end
|
134
|
-
|
135
|
-
Gurke::Step.public_instance_methods(false).each do |m|
|
136
|
-
define_method(m){|*args| step.send m, *args }
|
137
|
-
end
|
138
|
-
|
139
|
-
def failed?
|
140
|
-
@state == :failed
|
141
|
-
end
|
142
|
-
|
143
|
-
def pending?
|
144
|
-
@state == :pending
|
145
|
-
end
|
146
|
-
|
147
|
-
def skipped?
|
148
|
-
@state == :skipped
|
149
|
-
end
|
150
|
-
|
151
|
-
def success?
|
152
|
-
@state == :success
|
153
|
-
end
|
154
|
-
end
|
155
50
|
end
|
156
51
|
end
|
data/lib/gurke/scenario.rb
CHANGED
@@ -14,24 +14,35 @@ module Gurke
|
|
14
14
|
#
|
15
15
|
attr_reader :line
|
16
16
|
|
17
|
+
# The feature that contains this scenario.
|
18
|
+
#
|
19
|
+
# @return [Feature] Parent feature.
|
20
|
+
#
|
21
|
+
attr_reader :feature
|
22
|
+
|
23
|
+
# List of this scenario's steps.
|
24
|
+
#
|
25
|
+
# This does not include background steps.
|
26
|
+
#
|
27
|
+
# @return [Array<Step>] Steps.
|
28
|
+
#
|
29
|
+
attr_reader :steps
|
30
|
+
|
31
|
+
#
|
17
32
|
attr_reader :tags
|
18
33
|
|
19
34
|
# @api private
|
20
35
|
attr_reader :raw
|
21
36
|
|
22
37
|
# @api private
|
23
|
-
def initialize(file, line, tags, raw)
|
24
|
-
@file, @line, @tags, @raw = file, line, tags, raw
|
25
|
-
end
|
26
|
-
|
27
|
-
# Return list of this scenario's steps.
|
28
|
-
#
|
29
|
-
# This does not include background steps.
|
30
|
-
#
|
31
|
-
# @return [Array<Step>] Steps.
|
32
38
|
#
|
33
|
-
def
|
34
|
-
@
|
39
|
+
def initialize(feature, file, line, tags, raw)
|
40
|
+
@feature = feature
|
41
|
+
@steps = RunList.new
|
42
|
+
@file = file
|
43
|
+
@line = line
|
44
|
+
@tags = tags
|
45
|
+
@raw = raw
|
35
46
|
end
|
36
47
|
|
37
48
|
# Return name of the scenario.
|
@@ -42,23 +53,52 @@ module Gurke
|
|
42
53
|
raw.name
|
43
54
|
end
|
44
55
|
|
56
|
+
# Return all backgrounds for this scenario.
|
57
|
+
#
|
58
|
+
# They are taken from the feature containing this scenario.
|
59
|
+
#
|
60
|
+
# @return [Array<Background>] Backgrounds.
|
61
|
+
#
|
62
|
+
def backgrounds
|
63
|
+
feature.backgrounds
|
64
|
+
end
|
65
|
+
|
66
|
+
# Check if scenario is pending.
|
67
|
+
#
|
68
|
+
# @return [Boolean] True if pending, false otherwise.
|
69
|
+
#
|
45
70
|
def pending?
|
46
71
|
@state == :pending
|
47
72
|
end
|
48
73
|
|
74
|
+
# Check if scenario has failed.
|
75
|
+
#
|
76
|
+
# @return [Boolean] True if failed, false otherwise.
|
77
|
+
#
|
49
78
|
def failed?
|
50
79
|
@state == :failed
|
51
80
|
end
|
52
81
|
|
82
|
+
# Exception that led to either pending or failed state.
|
83
|
+
#
|
84
|
+
# @return [Exception] Exception or nil of none given.
|
85
|
+
#
|
53
86
|
attr_reader :exception
|
54
87
|
|
55
|
-
#
|
56
|
-
|
88
|
+
# Call to mark scenario as failed.
|
89
|
+
#
|
90
|
+
# @param error [Exception] Given an exception as reason.
|
91
|
+
#
|
92
|
+
def failed!(error = nil)
|
57
93
|
@exception = error
|
58
94
|
@state = :failed
|
59
95
|
end
|
60
96
|
|
61
|
-
#
|
97
|
+
# Call to mark scenario as pending. Will do nothing
|
98
|
+
# if scenario is already failed.
|
99
|
+
#
|
100
|
+
# @param error [Exception] Given an exception as reason.
|
101
|
+
#
|
62
102
|
def pending!(error)
|
63
103
|
return if failed?
|
64
104
|
|
@@ -66,8 +106,29 @@ module Gurke
|
|
66
106
|
@state = :pending
|
67
107
|
end
|
68
108
|
|
109
|
+
# @api private
|
110
|
+
#
|
111
|
+
def run(runner, reporter)
|
112
|
+
reporter.invoke :before_scenario, self
|
113
|
+
|
114
|
+
runner.hook :scenario, world do
|
115
|
+
run_scenario runner, reporter
|
116
|
+
end
|
117
|
+
ensure
|
118
|
+
reporter.invoke :after_scenario, self
|
119
|
+
end
|
120
|
+
|
69
121
|
private
|
70
122
|
|
123
|
+
def run_scenario(runner, reporter)
|
124
|
+
reporter.invoke :start_scenario, self
|
125
|
+
|
126
|
+
feature.backgrounds.run runner, reporter, self, world
|
127
|
+
steps.run runner, reporter, self, world
|
128
|
+
ensure
|
129
|
+
reporter.invoke :end_scenario, self
|
130
|
+
end
|
131
|
+
|
71
132
|
def world
|
72
133
|
@world ||= begin
|
73
134
|
cls = Class.new
|
data/lib/gurke/step.rb
CHANGED
@@ -37,5 +37,77 @@ module Gurke
|
|
37
37
|
def doc_string
|
38
38
|
raw.doc_string.value if raw.doc_string
|
39
39
|
end
|
40
|
+
|
41
|
+
# @api private
|
42
|
+
#
|
43
|
+
def run(runner, reporter, scenario, world)
|
44
|
+
reporter.invoke :before_step, self, scenario
|
45
|
+
|
46
|
+
result = runner.hook(:step, world) do
|
47
|
+
run_step runner, reporter, scenario, world
|
48
|
+
end
|
49
|
+
ensure
|
50
|
+
reporter.invoke :after_step, result, scenario
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def run_step(runner, reporter, scenario, world)
|
56
|
+
reporter.invoke :start_step, self, scenario
|
57
|
+
|
58
|
+
result = nil
|
59
|
+
runner.with_filtered_backtrace do
|
60
|
+
match = Steps.find_step self, world, type
|
61
|
+
|
62
|
+
if scenario.pending? || scenario.failed?
|
63
|
+
result = StepResult.new self, :skipped
|
64
|
+
return result
|
65
|
+
end
|
66
|
+
|
67
|
+
m = world.method match.method_name
|
68
|
+
world.send match.method_name, *(match.params + [self])[0...m.arity]
|
69
|
+
end
|
70
|
+
|
71
|
+
result = StepResult.new self, :success
|
72
|
+
rescue StepPending => e
|
73
|
+
scenario.pending! e
|
74
|
+
result = StepResult.new self, :pending, e
|
75
|
+
rescue => e
|
76
|
+
scenario.failed! e
|
77
|
+
result = StepResult.new self, :failed, e
|
78
|
+
ensure
|
79
|
+
reporter.invoke :end_step, result, scenario
|
80
|
+
end
|
81
|
+
|
82
|
+
#
|
83
|
+
class StepResult
|
84
|
+
attr_reader :step, :exception, :state
|
85
|
+
|
86
|
+
def initialize(step, state, err = nil)
|
87
|
+
@step, @state, @exception = step, state, err
|
88
|
+
end
|
89
|
+
|
90
|
+
Step.public_instance_methods(false).each do |mth|
|
91
|
+
class_eval <<-EOS, __FILE__, __LINE__
|
92
|
+
def #{mth}(*args) @step.send #{mth}, *args; end
|
93
|
+
EOS
|
94
|
+
end
|
95
|
+
|
96
|
+
def failed?
|
97
|
+
@state == :failed
|
98
|
+
end
|
99
|
+
|
100
|
+
def pending?
|
101
|
+
@state == :pending
|
102
|
+
end
|
103
|
+
|
104
|
+
def skipped?
|
105
|
+
@state == :skipped
|
106
|
+
end
|
107
|
+
|
108
|
+
def success?
|
109
|
+
@state == :success
|
110
|
+
end
|
111
|
+
end
|
40
112
|
end
|
41
113
|
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
if ENV['CI'] || (defined?(:RUBY_ENGINE) && RUBY_ENGINE != 'rbx')
|
2
|
+
require 'coveralls'
|
3
|
+
Coveralls.wear! do
|
4
|
+
add_filter 'spec'
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
require 'bundler'
|
9
|
+
Bundler.require
|
10
|
+
|
11
|
+
require 'gurke'
|
12
|
+
|
13
|
+
Dir[File.expand_path('spec/support/**/*.rb')].each{|f| require f }
|
14
|
+
|
15
|
+
RSpec.configure do |config|
|
16
|
+
config.order = 'random'
|
17
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gurke
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.0.dev.1.
|
4
|
+
version: 2.0.0.dev.1.b18
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jan Graichen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-05-
|
11
|
+
date: 2014-05-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: trollop
|
@@ -93,8 +93,12 @@ files:
|
|
93
93
|
- lib/gurke/configuration.rb
|
94
94
|
- lib/gurke/dsl.rb
|
95
95
|
- lib/gurke/feature.rb
|
96
|
+
- lib/gurke/feature_list.rb
|
96
97
|
- lib/gurke/reporter.rb
|
98
|
+
- lib/gurke/reporters/default_reporter.rb
|
99
|
+
- lib/gurke/reporters/null_reporter.rb
|
97
100
|
- lib/gurke/rspec.rb
|
101
|
+
- lib/gurke/run_list.rb
|
98
102
|
- lib/gurke/runner.rb
|
99
103
|
- lib/gurke/scenario.rb
|
100
104
|
- lib/gurke/step.rb
|
@@ -102,6 +106,7 @@ files:
|
|
102
106
|
- lib/gurke/steps.rb
|
103
107
|
- lib/gurke/tag.rb
|
104
108
|
- lib/gurke/version.rb
|
109
|
+
- spec/spec_helper.rb
|
105
110
|
homepage: https://github.com/jgraichen/gurke
|
106
111
|
licenses:
|
107
112
|
- MIT
|
@@ -134,3 +139,4 @@ test_files:
|
|
134
139
|
- features/gurke/step_specific_definitions.feature
|
135
140
|
- features/support/steps/cli_steps.rb
|
136
141
|
- features/support/steps/file_steps.rb
|
142
|
+
- spec/spec_helper.rb
|