diff options
Diffstat (limited to 'spec/support/formatters/json_formatter.rb')
-rw-r--r-- | spec/support/formatters/json_formatter.rb | 101 |
1 files changed, 101 insertions, 0 deletions
diff --git a/spec/support/formatters/json_formatter.rb b/spec/support/formatters/json_formatter.rb new file mode 100644 index 00000000000..10af5445b7a --- /dev/null +++ b/spec/support/formatters/json_formatter.rb @@ -0,0 +1,101 @@ +# frozen_string_literal: true + +require 'rspec/core/formatters' + +module Support + module Formatters + class JsonFormatter < RSpec::Core::Formatters::JsonFormatter + QA_SUPPORT_LOGLINKING_CONST = 'QA::Support::Loglinking' + + RSpec::Core::Formatters.register self, :message, :dump_summary, :stop, :seed, :close + + def dump_profile(profile) + # We don't currently use the profile info. This overrides the base + # implementation so that it's not included. + end + + def stop(example_notification) + # Based on https://github.com/rspec/rspec-core/blob/main/lib/rspec/core/formatters/json_formatter.rb#L35 + # But modified to include full details of multiple exceptions and to provide output similar to + # https://github.com/sj26/rspec_junit_formatter + @output_hash[:examples] = example_notification.notifications.map do |notification| + format_example(notification.example).tap do |hash| + e = notification.example.exception + if e + exceptions = e.respond_to?(:all_exceptions) ? e.all_exceptions : [e] + hash[:exceptions] = exceptions.map do |exception| + hash = { + class: exception.class.name, + message: exception.message, + message_lines: strip_ansi_codes(notification.message_lines), + backtrace: notification.formatted_backtrace + } + + if loglinking + hash.merge!( + correlation_id: exception.message[match_data_after(loglinking::CORRELATION_ID_TITLE)], + sentry_url: exception.message[match_data_after(loglinking::SENTRY_URL_TITLE)], + kibana_discover_url: exception.message[match_data_after(loglinking::KIBANA_DISCOVER_URL_TITLE)], + kibana_dashboard_url: exception.message[match_data_after(loglinking::KIBANA_DASHBOARD_URL_TITLE)] + ) + end + + hash + end + end + end + end + end + + private + + def loglinking + return @loglinking if defined?(@loglinking) + + @loglinking = Object.const_defined?(QA_SUPPORT_LOGLINKING_CONST) && + Object.const_get(QA_SUPPORT_LOGLINKING_CONST, false) + end + + def format_example(example) + file_path, line_number = location_including_shared_examples(example.metadata) + + { + id: example.id, + description: example.description, + full_description: example.full_description, + status: example.execution_result.status.to_s, + file_path: file_path, + line_number: line_number.to_i, + run_time: example.execution_result.run_time, + pending_message: example.execution_result.pending_message, + testcase: example.metadata[:testcase], + quarantine: example.metadata[:quarantine], + screenshot: example.metadata[:screenshot], + product_group: example.metadata[:product_group], + feature_category: example.metadata[:feature_category], + ci_job_url: ENV['CI_JOB_URL'], + retry_attempts: example.metadata[:retry_attempts] + } + end + + def location_including_shared_examples(metadata) + if metadata[:shared_group_inclusion_backtrace].empty? + [metadata[:file_path], metadata[:line_number]] + else + # If there are nested shared examples, the outermost location is last in the array + metadata[:shared_group_inclusion_backtrace].last.formatted_inclusion_location.split(':') + end + end + + def strip_ansi_codes(strings) + # The code below is from https://github.com/piotrmurach/pastel/blob/master/lib/pastel/color.rb + modified = Array(strings).map { |string| string.dup.gsub(/\x1b\[{1,2}[0-9;:?]*m/m, '') } + modified.size == 1 ? modified[0] : modified + end + + def match_data_after(title) + /(?<=#{title} ).*/ + end + end + end +end |