Skip to content

Commit

Permalink
Color unhandled errors appropriately
Browse files Browse the repository at this point in the history
If an unhandled error occurs — that is, if an error from a matcher that
the gem does not explicitly support occurs, or a random exception occurs
— only color the first line of the error in red.
  • Loading branch information
mcmire committed Oct 5, 2019
1 parent 5a1d615 commit f52e4be
Show file tree
Hide file tree
Showing 2 changed files with 150 additions and 4 deletions.
60 changes: 56 additions & 4 deletions lib/super_diff/rspec/monkey_patches.rb
Original file line number Diff line number Diff line change
Expand Up @@ -82,16 +82,35 @@ class ExceptionPresenter
def initialize(exception, example, options={})
@exception = exception
@example = example
# Patch to use no color by default
# TODO: Only use color if no diff is being printed
@message_color = options[:message_color]
@message_color = options.fetch(:message_color) { RSpec.configuration.failure_color }
@description = options.fetch(:description) { example.full_description }
@detail_formatter = options.fetch(:detail_formatter) { Proc.new {} }
@extra_detail_formatter = options.fetch(:extra_detail_formatter) { Proc.new {} }
@backtrace_formatter = options.fetch(:backtrace_formatter) { RSpec.configuration.backtrace_formatter }
@indentation = options.fetch(:indentation, 2)
@skip_shared_group_trace = options.fetch(:skip_shared_group_trace, false)
@failure_lines = options[:failure_lines]
# Patch to convert options[:failure_lines] to groups
if options.include?(:failure_lines)
@failure_line_groups = {
lines: options[:failure_lines],
already_colored: false
}
end
end

# Override to only color uncolored lines in red
def colorized_message_lines(colorizer=::RSpec::Core::Formatters::ConsoleCodes)
lines = failure_line_groups.flat_map do |group|
if group[:already_colored]
group[:lines]
else
group[:lines].map do |line|
colorizer.wrap(line, message_color)
end
end
end

add_shared_group_lines(lines, colorizer)
end

private
Expand All @@ -107,6 +126,39 @@ def add_shared_group_lines(lines, colorizer)
lines
end

# Considering that `failure_slash_error_lines` is already colored,
# extract this from the other lines so that they, too, can be colored,
# later
def failure_line_groups
@failure_line_groups ||= [].tap do |groups|
groups << {
lines: failure_slash_error_lines,
already_colored: true
}

sections = [failure_slash_error_lines, exception_lines]
separate_groups = (
sections.any? { |section| section.size > 1 } &&
!exception_lines.first.empty?
)
if separate_groups
groups << { lines: [''], already_colored: true }
end
already_has_coloration = exception_lines.any? do |line|
line.match?(/\e\[\d+m/)
end

groups << {
lines: exception_lines[0..0],
already_colored: already_has_coloration
}
groups << {
lines: exception_lines[1..-1] + extra_failure_lines,
already_colored: true
}
end
end

# Style the first part in white and don't style the snippet of the line
def failure_slash_error_lines
lines = read_failed_lines
Expand Down
94 changes: 94 additions & 0 deletions spec/integration/rspec/unhandled_errors_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
require "spec_helper"

RSpec.describe "Integration with RSpec and unhandled errors", type: :integration do
context "when a random exception occurs" do
it "highlights the whole output after the code snippet in red" do
as_both_colored_and_uncolored do |color_enabled|
snippet = <<~TEST.strip
raise "Some kind of error or whatever"
TEST
program = make_plain_test_program(
snippet,
color_enabled: color_enabled,
)

expected_output = colored(color_enabled: color_enabled) do
line "Failures:\n"

line "1) test passes", indent_by: 2

line indent_by: 5 do
bold "Failure/Error: "
plain %|raise "Some kind of error or whatever"|
end

newline

indent by: 5 do
red_line "RuntimeError:"
plain_line " Some kind of error or whatever"
end
end

expect(program).
to produce_output_when_run(expected_output).
in_color(color_enabled)
end
end
end

context "when a matcher that has a multi-line message fails" do
it "highlights the first line of the failure message in red" do
as_both_colored_and_uncolored do |color_enabled|
snippet = <<~TEST.strip
RSpec::Matchers.define :fail_with_multiline_message do
match do
false
end
failure_message do
<<\~MESSAGE
First line
Second line
Third line
MESSAGE
end
end
expect(:foo).to fail_with_multiline_message
TEST
program = make_plain_test_program(
snippet,
color_enabled: color_enabled,
)

expected_output = colored(color_enabled: color_enabled) do
line "Failures:\n"

line "1) test passes", indent_by: 2

line indent_by: 5 do
bold "Failure/Error: "
plain %|expect(:foo).to fail_with_multiline_message|
end

newline

indent by: 5 do
red_line " First line"
newline
plain_line " Second line"
newline
plain_line " Third line"
end
end

expect(program).
to produce_output_when_run(expected_output).
in_color(color_enabled)
end
end
end
end

0 comments on commit f52e4be

Please sign in to comment.