|
- # frozen_string_literal: true
-
- require 'cucumber/formatter/progress'
- require 'cucumber/step_definition_light'
- require 'cucumber/formatter/console'
-
- module Cucumber
- module Formatter
- class Usage < Progress
- include Console
- class StepDefKey < StepDefinitionLight
- attr_accessor :mean_duration, :status
- end
-
- def initialize(config)
- super
- @stepdef_to_match = Hash.new { |h, stepdef_key| h[stepdef_key] = [] }
- @total_duration = 0
- @matches = {}
- config.on_event :step_activated do |event|
- test_step, step_match = *event.attributes
- @matches[test_step.to_s] = step_match
- end
- config.on_event :step_definition_registered, &method(:on_step_definition_registered)
- end
-
- def on_step_definition_registered(event)
- stepdef_key = StepDefKey.new(event.step_definition.expression.to_s, event.step_definition.location)
- @stepdef_to_match[stepdef_key] = []
- end
-
- def on_step_match(event)
- @matches[event.test_step.to_s] = event.step_match
- super
- end
-
- def on_test_step_finished(event)
- return if event.test_step.hook?
-
- test_step = event.test_step
- result = event.result.with_filtered_backtrace(Cucumber::Formatter::BacktraceFilter)
- step_match = @matches[test_step.to_s]
-
- unless step_match.nil?
- step_definition = step_match.step_definition
- stepdef_key = StepDefKey.new(step_definition.expression.to_s, step_definition.location)
- unless @stepdef_to_match[stepdef_key].map { |key| key[:location] }.include? test_step.location
- duration = DurationExtractor.new(result).result_duration
- keyword = @ast_lookup.step_source(test_step).step.keyword
-
- @stepdef_to_match[stepdef_key] << {
- keyword: keyword,
- step_match: step_match,
- status: result.to_sym,
- location: test_step.location,
- duration: duration
- }
- end
- end
-
- super
- end
-
- private
-
- def print_summary
- aggregate_info
-
- keys = if config.dry_run?
- @stepdef_to_match.keys.sort_by(&:regexp_source)
- else
- @stepdef_to_match.keys.sort_by(&:mean_duration).reverse
- end
-
- keys.each do |stepdef_key|
- print_step_definition(stepdef_key)
-
- if @stepdef_to_match[stepdef_key].any?
- print_steps(stepdef_key)
- else
- @io.puts(" #{format_string('NOT MATCHED BY ANY STEPS', :failed)}")
- end
- end
- @io.puts
- super
- end
-
- def print_step_definition(stepdef_key)
- @io.print "#{format_string(format('%<duration>.7f', duration: stepdef_key.mean_duration), :skipped)} " unless config.dry_run?
- @io.print format_string(stepdef_key.regexp_source, stepdef_key.status)
- if config.source?
- indent_amount = max_length - stepdef_key.regexp_source.unpack('U*').length
- line_comment = indent(" # #{stepdef_key.location}", indent_amount)
- @io.print(format_string(line_comment, :comment))
- end
- @io.puts
- end
-
- def print_steps(stepdef_key)
- @stepdef_to_match[stepdef_key].each do |step|
- @io.print ' '
- @io.print "#{format_string(format('%<duration>.7f', duration: step[:duration]), :skipped)} " unless config.dry_run?
- @io.print format_step(step[:keyword], step[:step_match], step[:status], nil)
- if config.source?
- indent_amount = max_length - (step[:keyword].unpack('U*').length + step[:step_match].format_args.unpack('U*').length)
- line_comment = indent(" # #{step[:location]}", indent_amount)
- @io.print(format_string(line_comment, :comment))
- end
- @io.puts
- end
- end
-
- def max_length
- [max_stepdef_length, max_step_length].compact.max
- end
-
- def max_stepdef_length
- @stepdef_to_match.keys.flatten.map { |key| key.regexp_source.unpack('U*').length }.max
- end
-
- def max_step_length
- @stepdef_to_match.values.to_a.flatten.map do |step|
- step[:keyword].unpack('U*').length + step[:step_match].format_args.unpack('U*').length
- end.max
- end
-
- def aggregate_info
- @stepdef_to_match.each do |key, steps|
- if steps.empty?
- key.status = :skipped
- key.mean_duration = 0
- else
- key.status = worst_status(steps.map { |step| step[:status] })
- total_duration = steps.inject(0) { |sum, step| step[:duration] + sum }
- key.mean_duration = total_duration / steps.length
- end
- end
- end
-
- def worst_status(statuses)
- %i[passed undefined pending skipped failed].find do |status|
- statuses.include?(status)
- end
- end
- end
- end
- end
|