spinach 0.1.4 → 0.1.5

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.
Files changed (53) hide show
  1. data/.gitignore +1 -0
  2. data/.yardopts +6 -0
  3. data/Gemfile +1 -0
  4. data/Guardfile +7 -0
  5. data/Rakefile +1 -0
  6. data/Readme.md +147 -10
  7. data/features/{generate_features.feature → automatic_feature_generation.feature} +0 -0
  8. data/features/steps/automatic_feature_generation.rb +5 -2
  9. data/features/steps/exit_status.rb +8 -3
  10. data/features/steps/feature_name_guessing.rb +4 -1
  11. data/features/steps/reporting/display_run_summary.rb +7 -4
  12. data/features/steps/reporting/error_reporting.rb +7 -2
  13. data/features/steps/reporting/show_step_source_location.rb +9 -5
  14. data/features/steps/reporting/undefined_feature_reporting.rb +4 -1
  15. data/features/steps/rspec_compatibility.rb +6 -2
  16. data/features/support/spinach_runner.rb +2 -9
  17. data/lib/spinach/capybara.rb +3 -4
  18. data/lib/spinach/cli.rb +0 -1
  19. data/lib/spinach/config.rb +0 -8
  20. data/lib/spinach/dsl.rb +4 -13
  21. data/lib/spinach/exceptions.rb +1 -1
  22. data/lib/spinach/feature_steps.rb +1 -9
  23. data/lib/spinach/generators/feature_generator.rb +3 -2
  24. data/lib/spinach/generators.rb +1 -1
  25. data/lib/spinach/hookable.rb +81 -0
  26. data/lib/spinach/hooks.rb +132 -0
  27. data/lib/spinach/reporter/stdout/error_reporting.rb +163 -0
  28. data/lib/spinach/reporter/stdout.rb +3 -151
  29. data/lib/spinach/reporter.rb +39 -25
  30. data/lib/spinach/runner/{feature.rb → feature_runner.rb} +5 -15
  31. data/lib/spinach/runner/scenario_runner.rb +65 -0
  32. data/lib/spinach/runner.rb +5 -11
  33. data/lib/spinach/version.rb +1 -1
  34. data/lib/spinach.rb +20 -8
  35. data/spinach.gemspec +2 -3
  36. data/test/spinach/capybara_test.rb +4 -3
  37. data/test/spinach/cli_test.rb +0 -1
  38. data/test/spinach/feature_steps_test.rb +6 -23
  39. data/test/spinach/generators/feature_generator_test.rb +2 -2
  40. data/test/spinach/generators_test.rb +2 -2
  41. data/test/spinach/hookable_test.rb +59 -0
  42. data/test/spinach/hooks_test.rb +28 -0
  43. data/test/spinach/reporter/stdout/error_reporting_test.rb +265 -0
  44. data/test/spinach/reporter/stdout_test.rb +1 -238
  45. data/test/spinach/reporter_test.rb +58 -103
  46. data/test/spinach/runner/{feature_test.rb → feature_runner_test.rb} +21 -23
  47. data/test/spinach/runner/scenario_runner_test.rb +111 -0
  48. data/test/spinach/runner_test.rb +1 -1
  49. data/test/spinach_test.rb +19 -18
  50. data/test/test_helper.rb +1 -1
  51. metadata +60 -61
  52. data/lib/spinach/runner/scenario.rb +0 -77
  53. data/test/spinach/runner/scenario_test.rb +0 -120
