# frozen_string_literal: true
require 'builder'
require 'cucumber/formatter/backtrace_filter'
require 'cucumber/formatter/io'
require 'cucumber/formatter/interceptor'
require 'fileutils'
require 'cucumber/formatter/ast_lookup'
module Cucumber
module Formatter
# The formatter used for --format junit
class Junit
include Io
class UnNamedFeatureError < StandardError
def initialize(feature_file)
super("The feature in '#{feature_file}' does not have a name. The JUnit XML format requires a name for the testsuite element.")
end
end
def initialize(config)
@ast_lookup = AstLookup.new(config)
config.on_event :test_case_started, &method(:on_test_case_started)
config.on_event :test_case_finished, &method(:on_test_case_finished)
config.on_event :test_step_finished, &method(:on_test_step_finished)
config.on_event :test_run_finished, &method(:on_test_run_finished)
@reportdir = ensure_dir(config.out_stream, 'junit')
@config = config
@features_data = Hash.new do |h, k|
h[k] = {
feature: nil,
failures: 0,
errors: 0,
tests: 0,
skipped: 0,
time: 0,
builder: Builder::XmlMarkup.new(indent: 2)
}
end
end
def on_test_case_started(event)
test_case = event.test_case
start_feature(test_case) unless same_feature_as_previous_test_case?(test_case)
@failing_test_step = nil
# In order to fill out and , we need to
# intercept the $stderr and $stdout
@interceptedout = Interceptor::Pipe.wrap(:stdout)
@interceptederr = Interceptor::Pipe.wrap(:stderr)
end
def on_test_step_finished(event)
test_step, result = *event.attributes
return if @failing_test_step
@failing_test_step = test_step unless result.ok?(@config.strict)
end
def on_test_case_finished(event)
test_case, result = *event.attributes
result = result.with_filtered_backtrace(Cucumber::Formatter::BacktraceFilter)
test_case_name = NameBuilder.new(test_case, @ast_lookup)
scenario = test_case_name.scenario_name
scenario_designation = "#{scenario}#{test_case_name.name_suffix}"
output = create_output_string(test_case, scenario, result, test_case_name.row_name)
build_testcase(result, scenario_designation, output)
Interceptor::Pipe.unwrap! :stdout
Interceptor::Pipe.unwrap! :stderr
end
def on_test_run_finished(_event)
@features_data.each { |_file, data| end_feature(data) }
end
private
def same_feature_as_previous_test_case?(test_case)
@current_feature_data && @current_feature_data[:uri] == test_case.location.file
end
def start_feature(test_case)
uri = test_case.location.file
feature = @ast_lookup.gherkin_document(uri).feature
raise UnNamedFeatureError, uri if feature.name.empty?
@current_feature_data = @features_data[uri]
@current_feature_data[:uri] = uri unless @current_feature_data[:uri]
@current_feature_data[:feature] = feature unless @current_feature_data[:feature]
end
def end_feature(feature_data)
@testsuite = Builder::XmlMarkup.new(indent: 2)
@testsuite.instruct!
@testsuite.testsuite(
failures: feature_data[:failures],
errors: feature_data[:errors],
skipped: feature_data[:skipped],
tests: feature_data[:tests],
time: format('%