gurke 2.0.0.dev.1.b17 → 2.0.0.dev.1.b18
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 +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
|