@@ -0,0 +1,132 @@
1
+ require_relative 'hookable'
2
+
3
+ module Spinach
4
+ # Spinach's hooks is a subscription mechanism to allow developers to define
5
+ # certain callbacks given several Spinach signals, like running a feature,
6
+ # executing a particular step and such.
7
+ class Hooks
8
+ include Hookable
9
+
10
+ # Runs before the entire spinach run
11
+ #
12
+ # @example
13
+ # Spinach.before_run do
14
+ # # Whatever
15
+ # end
16
+ hook :before_run
17
+
18
+ # Runs after the entire spinach run
19
+ #
20
+ # @example
21
+ # Spinach.after_run do |status|
22
+ # # status is true when the run is successful, false otherwise
23
+ # end
24
+ hook :after_run
25
+
26
+ # Runs before every feature,
27
+ #
28
+ # @example
29
+ # Spinach.before_feature do |feature_data|
30
+ # # feature_data is a hash of the parsed feature data
31
+ # end
32
+ hook :before_feature
33
+
34
+ # Runs after every feature
35
+ #
36
+ # @example
37
+ # Spinach.after_feature do |feature_data|
38
+ # # feature_data is a hash of the parsed feature data
39
+ # end
40
+ hook :after_feature
41
+
42
+ # Runs when an undefined feature is found
43
+ #
44
+ # @example
45
+ # Spinach.on_undefined_feature do |feature_data, exception|
46
+ # # feature_data is a hash of the parsed feature data
47
+ # # exception contains the thrown exception
48
+ # end
49
+ hook :on_undefined_feature
50
+
51
+ # Runs before every scenario
52
+ #
53
+ # @example
54
+ # Spinach.before_scenario do |scenario_data|
55
+ # # feature_data is a hash of the parsed scenario data
56
+ # end
57
+ hook :before_scenario
58
+
59
+ # Runs after every scenario
60
+ #
61
+ # @example
62
+ # Spinach.after_scenario do |scenario_data|
63
+ # # feature_data is a hash of the parsed scenario data
64
+ # end
65
+ hook :after_scenario
66
+
67
+ # Runs before every step execution
68
+ #
69
+ # @example
70
+ # Spinach.before_step do |step_data|
71
+ # # step_data contains a hash with this step's data
72
+ # end
73
+ hook :before_step
74
+
75
+ # Runs after every step execution
76
+ #
77
+ # @example
78
+ # Spinach.before_step do |step_data|
79
+ # # step_data contains a hash with this step's data
80
+ # end
81
+ hook :after_step
82
+
83
+ # Runs after every successful step execution
84
+ #
85
+ # @example
86
+ # Spinach.on_successful_step do |step_data, location|
87
+ # # step_data contains a hash with this step's data
88
+ # # step_location contains a string indication this step definition's
89
+ # # location
90
+ # end
91
+ hook :on_successful_step
92
+
93
+ # Runs after every failed step execution
94
+ #
95
+ # @example
96
+ # Spinach.on_failed_step do |step_data, location|
97
+ # # step_data contains a hash with this step's data
98
+ # # step_location contains a string indication this step definition's
99
+ # # location
100
+ # end
101
+ hook :on_failed_step
102
+
103
+ # Runs after every step execution that raises an exception
104
+ #
105
+ # @example
106
+ # Spinach.on_error_step do |step_data, location|
107
+ # # step_data contains a hash with this step's data
108
+ # # step_location contains a string indication this step definition's
109
+ # # location
110
+ # end
111
+ hook :on_error_step
112
+
113
+ # Runs every time a step which is not defined is called
114
+ #
115
+ # @example
116
+ # Spinach.on_undefined_step do |step_data, location|
117
+ # # step_data contains a hash with this step's data
118
+ # # step_location contains a string indication this step definition's
119
+ # # location
120
+ # end
121
+ hook :on_undefined_step
122
+
123
+ # Runs every time a step is skipped because there has been an unsuccessful
124
+ # one just before.
125
+ #
126
+ # @example
127
+ # Spinach.on_undefined_step do |step_data|
128
+ # # step_data contains a hash with this step's data
129
+ # end
130
+ hook :on_skipped_step
131
+ end
132
+ end
@@ -0,0 +1,163 @@
1
+ module Spinach
2
+ class Reporter
3
+ class Stdout < Reporter
4
+ # This module handles Stdout's reporter error reporting capabilities.
5
+ module ErrorReporting
6
+
7
+ # Prints the errors for this run.
8
+ #
9
+ def error_summary
10
+ error.puts "\nError summary:\n"
11
+ report_error_steps
12
+ report_failed_steps
13
+ report_undefined_features
14
+ report_undefined_steps
15
+ end
16
+
17
+ # Prints the steps that raised an error.
18
+ #
19
+ def report_error_steps
20
+ report_errors('Errors', error_steps, :light_red) if error_steps.any?
21
+ end
22
+
23
+ # Prints failing steps.
24
+ #
25
+ def report_failed_steps
26
+ report_errors('Failures', failed_steps, :light_red) if failed_steps.any?
27
+ end
28
+
29
+ # Prints undefined steps.
30
+ #
31
+ def report_undefined_steps
32
+ report_errors('Undefined steps', undefined_steps, :yellow) if undefined_steps.any?
33
+ end
34
+
35
+ def report_undefined_features
36
+ if undefined_features.any?
37
+ error.puts " Undefined features (#{undefined_features.length})".light_yellow
38
+ undefined_features.each do |feature|
39
+ error.puts " #{feature['name']}".yellow
40
+ end
41
+ end
42
+ end
43
+
44
+ # Prints the error for a given set of steps
45
+ #
46
+ # @param [String] banner
47
+ # the text to prepend as the title
48
+ #
49
+ # @param [Array] steps
50
+ # the steps to output
51
+ #
52
+ # @param [Symbol] color
53
+ # The color code to use with Colorize to colorize the output.
54
+ #
55
+ def report_errors(banner, steps, color)
56
+ error.puts " #{banner} (#{steps.length})".colorize(color)
57
+ steps.each do |error|
58
+ report_error error
59
+ end
60
+ error.puts ""
61
+ end
62
+
63
+ # Prints an error in a nice format
64
+ #
65
+ # @param [Array] error
66
+ # An array containing the feature, scenario, step and exception
67
+ #
68
+ # @param [Symbol] format
69
+ # The format to output the error. Currently supproted formats are
70
+ # :summarized (default) and :full
71
+ #
72
+ # @return [String]
73
+ # The error report
74
+ #
75
+ def report_error(error, format=:summarized)
76
+ case format
77
+ when :summarized
78
+ self.error.puts summarized_error(error)
79
+ when :full
80
+ self.error.puts full_error(error)
81
+ else
82
+ raise "Format not defined"
83
+ end
84
+ end
85
+
86
+ # Returns summarized error report
87
+ #
88
+ # @param [Array] error
89
+ # An array containing the feature, scenario, step and exception
90
+ #
91
+ # @return [String]
92
+ # The summarized error report
93
+ #
94
+ def summarized_error(error)
95
+ feature, scenario, step, exception = error
96
+ summary = " #{feature['name']} :: #{scenario['name']} :: #{full_step step}"
97
+ if exception.kind_of?(Spinach::StepNotDefinedException)
98
+ summary.yellow
99
+ else
100
+ summary.red
101
+ end
102
+ end
103
+
104
+ # Returns a complete error report
105
+ #
106
+ # @param [Array] error
107
+ # An array containing the feature, scenario, step and exception
108
+ #
109
+ # @return [String]
110
+ # The coplete error report
111
+ #
112
+ def full_error(error)
113
+ feature, scenario, step, exception = error
114
+ output = "\n"
115
+ output += report_exception(exception)
116
+ output +="\n"
117
+
118
+ if exception.kind_of?(Spinach::StepNotDefinedException)
119
+ output << "\n"
120
+ output << " You can define it with: \n\n".yellow
121
+ suggestion = Generators::StepGenerator.new(step).generate
122
+ suggestion.split("\n").each do |line|
123
+ output << " #{line}\n".yellow
124
+ end
125
+ output << "\n"
126
+ else
127
+ if options[:backtrace]
128
+ output += "\n"
129
+ exception.backtrace.map do |line|
130
+ output << " #{line}\n"
131
+ end
132
+ else
133
+ output << " #{exception.backtrace[0]}"
134
+ end
135
+ end
136
+ output
137
+ end
138
+
139
+
140
+ # Prints a information when an exception is raised.
141
+ #
142
+ # @param [Exception] exception
143
+ # The exception to report
144
+ #
145
+ # @return [String]
146
+ # The exception report
147
+ #
148
+ def report_exception(exception)
149
+ output = exception.message.split("\n").map{ |line|
150
+ " #{line}"
151
+ }.join("\n")
152
+
153
+ if exception.kind_of?(Spinach::StepNotDefinedException)
154
+ output.yellow
155
+ else
156
+ output.red
157
+ end
158
+ end
159
+
160
+ end
161
+ end
162
+ end
163
+ end
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ require_relative 'stdout/error_reporting'
2
3
 
