lib/gurke/reporter.rb in gurke-2.0.0.dev.1.b17 vs lib/gurke/reporter.rb in gurke-2.0.0.dev.1.b18

- old
+ new

@@ -1,98 +1,259 @@ -require 'colorize' - -# Colorize colors: -# :black, :red, :green, :yellow, :blue, -# :magenta, :cyan, :white, :default, :light_black, -# :light_red, :light_green, :light_yellow, :light_blue, -# :light_magenta, :light_cyan, :light_white - module Gurke # + # A {Reporter} provides callbacks that will be executed whenever + # a specific execution step starts or ends. + # + # @api public + # class Reporter - def start_features(*) + # rubocop:disable UnusedMethodArgument + + # List of all callback methods as symbols. + # + CALLBACKS = [ + :before_features, + :before_feature, + :before_scenario, + :before_step, + :start_features, + :start_feature, + :start_scenario, + :start_background, + :start_step, + :end_features, + :end_feature, + :end_scenario, + :end_background, + :end_step, + :after_features, + :after_feature, + :after_scenario, + :after_step + ] + + # Called before the execution of any feature and before any + # before-features hook is invoked. + # + # @param features [Array<Feature>] List of all features that + # are going to be executed. + # + # @api public + # + def before_features(features) + raise NotImplementedError.new \ + "#{self.class.name}#before_features must be implemented in subclass." end - def start_feature(feature) - io.puts "#{yellow('Feature')}: #{feature.name}" - io.puts ' ' + light_black(feature.description.split("\n").join("\n ")) - io.puts + # Called before the execute of any feature, but after all + # before-features hooks. + # + # @param features [Array<Feature>] List of all features that + # are going to be executed. + # + # @api public + # + def start_features(features) + raise NotImplementedError.new \ + "#{self.class.name}#before_features must be implemented in subclass." end - def start_scenario(scenario, feature) - io.puts " #{yellow('Scenario')}: #{scenario.name}" - io.puts light_black(' Background:') if feature.backgrounds.any? + # Called for each feature before it starts, but before any + # before-feature hook is run. + # + # @param feature [Feature] The feature that is going to + # be executed now. + # + # @api public + # + def before_feature(feature) + raise NotImplementedError.new \ + "#{self.class.name}#start_feature must be implemented in subclass." end - def start_background(*) - @background = true + # Called for each feature before it starts, but after + # all before-feature hooks. + # + # @param feature [Feature] The feature that is going to + # be executed now. + # + # @api public + # + def start_feature(feature) + raise NotImplementedError.new \ + "#{self.class.name}#start_feature must be implemented in subclass." end - def finish_background(*) - @background = false + # Called for each each scenario before it starts. Will be + # called before any hooks for the given scenario is executed. + # + # @param scenario [Scenario] Current scenario. + # + # @api public + # + def before_scenario(scenario) + raise NotImplementedError.new \ + "#{self.class.name}#before_scenario must be implemented in subclass." end - def start_step(step, *) - io.print ' ' if @background - io.print ' ' - io.print yellow(step.keyword) - io.print step.name.gsub(/"(.*?)"/, cyan('\0')) + # Called for each each scenario before it starts, but after + # all before hooks for given scenario. + # + # @param scenario [Scenario] Current scenario. + # + # @api public + # + def start_scenario(scenario) + raise NotImplementedError.new \ + "#{self.class.name}#start_scenario must be implemented in subclass." end - def finish_step(step, *) - case step.state - when :pending - print_braces yellow('pending') - when :failed - print_braces red('failure') - io.puts - io.puts red(" #{step.exception.class}:") + # Called before each background. + # + # @param background [Background] Current background. + # @param scenario [Scenario] Current scenario. + # + # @api public + # + def start_background(background, scenario) + raise NotImplementedError.new \ + "#{self.class.name}#start_background must be implemented in subclass." + end - msg = step.exception.message.split("\n").join("\n ") - io.puts red(" #{msg}") + # Called after each background. + # + # @param background [Background] Current background. + # @param scenario [Scenario] Current scenario. + # + # @api public + # + def end_background(background, scenario) + raise NotImplementedError.new \ + "#{self.class.name}#end_background must be implemented in subclass." + end - io.puts red(" #{step.exception.backtrace.join("\n ")}") - when :success - print_braces green('success') - else - print_braces cyan('skipped') - end - io.puts - io.flush + # Called before each step and before any before-step hook. + # + # @param step [Step] Current Step. + # @param scenario [Scenario] Current scenario. + # + # @api public + # + def before_step(step, scenario) + raise NotImplementedError.new \ + "#{self.class.name}#before_step must be implemented in subclass." end - def finish_scenario(*) - io.puts + # Called before each step and after all before-step hooks. + # + # @param step [Step] Current Step. + # @param scenario [Scenario] Current scenario. + # + # @api public + # + def start_step(step, scenario) + raise NotImplementedError.new \ + "#{self.class.name}#start_step must be implemented in subclass." end - def finish_feature(*) - io.puts + # Called after each step but before any after-step hook. + # + # @param step_result [StepResult] Result of current Step. + # @param scenario [Scenario] Current scenario. + # @param feature [Feature] Current feature. + # + # @api public + # + def end_step(step_result, scenario) + raise NotImplementedError.new \ + "#{self.class.name}#end_step must be implemented in subclass." end - def finish_features(features) - scenarios = features.map(&:scenarios).flatten + # Called after each step, after all step hook. + # + # @param step_result [StepResult] Result of current Step. + # @param scenario [Scenario] Current scenario. + # @param feature [Feature] Current feature. + # + # @api public + # + def after_step(step_result, scenario) + raise NotImplementedError.new \ + "#{self.class.name}#after_step must be implemented in subclass." + end - io.puts " #{scenarios.size} scenarios: "\ - "#{scenarios.select(&:failed?).size} failing, "\ - "#{scenarios.select(&:pending?).size} pending" - io.puts + # Called after each scenario but before any after hook. + # + # @param scenario [Scenario] Current scenario. + # @param feature [Feature] Current feature. + # + # @api public + # + def end_scenario(scenario) + raise NotImplementedError.new \ + "#{self.class.name}#end_scenario must be implemented in subclass." end - private + # Called after each scenario and after all hooks. + # + # @param scenario [Scenario] Current scenario. + # @param feature [Feature] Current feature. + # + # @api public + # + def after_scenario(scenario) + raise NotImplementedError.new \ + "#{self.class.name}#after_scenario must be implemented in subclass." + end - def print_braces(str) - io.print " (#{str})" + # Called after each feature but before any after hook. + # + # @param feature [Feature] Current feature. + # + # @api public + # + def end_feature(feature) + raise NotImplementedError.new \ + "#{self.class.name}#end_feature must be implemented in subclass." end - def io - $stdout + # Called after each feature and after all hooks. + # + # @param feature [Feature] Current feature. + # + # @api public + # + def after_feature(feature) + raise NotImplementedError.new \ + "#{self.class.name}#after_feature must be implemented in subclass." end - [:black, :red, :green, :yellow, :blue, - :magenta, :cyan, :white, :default, :light_black, - :light_red, :light_green, :light_yellow, :light_blue, - :light_magenta, :light_cyan, :light_white].each do |color| + # Called after all features but before any after-features hook. + # + # @param features [Array<Feature>] List of all features. + # + # @api public + # + def end_features(features) + raise NotImplementedError.new \ + "#{self.class.name}#end_features must be implemented in subclass." + end - define_method(color){|str| io.tty? ? str.send(color) : str } + # Called after all features and after all hooks. + # + # @param features [Array<Feature>] List of all features. + # + # @api public + # + def after_features(features) + raise NotImplementedError.new \ + "#{self.class.name}#after_features must be implemented in subclass." + end + + # @visibility private + def invoke(mth, *args) + send mth, *args + rescue => e + warn "Rescued in reporter: #{e}\n" + e.backtrace.join("\n") end end end