Skip to content

Commit f52e4be

Browse files
committed
Color unhandled errors appropriately
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.
1 parent 5a1d615 commit f52e4be

File tree

2 files changed

+150
-4
lines changed

2 files changed

+150
-4
lines changed

lib/super_diff/rspec/monkey_patches.rb

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,16 +82,35 @@ class ExceptionPresenter
8282
def initialize(exception, example, options={})
8383
@exception = exception
8484
@example = example
85-
# Patch to use no color by default
86-
# TODO: Only use color if no diff is being printed
87-
@message_color = options[:message_color]
85+
@message_color = options.fetch(:message_color) { RSpec.configuration.failure_color }
8886
@description = options.fetch(:description) { example.full_description }
8987
@detail_formatter = options.fetch(:detail_formatter) { Proc.new {} }
9088
@extra_detail_formatter = options.fetch(:extra_detail_formatter) { Proc.new {} }
9189
@backtrace_formatter = options.fetch(:backtrace_formatter) { RSpec.configuration.backtrace_formatter }
9290
@indentation = options.fetch(:indentation, 2)
9391
@skip_shared_group_trace = options.fetch(:skip_shared_group_trace, false)
94-
@failure_lines = options[:failure_lines]
92+
# Patch to convert options[:failure_lines] to groups
93+
if options.include?(:failure_lines)
94+
@failure_line_groups = {
95+
lines: options[:failure_lines],
96+
already_colored: false
97+
}
98+
end
99+
end
100+
101+
# Override to only color uncolored lines in red
102+
def colorized_message_lines(colorizer=::RSpec::Core::Formatters::ConsoleCodes)
103+
lines = failure_line_groups.flat_map do |group|
104+
if group[:already_colored]
105+
group[:lines]
106+
else
107+
group[:lines].map do |line|
108+
colorizer.wrap(line, message_color)
109+
end
110+
end
111+
end
112+
113+
add_shared_group_lines(lines, colorizer)
95114
end
96115

97116
private
@@ -107,6 +126,39 @@ def add_shared_group_lines(lines, colorizer)
107126
lines
108127
end
109128

129+
# Considering that `failure_slash_error_lines` is already colored,
130+
# extract this from the other lines so that they, too, can be colored,
131+
# later
132+
def failure_line_groups
133+
@failure_line_groups ||= [].tap do |groups|
134+
groups << {
135+
lines: failure_slash_error_lines,
136+
already_colored: true
137+
}
138+
139+
sections = [failure_slash_error_lines, exception_lines]
140+
separate_groups = (
141+
sections.any? { |section| section.size > 1 } &&
142+
!exception_lines.first.empty?
143+
)
144+
if separate_groups
145+
groups << { lines: [''], already_colored: true }
146+
end
147+
already_has_coloration = exception_lines.any? do |line|
148+
line.match?(/\e\[\d+m/)
149+
end
150+
151+
groups << {
152+
lines: exception_lines[0..0],
153+
already_colored: already_has_coloration
154+
}
155+
groups << {
156+
lines: exception_lines[1..-1] + extra_failure_lines,
157+
already_colored: true
158+
}
159+
end
160+
end
161+
110162
# Style the first part in white and don't style the snippet of the line
111163
def failure_slash_error_lines
112164
lines = read_failed_lines
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
require "spec_helper"
2+
3+
RSpec.describe "Integration with RSpec and unhandled errors", type: :integration do
4+
context "when a random exception occurs" do
5+
it "highlights the whole output after the code snippet in red" do
6+
as_both_colored_and_uncolored do |color_enabled|
7+
snippet = <<~TEST.strip
8+
raise "Some kind of error or whatever"
9+
TEST
10+
program = make_plain_test_program(
11+
snippet,
12+
color_enabled: color_enabled,
13+
)
14+
15+
expected_output = colored(color_enabled: color_enabled) do
16+
line "Failures:\n"
17+
18+
line "1) test passes", indent_by: 2
19+
20+
line indent_by: 5 do
21+
bold "Failure/Error: "
22+
plain %|raise "Some kind of error or whatever"|
23+
end
24+
25+
newline
26+
27+
indent by: 5 do
28+
red_line "RuntimeError:"
29+
plain_line " Some kind of error or whatever"
30+
end
31+
end
32+
33+
expect(program).
34+
to produce_output_when_run(expected_output).
35+
in_color(color_enabled)
36+
end
37+
end
38+
end
39+
40+
context "when a matcher that has a multi-line message fails" do
41+
it "highlights the first line of the failure message in red" do
42+
as_both_colored_and_uncolored do |color_enabled|
43+
snippet = <<~TEST.strip
44+
RSpec::Matchers.define :fail_with_multiline_message do
45+
match do
46+
false
47+
end
48+
49+
failure_message do
50+
<<\~MESSAGE
51+
First line
52+
53+
Second line
54+
55+
Third line
56+
MESSAGE
57+
end
58+
end
59+
60+
expect(:foo).to fail_with_multiline_message
61+
TEST
62+
program = make_plain_test_program(
63+
snippet,
64+
color_enabled: color_enabled,
65+
)
66+
67+
expected_output = colored(color_enabled: color_enabled) do
68+
line "Failures:\n"
69+
70+
line "1) test passes", indent_by: 2
71+
72+
line indent_by: 5 do
73+
bold "Failure/Error: "
74+
plain %|expect(:foo).to fail_with_multiline_message|
75+
end
76+
77+
newline
78+
79+
indent by: 5 do
80+
red_line " First line"
81+
newline
82+
plain_line " Second line"
83+
newline
84+
plain_line " Third line"
85+
end
86+
end
87+
88+
expect(program).
89+
to produce_output_when_run(expected_output).
90+
in_color(color_enabled)
91+
end
92+
end
93+
end
94+
end

0 commit comments

Comments
 (0)