super_diff 0.6.1 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/super_diff.rb +24 -0
- data/lib/super_diff/diff_formatters/collection.rb +1 -1
- data/lib/super_diff/equality_matchers/array.rb +2 -2
- data/lib/super_diff/equality_matchers/default.rb +2 -2
- data/lib/super_diff/equality_matchers/hash.rb +2 -2
- data/lib/super_diff/equality_matchers/multiline_string.rb +2 -2
- data/lib/super_diff/equality_matchers/primitive.rb +2 -2
- data/lib/super_diff/equality_matchers/singleline_string.rb +2 -2
- data/lib/super_diff/object_inspection.rb +0 -8
- data/lib/super_diff/object_inspection/nodes/inspection.rb +1 -1
- data/lib/super_diff/rspec/monkey_patches.rb +343 -304
- data/lib/super_diff/version.rb +1 -1
- data/spec/combustion/Gemfile.lock +173 -0
- data/spec/examples.txt +403 -387
- data/spec/spec_helper.rb +8 -0
- data/spec/unit/{object_inspection_spec.rb → super_diff_spec.rb} +136 -76
- metadata +6 -6
- data/spec/tmp/integration_spec.rb +0 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: be7fd08d833b19c6a99401a1c7b80c448903c315d198b09e55baf70ee663dcfc
|
4
|
+
data.tar.gz: ea79d7bb6469e711058b71716c9ce1a23d6353381a8ae876c6bb180a57e78e2a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 75a1258f1d15d4a830a89ad898cefed4d6379d012fd7aa50d61f82672f10d0acacbf32e88177970fe46afa977dbce42c30212194867eefa1a72eb874417a1156
|
7
|
+
data.tar.gz: eb40c7881a21263fc89c6c2cfd1e54c5277449f98a32fb266bc720c43177772480d9834b61c4614102c236942d46ffcea82bc4e5090ad5f70dfc6d15f1d9e184
|
data/lib/super_diff.rb
CHANGED
@@ -30,10 +30,34 @@ module SuperDiff
|
|
30
30
|
@_configuration ||= Configuration.new
|
31
31
|
end
|
32
32
|
|
33
|
+
def self.inspect_object(object, as_single_line:, indent_level: 0)
|
34
|
+
ObjectInspection::Inspectors::Main.call(
|
35
|
+
object,
|
36
|
+
as_single_line: as_single_line,
|
37
|
+
indent_level: indent_level,
|
38
|
+
)
|
39
|
+
end
|
40
|
+
|
33
41
|
def self.time_like?(value)
|
34
42
|
# Check for ActiveSupport's #acts_like_time? for their time-like objects
|
35
43
|
# (like ActiveSupport::TimeWithZone).
|
36
44
|
(value.respond_to?(:acts_like_time?) && value.acts_like_time?) ||
|
37
45
|
value.is_a?(Time)
|
38
46
|
end
|
47
|
+
|
48
|
+
def self.insert_overrides(target_module, mod = nil, &block)
|
49
|
+
if mod
|
50
|
+
target_module.prepend(mod)
|
51
|
+
else
|
52
|
+
target_module.prepend(Module.new(&block))
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.insert_singleton_overrides(target_module, mod = nil, &block)
|
57
|
+
if mod
|
58
|
+
target_module.singleton_class.prepend(mod)
|
59
|
+
else
|
60
|
+
target_module.singleton_class.prepend(Module.new(&block))
|
61
|
+
end
|
62
|
+
end
|
39
63
|
end
|
@@ -13,14 +13,14 @@ module SuperDiff
|
|
13
13
|
Helpers.style(
|
14
14
|
:expected,
|
15
15
|
"Expected: " +
|
16
|
-
|
16
|
+
SuperDiff.inspect_object(expected, as_single_line: true),
|
17
17
|
)
|
18
18
|
}
|
19
19
|
#{
|
20
20
|
Helpers.style(
|
21
21
|
:actual,
|
22
22
|
" Actual: " +
|
23
|
-
|
23
|
+
SuperDiff.inspect_object(actual, as_single_line: true),
|
24
24
|
)
|
25
25
|
}
|
26
26
|
|
@@ -21,7 +21,7 @@ module SuperDiff
|
|
21
21
|
Helpers.style(
|
22
22
|
:expected,
|
23
23
|
"Expected: " +
|
24
|
-
|
24
|
+
SuperDiff.inspect_object(expected, as_single_line: true),
|
25
25
|
)
|
26
26
|
end
|
27
27
|
|
@@ -29,7 +29,7 @@ module SuperDiff
|
|
29
29
|
Helpers.style(
|
30
30
|
:actual,
|
31
31
|
" Actual: " +
|
32
|
-
|
32
|
+
SuperDiff.inspect_object(actual, as_single_line: true),
|
33
33
|
)
|
34
34
|
end
|
35
35
|
|
@@ -13,14 +13,14 @@ module SuperDiff
|
|
13
13
|
Helpers.style(
|
14
14
|
:expected,
|
15
15
|
"Expected: " +
|
16
|
-
|
16
|
+
SuperDiff.inspect_object(expected, as_single_line: true),
|
17
17
|
)
|
18
18
|
}
|
19
19
|
#{
|
20
20
|
Helpers.style(
|
21
21
|
:actual,
|
22
22
|
" Actual: " +
|
23
|
-
|
23
|
+
SuperDiff.inspect_object(actual, as_single_line: true),
|
24
24
|
)
|
25
25
|
}
|
26
26
|
|
@@ -14,14 +14,14 @@ module SuperDiff
|
|
14
14
|
Helpers.style(
|
15
15
|
:expected,
|
16
16
|
"Expected: " +
|
17
|
-
|
17
|
+
SuperDiff.inspect_object(expected, as_single_line: true),
|
18
18
|
)
|
19
19
|
}
|
20
20
|
#{
|
21
21
|
Helpers.style(
|
22
22
|
:actual,
|
23
23
|
" Actual: " +
|
24
|
-
|
24
|
+
SuperDiff.inspect_object(actual, as_single_line: true),
|
25
25
|
)
|
26
26
|
}
|
27
27
|
|
@@ -17,14 +17,14 @@ module SuperDiff
|
|
17
17
|
Helpers.style(
|
18
18
|
:expected,
|
19
19
|
"Expected: " +
|
20
|
-
|
20
|
+
SuperDiff.inspect_object(expected, as_single_line: true),
|
21
21
|
)
|
22
22
|
}
|
23
23
|
#{
|
24
24
|
Helpers.style(
|
25
25
|
:actual,
|
26
26
|
" Actual: " +
|
27
|
-
|
27
|
+
SuperDiff.inspect_object(actual, as_single_line: true),
|
28
28
|
)
|
29
29
|
}
|
30
30
|
OUTPUT
|
@@ -13,14 +13,14 @@ module SuperDiff
|
|
13
13
|
Helpers.style(
|
14
14
|
:expected,
|
15
15
|
"Expected: " +
|
16
|
-
|
16
|
+
SuperDiff.inspect_object(expected, as_single_line: true),
|
17
17
|
)
|
18
18
|
}
|
19
19
|
#{
|
20
20
|
Helpers.style(
|
21
21
|
:actual,
|
22
22
|
" Actual: " +
|
23
|
-
|
23
|
+
SuperDiff.inspect_object(actual, as_single_line: true),
|
24
24
|
)
|
25
25
|
}
|
26
26
|
OUTPUT
|
@@ -3,13 +3,5 @@ module SuperDiff
|
|
3
3
|
autoload :InspectionTree, "super_diff/object_inspection/inspection_tree"
|
4
4
|
autoload :Inspectors, "super_diff/object_inspection/inspectors"
|
5
5
|
autoload :Nodes, "super_diff/object_inspection/nodes"
|
6
|
-
|
7
|
-
def self.inspect(object, as_single_line:, indent_level: 0)
|
8
|
-
Inspectors::Main.call(
|
9
|
-
object,
|
10
|
-
as_single_line: as_single_line,
|
11
|
-
indent_level: indent_level,
|
12
|
-
)
|
13
|
-
end
|
14
6
|
end
|
15
7
|
end
|
@@ -12,34 +12,38 @@ require "rspec/matchers/built_in/match"
|
|
12
12
|
|
13
13
|
module RSpec
|
14
14
|
module Expectations
|
15
|
-
|
16
|
-
|
15
|
+
SuperDiff.insert_singleton_overrides(self) do
|
16
|
+
def differ
|
17
|
+
SuperDiff::RSpec::Differ
|
18
|
+
end
|
17
19
|
end
|
18
20
|
|
19
21
|
module ExpectationHelper
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
matcher.expected_for_diff
|
29
|
-
|
30
|
-
|
31
|
-
|
22
|
+
SuperDiff.insert_singleton_overrides(self) do
|
23
|
+
def handle_failure(matcher, message, failure_message_method)
|
24
|
+
message = message.call if message.respond_to?(:call)
|
25
|
+
message ||= matcher.__send__(failure_message_method)
|
26
|
+
|
27
|
+
if matcher.respond_to?(:diffable?) && matcher.diffable?
|
28
|
+
# Look for expected_for_diff and actual_for_diff if possible
|
29
|
+
expected =
|
30
|
+
if matcher.respond_to?(:expected_for_diff)
|
31
|
+
matcher.expected_for_diff
|
32
|
+
else
|
33
|
+
matcher.expected
|
34
|
+
end
|
32
35
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
36
|
+
actual =
|
37
|
+
if matcher.respond_to?(:actual_for_diff)
|
38
|
+
matcher.actual_for_diff
|
39
|
+
else
|
40
|
+
matcher.actual
|
41
|
+
end
|
39
42
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
+
::RSpec::Expectations.fail_with(message, expected, actual)
|
44
|
+
else
|
45
|
+
::RSpec::Expectations.fail_with(message)
|
46
|
+
end
|
43
47
|
end
|
44
48
|
end
|
45
49
|
end
|
@@ -48,29 +52,31 @@ module RSpec
|
|
48
52
|
module Core
|
49
53
|
module Formatters
|
50
54
|
module ConsoleCodes
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
if
|
57
|
-
|
58
|
-
|
59
|
-
code_or_symbol
|
60
|
-
else
|
61
|
-
RSpec::Core::Formatters::ConsoleCodes::VT100_CODES.fetch(code_or_symbol) do
|
55
|
+
SuperDiff.insert_singleton_overrides(self) do
|
56
|
+
# Patch so it returns nothing if code_or_symbol is nil, and that it uses
|
57
|
+
# code_or_symbol if it can't be found in VT100_CODE_VALUES to allow for
|
58
|
+
# customization
|
59
|
+
def console_code_for(code_or_symbol)
|
60
|
+
if code_or_symbol
|
61
|
+
if (config_method = config_colors_to_methods[code_or_symbol])
|
62
|
+
console_code_for RSpec.configuration.__send__(config_method)
|
63
|
+
elsif RSpec::Core::Formatters::ConsoleCodes::VT100_CODE_VALUES.key?(code_or_symbol)
|
62
64
|
code_or_symbol
|
65
|
+
else
|
66
|
+
RSpec::Core::Formatters::ConsoleCodes::VT100_CODES.fetch(code_or_symbol) do
|
67
|
+
code_or_symbol
|
68
|
+
end
|
63
69
|
end
|
64
70
|
end
|
65
71
|
end
|
66
|
-
end
|
67
72
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
73
|
+
# Patch so it does not apply a color if code_or_symbol is nil
|
74
|
+
def wrap(text, code_or_symbol)
|
75
|
+
if RSpec.configuration.color_enabled? && code = console_code_for(code_or_symbol)
|
76
|
+
"\e[#{code}m#{text}\e[0m"
|
77
|
+
else
|
78
|
+
text
|
79
|
+
end
|
74
80
|
end
|
75
81
|
end
|
76
82
|
end
|
@@ -79,173 +85,177 @@ module RSpec
|
|
79
85
|
# UPDATE: Copy from SyntaxHighlighter::CodeRayImplementation
|
80
86
|
RESET_CODE = "\e[0m"
|
81
87
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
88
|
+
SuperDiff.insert_overrides(self) do
|
89
|
+
def initialize(exception, example, options={})
|
90
|
+
@exception = exception
|
91
|
+
@example = example
|
92
|
+
@message_color = options.fetch(:message_color) { RSpec.configuration.failure_color }
|
93
|
+
@description = options.fetch(:description) { example.full_description }
|
94
|
+
@detail_formatter = options.fetch(:detail_formatter) { Proc.new {} }
|
95
|
+
@extra_detail_formatter = options.fetch(:extra_detail_formatter) { Proc.new {} }
|
96
|
+
@backtrace_formatter = options.fetch(:backtrace_formatter) { RSpec.configuration.backtrace_formatter }
|
97
|
+
@indentation = options.fetch(:indentation, 2)
|
98
|
+
@skip_shared_group_trace = options.fetch(:skip_shared_group_trace, false)
|
99
|
+
# Patch to convert options[:failure_lines] to groups
|
100
|
+
if options.include?(:failure_lines)
|
101
|
+
@failure_line_groups = [
|
102
|
+
{
|
103
|
+
lines: options[:failure_lines],
|
104
|
+
already_colorized: false
|
105
|
+
}
|
106
|
+
]
|
107
|
+
end
|
100
108
|
end
|
101
|
-
end
|
102
109
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
else
|
114
|
-
indentation = line[/^[ ]+/]
|
115
|
-
rest = colorizer.wrap(line.sub(/^[ ]+/, ''), message_color)
|
116
|
-
|
117
|
-
if indentation
|
118
|
-
indentation + rest
|
110
|
+
# Override to only color uncolored lines in red
|
111
|
+
# and to not color empty lines
|
112
|
+
def colorized_message_lines(colorizer = ::RSpec::Core::Formatters::ConsoleCodes)
|
113
|
+
lines = failure_line_groups.flat_map do |group|
|
114
|
+
if group[:already_colorized]
|
115
|
+
group[:lines]
|
116
|
+
else
|
117
|
+
group[:lines].map do |line|
|
118
|
+
if line.strip.empty?
|
119
|
+
line
|
119
120
|
else
|
120
|
-
|
121
|
+
indentation = line[/^[ ]+/]
|
122
|
+
rest = colorizer.wrap(line.sub(/^[ ]+/, ''), message_color)
|
123
|
+
|
124
|
+
if indentation
|
125
|
+
indentation + rest
|
126
|
+
else
|
127
|
+
rest
|
128
|
+
end
|
121
129
|
end
|
122
130
|
end
|
123
131
|
end
|
124
132
|
end
|
125
|
-
end
|
126
|
-
|
127
|
-
add_shared_group_lines(lines, colorizer)
|
128
|
-
end
|
129
|
-
|
130
|
-
private
|
131
133
|
|
132
|
-
|
133
|
-
return lines if @skip_shared_group_trace
|
134
|
-
|
135
|
-
example.metadata[:shared_group_inclusion_backtrace].each do |frame|
|
136
|
-
# Use red instead of the default color
|
137
|
-
lines << colorizer.wrap(frame.description, :failure)
|
134
|
+
add_shared_group_lines(lines, colorizer)
|
138
135
|
end
|
139
136
|
|
140
|
-
|
141
|
-
end
|
137
|
+
private
|
142
138
|
|
143
|
-
|
144
|
-
|
145
|
-
# later
|
146
|
-
#
|
147
|
-
# TODO: Refactor this somehow
|
148
|
-
#
|
149
|
-
def failure_line_groups
|
150
|
-
if defined?(@failure_line_groups)
|
151
|
-
@failure_line_groups
|
152
|
-
else
|
153
|
-
@failure_line_groups = [
|
154
|
-
{
|
155
|
-
lines: failure_slash_error_lines,
|
156
|
-
already_colorized: true
|
157
|
-
}
|
158
|
-
]
|
159
|
-
|
160
|
-
sections = [failure_slash_error_lines, exception_lines]
|
161
|
-
separate_groups = (
|
162
|
-
sections.any? { |section| section.size > 1 } &&
|
163
|
-
!exception_lines.first.empty?
|
164
|
-
)
|
139
|
+
def add_shared_group_lines(lines, colorizer)
|
140
|
+
return lines if @skip_shared_group_trace
|
165
141
|
|
166
|
-
|
167
|
-
|
142
|
+
example.metadata[:shared_group_inclusion_backtrace].each do |frame|
|
143
|
+
# Use red instead of the default color
|
144
|
+
lines << colorizer.wrap(frame.description, :failure)
|
168
145
|
end
|
169
146
|
|
170
|
-
|
171
|
-
|
172
|
-
end
|
147
|
+
lines
|
148
|
+
end
|
173
149
|
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
150
|
+
# Considering that `failure_slash_error_lines` is already colored,
|
151
|
+
# extract this from the other lines so that they, too, can be colored,
|
152
|
+
# later
|
153
|
+
#
|
154
|
+
# TODO: Refactor this somehow
|
155
|
+
#
|
156
|
+
def failure_line_groups
|
157
|
+
if defined?(@failure_line_groups)
|
158
|
+
@failure_line_groups
|
179
159
|
else
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
160
|
+
@failure_line_groups = [
|
161
|
+
{
|
162
|
+
lines: failure_slash_error_lines,
|
163
|
+
already_colorized: true
|
164
|
+
}
|
165
|
+
]
|
184
166
|
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
167
|
+
sections = [failure_slash_error_lines, exception_lines]
|
168
|
+
separate_groups = (
|
169
|
+
sections.any? { |section| section.size > 1 } &&
|
170
|
+
!exception_lines.first.empty?
|
171
|
+
)
|
189
172
|
|
190
|
-
if
|
191
|
-
@failure_line_groups << {
|
192
|
-
|
193
|
-
|
194
|
-
|
173
|
+
if separate_groups
|
174
|
+
@failure_line_groups << { lines: [''], already_colorized: true }
|
175
|
+
end
|
176
|
+
|
177
|
+
already_colorized = exception_lines.any? do |line|
|
178
|
+
SuperDiff::Csi.already_colorized?(line)
|
179
|
+
end
|
180
|
+
|
181
|
+
if already_colorized
|
195
182
|
@failure_line_groups << {
|
196
|
-
lines: exception_lines
|
183
|
+
lines: exception_lines,
|
197
184
|
already_colorized: true
|
198
185
|
}
|
199
186
|
else
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
187
|
+
locatable_exception_lines =
|
188
|
+
exception_lines.each_with_index.map do |line, index|
|
189
|
+
{ text: line, index: index }
|
190
|
+
end
|
191
|
+
|
192
|
+
boundary_line =
|
193
|
+
locatable_exception_lines.find do |line, index|
|
194
|
+
line[:text].strip.empty? || line[:text].match?(/^ /)
|
195
|
+
end
|
196
|
+
|
197
|
+
if boundary_line
|
198
|
+
@failure_line_groups << {
|
199
|
+
lines: exception_lines[0..boundary_line[:index] - 1],
|
200
|
+
already_colorized: false
|
201
|
+
}
|
202
|
+
@failure_line_groups << {
|
203
|
+
lines: exception_lines[boundary_line[:index]..-1],
|
204
|
+
already_colorized: true
|
205
|
+
}
|
206
|
+
else
|
207
|
+
@failure_line_groups << {
|
208
|
+
lines: exception_lines,
|
209
|
+
already_colorized: false
|
210
|
+
}
|
211
|
+
end
|
204
212
|
end
|
205
|
-
end
|
206
213
|
|
207
|
-
|
214
|
+
@failure_line_groups
|
215
|
+
end
|
208
216
|
end
|
209
|
-
end
|
210
217
|
|
211
|
-
|
212
|
-
|
213
|
-
|
218
|
+
# Style the first part in white and don't style the snippet of the line
|
219
|
+
def failure_slash_error_lines
|
220
|
+
lines = read_failed_lines
|
214
221
|
|
215
|
-
|
222
|
+
failure_slash_error = ConsoleCodes.wrap("Failure/Error: ", :bold)
|
216
223
|
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
224
|
+
if lines.count == 1
|
225
|
+
lines[0] = failure_slash_error + lines[0].strip
|
226
|
+
else
|
227
|
+
least_indentation = SnippetExtractor.least_indentation_from(lines)
|
228
|
+
lines = lines.map do |line|
|
229
|
+
line.sub(/^#{least_indentation}/, ' ')
|
230
|
+
end
|
231
|
+
lines.unshift(failure_slash_error)
|
223
232
|
end
|
224
|
-
|
233
|
+
|
234
|
+
lines
|
225
235
|
end
|
226
236
|
|
227
|
-
|
228
|
-
|
237
|
+
# Exclude this file from being included in backtraces, so that the
|
238
|
+
# SnippetExtractor prints the right thing
|
239
|
+
def find_failed_line
|
240
|
+
line_regex = RSpec.configuration.in_project_source_dir_regex
|
241
|
+
loaded_spec_files = RSpec.configuration.loaded_spec_files
|
229
242
|
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
exception_backtrace.find do |line|
|
237
|
-
next unless (line_path = line[/(.+?):(\d+)(|:\d+)/, 1])
|
238
|
-
path = File.expand_path(line_path)
|
239
|
-
path != __FILE__ && (loaded_spec_files.include?(path) || path =~ line_regex)
|
240
|
-
end || exception_backtrace.first
|
243
|
+
exception_backtrace.find do |line|
|
244
|
+
next unless (line_path = line[/(.+?):(\d+)(|:\d+)/, 1])
|
245
|
+
path = File.expand_path(line_path)
|
246
|
+
path != __FILE__ && (loaded_spec_files.include?(path) || path =~ line_regex)
|
247
|
+
end || exception_backtrace.first
|
248
|
+
end
|
241
249
|
end
|
242
250
|
end
|
243
251
|
|
244
252
|
class SyntaxHighlighter
|
245
|
-
|
253
|
+
SuperDiff.insert_overrides(self) do
|
254
|
+
private
|
246
255
|
|
247
|
-
|
248
|
-
|
256
|
+
def implementation
|
257
|
+
RSpec::Core::Formatters::SyntaxHighlighter::NoSyntaxHighlightingImplementation
|
258
|
+
end
|
249
259
|
end
|
250
260
|
end
|
251
261
|
end
|
@@ -253,100 +263,108 @@ module RSpec
|
|
253
263
|
|
254
264
|
module Support
|
255
265
|
class ObjectFormatter
|
256
|
-
|
257
|
-
|
258
|
-
|
266
|
+
SuperDiff.insert_singleton_overrides(self) do
|
267
|
+
# Override to use our formatting algorithm
|
268
|
+
def format(value)
|
269
|
+
SuperDiff.inspect_object(value, as_single_line: true)
|
270
|
+
end
|
259
271
|
end
|
260
272
|
|
261
|
-
|
262
|
-
|
263
|
-
|
273
|
+
SuperDiff.insert_overrides(self) do
|
274
|
+
# Override to use our formatting algorithm
|
275
|
+
def format(value)
|
276
|
+
SuperDiff.inspect_object(value, as_single_line: true)
|
277
|
+
end
|
264
278
|
end
|
265
279
|
end
|
266
280
|
end
|
267
281
|
|
268
282
|
module Matchers
|
269
283
|
class ExpectedsForMultipleDiffs
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
new([[expected, text]])
|
306
|
-
end
|
284
|
+
SuperDiff.insert_singleton_overrides(self) do
|
285
|
+
# Add a key for different sides
|
286
|
+
def from(expected)
|
287
|
+
return expected if self === expected
|
288
|
+
|
289
|
+
text =
|
290
|
+
colorizer.wrap("Diff:", SuperDiff.configuration.header_color) +
|
291
|
+
"\n\n" +
|
292
|
+
colorizer.wrap(
|
293
|
+
"┌ (Key) ──────────────────────────┐",
|
294
|
+
SuperDiff.configuration.border_color
|
295
|
+
) +
|
296
|
+
"\n" +
|
297
|
+
colorizer.wrap("│ ", SuperDiff.configuration.border_color) +
|
298
|
+
colorizer.wrap(
|
299
|
+
"‹-› in expected, not in actual",
|
300
|
+
SuperDiff.configuration.expected_color
|
301
|
+
) +
|
302
|
+
colorizer.wrap(" │", SuperDiff.configuration.border_color) +
|
303
|
+
"\n" +
|
304
|
+
colorizer.wrap("│ ", SuperDiff.configuration.border_color) +
|
305
|
+
colorizer.wrap(
|
306
|
+
"‹+› in actual, not in expected",
|
307
|
+
SuperDiff.configuration.actual_color
|
308
|
+
) +
|
309
|
+
colorizer.wrap(" │", SuperDiff.configuration.border_color) +
|
310
|
+
"\n" +
|
311
|
+
colorizer.wrap("│ ", SuperDiff.configuration.border_color) +
|
312
|
+
"‹ › in both expected and actual" +
|
313
|
+
colorizer.wrap(" │", SuperDiff.configuration.border_color) +
|
314
|
+
"\n" +
|
315
|
+
colorizer.wrap(
|
316
|
+
"└─────────────────────────────────┘",
|
317
|
+
SuperDiff.configuration.border_color
|
318
|
+
)
|
307
319
|
|
308
|
-
|
309
|
-
|
320
|
+
new([[expected, text]])
|
321
|
+
end
|
322
|
+
|
323
|
+
def colorizer
|
324
|
+
RSpec::Core::Formatters::ConsoleCodes
|
325
|
+
end
|
310
326
|
end
|
311
327
|
|
312
|
-
|
313
|
-
|
314
|
-
|
328
|
+
SuperDiff.insert_overrides(self) do
|
329
|
+
# Add an extra line break
|
330
|
+
def message_with_diff(message, differ, actual)
|
331
|
+
diff = diffs(differ, actual)
|
315
332
|
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
333
|
+
if diff.empty?
|
334
|
+
message
|
335
|
+
else
|
336
|
+
"#{message.rstrip}\n\n#{diff}"
|
337
|
+
end
|
320
338
|
end
|
321
|
-
end
|
322
339
|
|
323
|
-
|
340
|
+
private
|
324
341
|
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
342
|
+
# Add extra line breaks in between diffs, and colorize the word "Diff"
|
343
|
+
def diffs(differ, actual)
|
344
|
+
@expected_list.map do |(expected, diff_label)|
|
345
|
+
diff = differ.diff(actual, expected)
|
346
|
+
next if diff.strip.empty?
|
347
|
+
diff_label + diff
|
348
|
+
end.compact.join("\n\n")
|
349
|
+
end
|
332
350
|
end
|
333
351
|
end
|
334
352
|
|
335
353
|
module BuiltIn
|
336
354
|
class Be
|
337
|
-
|
355
|
+
SuperDiff.insert_overrides(self, SuperDiff::RSpec::AugmentedMatcher)
|
338
356
|
|
339
|
-
|
357
|
+
SuperDiff.insert_overrides(self) do
|
340
358
|
def expected_for_matcher_text
|
341
359
|
"truthy"
|
342
360
|
end
|
343
|
-
end
|
361
|
+
end
|
344
362
|
end
|
345
363
|
|
346
364
|
class BeComparedTo
|
347
|
-
|
365
|
+
SuperDiff.insert_overrides(self, SuperDiff::RSpec::AugmentedMatcher)
|
348
366
|
|
349
|
-
|
367
|
+
SuperDiff.insert_overrides(self) do
|
350
368
|
def expected_action_for_matcher_text
|
351
369
|
if [:==, :===, :=~].include?(@operator)
|
352
370
|
"#{@operator}"
|
@@ -354,13 +372,13 @@ module RSpec
|
|
354
372
|
"be #{@operator}"
|
355
373
|
end
|
356
374
|
end
|
357
|
-
end
|
375
|
+
end
|
358
376
|
end
|
359
377
|
|
360
378
|
class BeFalsey
|
361
|
-
|
379
|
+
SuperDiff.insert_overrides(self, SuperDiff::RSpec::AugmentedMatcher)
|
362
380
|
|
363
|
-
|
381
|
+
SuperDiff.insert_overrides(self) do
|
364
382
|
def expected_action_for_matcher_text
|
365
383
|
"be"
|
366
384
|
end
|
@@ -368,13 +386,13 @@ module RSpec
|
|
368
386
|
def expected_for_matcher_text
|
369
387
|
"falsey"
|
370
388
|
end
|
371
|
-
end
|
389
|
+
end
|
372
390
|
end
|
373
391
|
|
374
392
|
class BeNil
|
375
|
-
|
393
|
+
SuperDiff.insert_overrides(self, SuperDiff::RSpec::AugmentedMatcher)
|
376
394
|
|
377
|
-
|
395
|
+
SuperDiff.insert_overrides(self) do
|
378
396
|
def expected_action_for_matcher_text
|
379
397
|
"be"
|
380
398
|
end
|
@@ -382,13 +400,13 @@ module RSpec
|
|
382
400
|
def expected_for_matcher_text
|
383
401
|
"nil"
|
384
402
|
end
|
385
|
-
end
|
403
|
+
end
|
386
404
|
end
|
387
405
|
|
388
406
|
class BePredicate
|
389
|
-
|
407
|
+
SuperDiff.insert_overrides(self, SuperDiff::RSpec::AugmentedMatcher)
|
390
408
|
|
391
|
-
|
409
|
+
SuperDiff.insert_overrides(self) do
|
392
410
|
def actual_for_matcher_text
|
393
411
|
actual
|
394
412
|
end
|
@@ -416,13 +434,13 @@ module RSpec
|
|
416
434
|
expected_predicate_method_name: predicate
|
417
435
|
)
|
418
436
|
end
|
419
|
-
end
|
437
|
+
end
|
420
438
|
end
|
421
439
|
|
422
440
|
class BeTruthy
|
423
|
-
|
441
|
+
SuperDiff.insert_overrides(self, SuperDiff::RSpec::AugmentedMatcher)
|
424
442
|
|
425
|
-
|
443
|
+
SuperDiff.insert_overrides(self) do
|
426
444
|
def expected_action_for_matcher_text
|
427
445
|
"be"
|
428
446
|
end
|
@@ -430,13 +448,13 @@ module RSpec
|
|
430
448
|
def expected_for_matcher_text
|
431
449
|
"truthy"
|
432
450
|
end
|
433
|
-
end
|
451
|
+
end
|
434
452
|
end
|
435
453
|
|
436
454
|
class ContainExactly
|
437
|
-
|
455
|
+
SuperDiff.insert_overrides(self, SuperDiff::RSpec::AugmentedMatcher)
|
438
456
|
|
439
|
-
|
457
|
+
SuperDiff.insert_overrides(self) do
|
440
458
|
# Override this method so that the differ knows that this is a partial
|
441
459
|
# collection
|
442
460
|
def expected_for_diff
|
@@ -452,21 +470,26 @@ module RSpec
|
|
452
470
|
def matcher_text_builder_class
|
453
471
|
SuperDiff::RSpec::MatcherTextBuilders::ContainExactly
|
454
472
|
end
|
455
|
-
end
|
473
|
+
end
|
456
474
|
end
|
457
475
|
|
458
476
|
class Eq
|
459
|
-
|
477
|
+
SuperDiff.insert_overrides(self, SuperDiff::RSpec::AugmentedMatcher)
|
460
478
|
end
|
461
479
|
|
462
480
|
class Equal
|
463
|
-
|
481
|
+
SuperDiff.insert_overrides(self, SuperDiff::RSpec::AugmentedMatcher)
|
464
482
|
end
|
465
483
|
|
466
484
|
class HaveAttributes
|
467
|
-
|
485
|
+
SuperDiff.insert_overrides(self, SuperDiff::RSpec::AugmentedMatcher)
|
486
|
+
|
487
|
+
SuperDiff.insert_overrides(self) do
|
488
|
+
def initialize(*)
|
489
|
+
super
|
490
|
+
@actual = nil
|
491
|
+
end
|
468
492
|
|
469
|
-
prepend(Module.new do
|
470
493
|
# Use the message in the base matcher
|
471
494
|
def failure_message
|
472
495
|
respond_to_failure_message_or { super }
|
@@ -502,48 +525,48 @@ module RSpec
|
|
502
525
|
matchers.an_object_having_attributes(@expected)
|
503
526
|
end
|
504
527
|
end
|
505
|
-
end)
|
506
528
|
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
529
|
+
# Override to force @values to get populated so that we can show a
|
530
|
+
# proper diff
|
531
|
+
def respond_to_attributes?
|
532
|
+
cache_all_values
|
533
|
+
matches = respond_to_matcher.matches?(@actual)
|
534
|
+
@respond_to_failed = !matches
|
535
|
+
matches
|
536
|
+
end
|
515
537
|
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
538
|
+
# Override this method to skip non-existent attributes, and to use
|
539
|
+
# public_send
|
540
|
+
def cache_all_values
|
541
|
+
@values = @expected.keys.inject({}) do |hash, attribute_key|
|
542
|
+
if @actual.respond_to?(attribute_key)
|
543
|
+
actual_value = @actual.public_send(attribute_key)
|
544
|
+
hash.merge(attribute_key => actual_value)
|
545
|
+
else
|
546
|
+
hash
|
547
|
+
end
|
525
548
|
end
|
526
549
|
end
|
527
|
-
end
|
528
550
|
|
529
|
-
|
530
|
-
|
531
|
-
|
551
|
+
def actual_has_attribute?(attribute_key, attribute_value)
|
552
|
+
values_match?(attribute_value, @values.fetch(attribute_key))
|
553
|
+
end
|
532
554
|
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
555
|
+
# Override to not improve_hash_formatting
|
556
|
+
def respond_to_failure_message_or
|
557
|
+
if respond_to_failed
|
558
|
+
respond_to_matcher.failure_message
|
559
|
+
else
|
560
|
+
yield
|
561
|
+
end
|
539
562
|
end
|
540
563
|
end
|
541
564
|
end
|
542
565
|
|
543
566
|
class Has
|
544
|
-
|
567
|
+
SuperDiff.insert_overrides(self, SuperDiff::RSpec::AugmentedMatcher)
|
545
568
|
|
546
|
-
|
569
|
+
SuperDiff.insert_overrides(self) do
|
547
570
|
def actual_for_matcher_text
|
548
571
|
actual
|
549
572
|
end
|
@@ -570,13 +593,18 @@ module RSpec
|
|
570
593
|
private_predicate: private_predicate?
|
571
594
|
)
|
572
595
|
end
|
573
|
-
end
|
596
|
+
end
|
574
597
|
end
|
575
598
|
|
576
599
|
class Include
|
577
|
-
|
600
|
+
SuperDiff.insert_overrides(self, SuperDiff::RSpec::AugmentedMatcher)
|
601
|
+
|
602
|
+
SuperDiff.insert_overrides(self) do
|
603
|
+
def initialize(*)
|
604
|
+
super
|
605
|
+
@actual = nil
|
606
|
+
end
|
578
607
|
|
579
|
-
prepend(Module.new do
|
580
608
|
# Override this method so that the differ knows that this is a partial
|
581
609
|
# array or hash
|
582
610
|
def expected_for_diff
|
@@ -611,7 +639,11 @@ module RSpec
|
|
611
639
|
def expected_for_failure_message
|
612
640
|
# TODO: Switch to using @divergent_items and handle this in the text
|
613
641
|
# builder
|
614
|
-
|
642
|
+
if defined?(@divergent_items)
|
643
|
+
readable_list_of(@divergent_items).lstrip
|
644
|
+
else
|
645
|
+
""
|
646
|
+
end
|
615
647
|
end
|
616
648
|
|
617
649
|
# Update to use (...) as delimiter instead of {...}
|
@@ -624,13 +656,13 @@ module RSpec
|
|
624
656
|
super
|
625
657
|
end
|
626
658
|
end
|
627
|
-
end
|
659
|
+
end
|
628
660
|
end
|
629
661
|
|
630
662
|
class Match
|
631
|
-
|
663
|
+
SuperDiff.insert_overrides(self, SuperDiff::RSpec::AugmentedMatcher)
|
632
664
|
|
633
|
-
|
665
|
+
SuperDiff.insert_overrides(self) do
|
634
666
|
def matcher_text_builder_class
|
635
667
|
SuperDiff::RSpec::MatcherTextBuilders::Match
|
636
668
|
end
|
@@ -638,7 +670,7 @@ module RSpec
|
|
638
670
|
def matcher_text_builder_args
|
639
671
|
super.merge(expected_captures: @expected_captures)
|
640
672
|
end
|
641
|
-
end
|
673
|
+
end
|
642
674
|
end
|
643
675
|
|
644
676
|
class MatchArray < ContainExactly
|
@@ -652,9 +684,9 @@ module RSpec
|
|
652
684
|
end
|
653
685
|
|
654
686
|
class RaiseError
|
655
|
-
|
687
|
+
SuperDiff.insert_overrides(self, SuperDiff::RSpec::AugmentedMatcher)
|
656
688
|
|
657
|
-
|
689
|
+
SuperDiff.insert_overrides(self) do
|
658
690
|
def actual_for_matcher_text
|
659
691
|
if @actual_error
|
660
692
|
"#<#{@actual_error.class.name} #{@actual_error.message.inspect}>"
|
@@ -696,7 +728,7 @@ module RSpec
|
|
696
728
|
def matcher_text_builder_class
|
697
729
|
SuperDiff::RSpec::MatcherTextBuilders::RaiseError
|
698
730
|
end
|
699
|
-
end
|
731
|
+
end
|
700
732
|
|
701
733
|
def self.matcher_name
|
702
734
|
"raise error"
|
@@ -704,9 +736,9 @@ module RSpec
|
|
704
736
|
end
|
705
737
|
|
706
738
|
class RespondTo
|
707
|
-
|
739
|
+
SuperDiff.insert_overrides(self, SuperDiff::RSpec::AugmentedMatcher)
|
708
740
|
|
709
|
-
|
741
|
+
SuperDiff.insert_overrides(self) do
|
710
742
|
def initialize(*)
|
711
743
|
super
|
712
744
|
@failing_method_names = nil
|
@@ -732,13 +764,20 @@ module RSpec
|
|
732
764
|
def expected_for_failure_message
|
733
765
|
@failing_method_names
|
734
766
|
end
|
735
|
-
end
|
767
|
+
end
|
736
768
|
end
|
737
769
|
end
|
738
770
|
|
739
|
-
|
740
|
-
|
771
|
+
SuperDiff.insert_overrides(self) do
|
772
|
+
def self.prepended(base)
|
773
|
+
base.class_eval do
|
774
|
+
alias_matcher :an_array_matching, :match_array
|
775
|
+
end
|
776
|
+
end
|
777
|
+
|
778
|
+
def match_array(items)
|
779
|
+
BuiltIn::MatchArray.new(items.is_a?(String) ? [items] : items)
|
780
|
+
end
|
741
781
|
end
|
742
|
-
alias_matcher :an_array_matching, :match_array
|
743
782
|
end
|
744
783
|
end
|