3
4
  module Spinach
4
5
  class Reporter
@@ -6,6 +7,8 @@ module Spinach
6
7
  #
7
8
  class Stdout < Reporter
8
9
 
10
+ include ErrorReporting
11
+
9
12
  # The output buffers to store the reports.
10
13
  attr_reader :out, :error
11
14
 
@@ -191,138 +194,6 @@ module Spinach
191
194
  out.puts "Steps Summary: #{successful_summary}, #{undefined_summary}, #{failed_summary}, #{error_summary}\n\n"
192
195
  end
193
196
 
194
- # Prints the errors for this run.
195
- #
196
- def error_summary
197
- error.puts "\nError summary:\n"
198
- report_error_steps
199
- report_failed_steps
200
- report_undefined_features
201
- report_undefined_steps
202
- end
203
-
204
- # Prints the steps that raised an error.
205
- #
206
- def report_error_steps
207
- report_errors('Errors', error_steps, :light_red) if error_steps.any?
208
- end
209
-
210
- # Prints failing steps.
211
- #
212
- def report_failed_steps
213
- report_errors('Failures', failed_steps, :light_red) if failed_steps.any?
214
- end
215
-
216
- # Prints undefined steps.
217
- #
218
- def report_undefined_steps
219
- report_errors('Undefined steps', undefined_steps, :yellow) if undefined_steps.any?
220
- end
221
-
222
- def report_undefined_features
223
- if undefined_features.any?
224
- error.puts " Undefined features (#{undefined_features.length})".light_yellow
225
- undefined_features.each do |feature|
226
- error.puts " #{feature['name']}".yellow
227
- end
228
- end
229
- end
230
-
231
- # Prints the error for a given set of steps
232
- #
233
- # @param [String] banner
234
- # the text to prepend as the title
235
- #
236
- # @param [Array] steps
237
- # the steps to output
238
- #
239
- # @param [Symbol] color
240
- # The color code to use with Colorize to colorize the output.
241
- #
242
- def report_errors(banner, steps, color)
243
- error.puts " #{banner} (#{steps.length})".colorize(color)
244
- steps.each do |error|
245
- report_error error
246
- end
247
- error.puts ""
248
- end
249
-
250
- # Prints an error in a nice format
251
- #
252
- # @param [Array] error
253
- # An array containing the feature, scenario, step and exception
254
- #
255
- # @param [Symbol] format
256
- # The format to output the error. Currently supproted formats are
257
- # :summarized (default) and :full
258
- #
259
- # @returns [String]
260
- # The error report
261
- #
262
- def report_error(error, format=:summarized)
263
- case format
264
- when :summarized
265
- self.error.puts summarized_error(error)
266
- when :full
267
- self.error.puts full_error(error)
268
- else
269
- raise "Format not defined"
270
- end
271
- end
272
-
273
- # Returns summarized error report
274
- #
275
- # @param [Array] error
276
- # An array containing the feature, scenario, step and exception
277
- #
278
- # @returns [String]
279
- # The summarized error report
280
- #
281
- def summarized_error(error)
282
- feature, scenario, step, exception = error
283
- summary = " #{feature['name']} :: #{scenario['name']} :: #{full_step step}"
284
- if exception.kind_of?(Spinach::StepNotDefinedException)
285
- summary.yellow
286
- else
287
- summary.red
288
- end
289
- end
290
-
291
- # Returns a complete error report
292
- #
293
- # @param [Array] error
294
- # An array containing the feature, scenario, step and exception
295
- #
296
- # @returns [String]
297
- # The coplete error report
298
- #
299
- def full_error(error)
300
- feature, scenario, step, exception = error
301
- output = "\n"
302
- output += report_exception(exception)
303
- output +="\n"
304
-
305
- if exception.kind_of?(Spinach::StepNotDefinedException)
306
- output << "\n"
307
- output << " You can define it with: \n\n".yellow
308
- suggestion = Generators::StepGenerator.new(step).generate
309
- suggestion.split("\n").each do |line|
310
- output << " #{line}\n".yellow
311
- end
312
- output << "\n"
313
- else
314
- if options[:backtrace]
315
- output += "\n"
316
- exception.backtrace.map do |line|
317
- output << " #{line}\n"
318
- end
319
- else
320
- output << " #{exception.backtrace[0]}"
321
- end
322
- end
323
- output
324
- end
325
-
326
197
  # Constructs the full step definition
