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 CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- NDc3MWMyNWIzYzQ0MGRmMWFkMmIyMmFmMjYwMTgzN2I3NmQ0YTdmYQ==
4
+ MTliYzQwOGExYjAzODMxMTE3YWZhMmU1ZDhmZDM5YjZjZWM5Y2E5ZA==
5
5
  data.tar.gz: !binary |-
6
- ODQ4YWI3ZTUyZDhmYjk2YzQ2NWQwOWY1ZmM1YzFkNDQ5YWRhZTJjYQ==
6
+ ZjdiNTE1MDQ5NTk5ZmY4YTIwZGZhZWM2MmRhZGYwMWQ1YWRhZjVjYQ==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- OTcwMGYwOTM2NTEzNGQ0MWU0YmFiN2I3OWMxNjAxZTc4N2Q0OGEwYTdkOGY4
10
- NDJkNmI0MjY0ZTYzYzcyMTVhNDZkYWY0MWE2MzVmYTc1ZjE1ZTMxYzZkMjQ2
11
- OGU4ZTRmODkyZTQ2MWMzY2VjOTM0MWE5MTdhNWQwODdiYjEwZmQ=
9
+ Y2EzNTBhOTE4OTFmOWQ2ZjU0NzI1MWY2YjhhMGE1N2EyNGJiNjNlYTA5NTNl
10
+ MmVmM2Y0NWM4NTVhYzIxNDY0OTY5ZDBiZTZkYTIwODAwMDljMzIxNTNkMTYy
11
+ Y2JmZmZiZjc3ZTYwNTdkMzlhNzgyOTMwZjk3NzEzZWIzNDRjNGU=
12
12
  data.tar.gz: !binary |-
13
- MmUyOWRiZDQ3ZWYyMDU5ZGY0M2Y3ODk2MmIyYThjNGI2MGYzNTEyMTJiNzJl
14
- ZTAzOGZlNzIwMGJmNGQzZDBlY2E5ZDBlZDk1ZDlhOWVlZDFhMzJiYjJjODJh
15
- ZGNjMmU1OWFjOTdjMTkwNmFjNTE5ZDQ4NDI1MGViZTliNTA4ZjA=
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
@@ -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, @line, @raw = file, line, raw
28
+ @file = file
29
+ @line = line
30
+ @raw = raw
31
+ @steps = RunList.new
23
32
  end
24
33
 
25
- # Return list of steps this background specifies.
26
- #
27
- # @return [Array<Step>] Steps.
34
+ # @api private
28
35
  #
29
- def steps
30
- @steps ||= []
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(@file, t.line, t) }
33
+ tags = raw.tags.map{|t| Tag.new @file, t.line, t }
34
34
 
35
- @current_feature = Feature.new(@file, raw.line, tags, raw)
36
- @features << @current_feature
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
- @current_context = Background.new(@file, raw.line, raw)
41
- @current_feature.backgrounds << @current_context
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 = raw.tags.map{|t| Tag.new(@file, t.line, t) }
46
- tags += @current_feature.tags
50
+ tags = raw.tags.map{|t| Tag.new @file, t.line, t }
51
+ tags += features.last.tags
47
52
 
48
- @current_context = Scenario.new(@file, raw.line, tags, raw)
53
+ @scenario = Scenario.new @feature, @file, raw.line, tags, raw
54
+ @context = @scenario
55
+ @type = nil
49
56
 
50
- unless filtered?(@current_context)
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 = get_type(raw.keyword.strip)
61
+ @type = lookup_type raw.keyword.strip
57
62
 
58
- @current_context.steps << Step.new(@file, raw.line, type, raw)
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
- @current_context = nil
64
- @current_feature = nil
65
- @file = nil
68
+ @feature = nil
69
+ @scenario = nil
70
+ @context = nil
71
+ @type = nil
72
+ @file = nil
66
73
  end
67
74
 
68
- def get_type(keyword)
75
+ private
76
+
77
+ def lookup_type(keyword)
69
78
  case (kw = @keywords.fetch(keyword))
70
79
  when :and, :but
71
- if (step = @current_context.steps.last)
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
- @file, @line, @tags, @raw = file, line, tags, raw
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
@@ -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
- def start_features(*)
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
- def start_feature(feature)
16
- io.puts "#{yellow('Feature')}: #{feature.name}"
17
- io.puts ' ' + light_black(feature.description.split("\n").join("\n "))
18
- io.puts
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
- def start_scenario(scenario, feature)
22
- io.puts " #{yellow('Scenario')}: #{scenario.name}"
23
- io.puts light_black(' Background:') if feature.backgrounds.any?
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
- def start_background(*)
27
- @background = true
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
- def finish_background(*)
31
- @background = false
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
- def start_step(step, *)
35
- io.print ' ' if @background
36
- io.print ' '
37
- io.print yellow(step.keyword)
38
- io.print step.name.gsub(/"(.*?)"/, cyan('\0'))
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
- def finish_step(step, *)
42
- case step.state
43
- when :pending
44
- print_braces yellow('pending')
45
- when :failed
46
- print_braces red('failure')
47
- io.puts
48
- io.puts red(" #{step.exception.class}:")
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
- msg = step.exception.message.split("\n").join("\n ")
51
- io.puts red(" #{msg}")
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
- io.puts red(" #{step.exception.backtrace.join("\n ")}")
54
- when :success
55
- print_braces green('success')
56
- else
57
- print_braces cyan('skipped')
58
- end
59
- io.puts
60
- io.flush
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
- def finish_scenario(*)
64
- io.puts
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
- def finish_feature(*)
68
- io.puts
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
- def finish_features(features)
72
- scenarios = features.map(&:scenarios).flatten
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
- io.puts " #{scenarios.size} scenarios: "\
75
- "#{scenarios.select(&:failed?).size} failing, "\
76
- "#{scenarios.select(&:pending?).size} pending"
77
- io.puts
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
- private
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
- def print_braces(str)
83
- io.print " (#{str})"
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
- def io
87
- $stdout
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
- [:black, :red, :green, :yellow, :blue,
91
- :magenta, :cyan, :white, :default, :light_black,
92
- :light_red, :light_green, :light_yellow, :light_blue,
93
- :light_magenta, :light_cyan, :light_white].each do |color|
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
- define_method(color){|str| io.tty? ? str.send(color) : str }
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,12 @@
1
+ module Gurke::Reporters
2
+ #
3
+ # The {NullReporter} does not output anything.
4
+ #
5
+ class NullReporter < Gurke::Reporter
6
+ Gurke::Reporter::CALLBACKS.each do |cb|
7
+ class_eval <<-EOF, __FILE__, __LINE__
8
+ def #{cb}(*) end
9
+ EOF
10
+ end
11
+ end
12
+ 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 ||= Reporter.new
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
- with_hooks(:features, nil, nil) do
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 with_hooks(scope, _context, world, &block)
102
- Configuration::BEFORE_HOOKS.for(scope).each do |hook|
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
- hook.run(world)
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
@@ -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 steps
34
- @steps ||= []
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
- # @api private
56
- def failed!(error)
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
- # @api private
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
@@ -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.b17
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-30 00:00:00.000000000 Z
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