327
198
  #
328
199
  # @param [Hash] step
@@ -332,25 +203,6 @@ module Spinach
332
203
  "#{step['keyword'].strip} #{step['name'].strip}"
333
204
  end
334
205
 
335
- # Prints a information when an exception is raised.
336
- #
337
- # @param [Exception] exception
338
- # The exception to report
339
- #
340
- # @returns [String]
341
- # The exception report
342
- #
343
- def report_exception(exception)
344
- output = exception.message.split("\n").map{ |line|
345
- " #{line}"
346
- }.join("\n")
347
-
348
- if exception.kind_of?(Spinach::StepNotDefinedException)
349
- output.yellow
350
- else
351
- output.red
352
- end
353
- end
354
206
  end
355
207
  end
356
208
  end
@@ -19,35 +19,32 @@ module Spinach
19
19
 
20
20
  # A Hash with options for the reporter
21
21
  #
22
- attr_reader :options
23
-
24
- attr_accessor :current_feature, :current_scenario
22
+ attr_reader :options, :current_feature, :current_scenario
25
23
 
26
24
  attr_reader :undefined_steps, :failed_steps, :error_steps, :undefined_features, :successful_steps
27
25
 
26
+ # Hooks the reporter to the runner endpoints
28
27
  def bind
29
- runner.after_run method(:after_run)
30
- feature_runner.before_run method(:before_feature_run)
31
- feature_runner.after_run method(:after_feature_run)
32
- feature_runner.when_not_found method(:on_feature_not_found)
33
- scenario_runner.before_run method(:before_scenario_run)
34
- scenario_runner.after_run method(:after_scenario_run)
35
- scenario_runner.on_successful_step method(:on_successful_step)
36
- scenario_runner.on_undefined_step method(:on_undefined_step)
37
- scenario_runner.on_failed_step method(:on_failed_step)
38
- scenario_runner.on_error_step method(:on_error_step)
39
- scenario_runner.on_skipped_step method(:on_skipped_step)
28
+ Spinach.hooks.tap do |hooks|
29
+ hooks.after_run { |*args| after_run(*args) }
30
+ hooks.before_feature { |*args| before_feature_run(*args) }
31
+ hooks.after_feature { |*args| after_feature_run(*args) }
32
+ hooks.on_undefined_feature { |*args| on_feature_not_found(*args) }
33
+ hooks.before_scenario { |*args| before_scenario_run(*args) }
34
+ hooks.after_scenario { |*args| after_scenario_run(*args) }
35
+ hooks.on_successful_step { |*args| on_successful_step(*args) }
36
+ hooks.on_undefined_step { |*args| on_undefined_step(*args) }
37
+ hooks.on_failed_step { |*args| on_failed_step(*args) }
38
+ hooks.on_error_step { |*args| on_error_step(*args) }
39
+ hooks.on_skipped_step { |*args| on_skipped_step(*args) }
40
40
 
41
- feature_runner.before_run method(:current_feature=)
42
- feature_runner.after_run method(:clear_current_feature)
43
- scenario_runner.before_run method(:current_scenario=)
44
- scenario_runner.after_run method(:clear_current_scenario)
41
+ hooks.before_feature { |*args| set_current_feature(*args) }
42
+ hooks.after_feature { |*args| clear_current_feature(*args) }
43
+ hooks.before_scenario { |*args| set_current_scenario(*args) }
44
+ hooks.after_scenario { |*args| clear_current_scenario(*args) }
45
+ end
45
46
  end
46
47
 
47
- def feature_runner; Runner::Feature; end
48
- def scenario_runner; Runner::Scenario; end
49
- def runner; Runner; end
50
-
51
48
  def after_run(*args); end;
52
49
  def before_feature_run(*args); end
53
50
  def after_feature_run(*args); end
@@ -60,14 +57,31 @@ module Spinach
60
57
  def on_undefined_step(*args); end;
61
58
  def on_skipped_step(*args); end;
62
59
 
60
+ # Stores the current feature
61
+ #
62
+ # @param [Hash]
63
+ # the data for this feature
64
+ def set_current_feature(data)
65
+ @current_feature = data
66
+ end
67
+
68
+ # Clears this current feature
63
69
  def clear_current_feature(*args)
64
- self.current_feature = nil
70
+ @current_feature = nil
65
71
  end
66
72
 
67
- def clear_current_scenario(*args)
68
- self.current_scenario = nil
73
+ # Stores the current scenario
74
+ #
75
+ # @param [Hash]
76
+ # the data for this scenario
77
+ def set_current_scenario(data)
78
+ @current_scenario = data
69
79
  end
70
80
 
81
+ # Clears this current scenario
82
+ def clear_current_scenario(*args)
83
+ @current_scenario = nil
84
+ end
71
85
  end
72
86
  end
73
87
 
@@ -1,22 +1,12 @@
1
- require 'hooks'
2
-
3
1
  module Spinach
4
2
  class Runner
5
3
  # A feature runner handles a particular feature run.
6
4
  #
7
- class Feature
8
- include Hooks
9
-
10
- # The {Reporter} used in this feature.
11
- attr_reader :reporter
5
+ class FeatureRunner
12
6
 
13
7
  # The file that describes the feature.
14
8
  attr_reader :filename
15
9
 
16
- define_hook :before_run
17
- define_hook :after_run
18
- define_hook :when_not_found
19
-
20
10
  # @param [String] filename
21
11
  # path to the feature file. Scenario line could be passed to run just
22
12
  # that scenario.
@@ -62,20 +52,20 @@ module Spinach
62
52
  #
63
53
  # @api public
64
54
  def run
65
- run_hook :before_run, data
55
+ Spinach.hooks.run_before_feature data
66
56
 
67
57
  scenarios.each do |scenario|
68
58
  if !@scenario_line || scenario['line'].to_s == @scenario_line
69
- success = Scenario.new(feature_name, scenario).run
59
+ success = ScenarioRunner.new(feature_name, scenario).run
70
60
  @failed = true unless success
71
61
  end
72
62
  end
73
63
 
74
64
  rescue Spinach::FeatureStepsNotFoundException => e
75
- run_hook :when_not_found, data, e
65
+ Spinach.hooks.run_on_undefined_feature data, e
76
66
  @failed = true
77
67
  ensure
78
- run_hook :after_run, data
68
+ Spinach.hooks.run_after_feature data
79
69
  return !@failed
80
70
  end
81
71
  end
@@ -0,0 +1,65 @@
1
+ module Spinach
2
+ class Runner
3
+ # A Scenario Runner handles a particular scenario run.
4
+ #
5
+ class ScenarioRunner
6
+ attr_reader :feature_name, :data
7
+
8
+ # @param [String] feature_name
9
+ # The feature name
10
+ #
11
+ # @param [Hash] data
12
+ # The parsed feature data.
13
+ #
14
+ # @api public
15
+ def initialize(feature_name, data)
16
+ @feature_name = feature_name
17
+ @data = data
18
+ end
19
+
20
+ def steps
21
+ @steps ||= data['steps']
22
+ end
23
+
24
+ # @return [FeatureSteps]
25
+ # The feature object used to run this scenario.
26
+ #
27
+ # @api public
28
+ def feature_steps
29
+ @feature_steps ||= Spinach.find_feature_steps(feature_name).new
30
+ end
31
+
32
+ # Runs this scenario
33
+ # @return [True, False]
34
+ # true if this scenario succeeded, false if not
35
+ def run
36
+ Spinach.hooks.run_before_scenario data
37
+ steps.each do |step|
38
+ Spinach.hooks.run_before_step step
39
+ unless @exception
40
+ begin
41
+ step_location = feature_steps.execute_step(step['name'])
42
+ Spinach.hooks.run_on_successful_step step, step_location
43
+ rescue Spinach::FeatureStepsNotFoundException => e
44
+ raise e
45
+ rescue *Spinach.config[:failure_exceptions] => e
46
+ @exception = e
47
+ Spinach.hooks.run_on_failed_step step, @exception, step_location
48
+ rescue Spinach::StepNotDefinedException => e
49
+ @exception = e
50
+ Spinach.hooks.run_on_undefined_step step, @exception
51
+ rescue Exception => e
52
+ @exception = e
53
+ Spinach.hooks.run_on_error_step step, @exception, step_location
54
+ end
55
+ else
56
+ Spinach.hooks.run_on_skipped_step step
57
+ end
58
+ Spinach.hooks.run_after_step step
59
+ end
60
+ Spinach.hooks.run_after_scenario data
61
+ !@exception
62
+ end
63
+ end
64
+ end
65
+ end