1
+ require 'active_support/testing/error_reporter_assertions'
2
+
1
3
module RSpec
2
4
module Rails
3
5
module Matchers
4
6
# @api private
5
7
# Sentinel value to distinguish between no argument passed vs explicitly passed nil.
6
8
# This follows the same pattern as RSpec's raise_error matcher.
7
9
UndefinedValue = Object . new . freeze
8
-
9
- # @api private
10
- class ErrorSubscriber
11
- attr_reader :events
12
-
13
- ErrorEvent = Struct . new ( :error , :attributes )
14
-
15
- def initialize
16
- @events = [ ]
17
- end
18
-
19
- def report ( error , **attrs )
20
- @events << ErrorEvent . new ( error , attrs . with_indifferent_access )
21
- end
22
- end
10
+ ErrorCollector = ActiveSupport ::Testing ::ErrorReporterAssertions ::ErrorCollector
23
11
24
12
# Matcher class for `have_reported_error`. Should not be instantiated directly.
25
13
#
@@ -68,17 +56,14 @@ def matches?(block)
68
56
69
57
warn_about_nil_error! if @warn_about_nil_error
70
58
71
- @error_subscriber = ErrorSubscriber . new
72
- ::Rails . error . subscribe ( @error_subscriber )
73
-
74
- block . call
59
+ @reports = ErrorCollector . record do
60
+ block . call
61
+ end
75
62
76
- return false if @error_subscriber . events . empty?
63
+ return false if @reports . empty?
77
64
return false unless error_matches_expectation?
78
65
79
66
return attributes_match_if_specified?
80
- ensure
81
- ::Rails . error . unsubscribe ( @error_subscriber )
82
67
end
83
68
84
69
def supports_block_expectations?
@@ -109,16 +94,16 @@ def description
109
94
end
110
95
111
96
def failure_message
112
- if !@error_subscriber . events . empty? && !@attributes . empty?
113
- event_context = @error_subscriber . events . last . attributes [ : context]
114
- unmatched = unmatched_attributes ( event_context )
97
+ if !@reports . empty? && !@attributes . empty?
98
+ report_context = @reports . last . context
99
+ unmatched = unmatched_attributes ( report_context )
115
100
unless unmatched . empty?
116
- return "Expected error attributes to match #{ @attributes } , but got these mismatches: #{ unmatched } and actual values are #{ event_context } "
101
+ return "Expected error attributes to match #{ @attributes } , but got these mismatches: #{ unmatched } and actual values are #{ report_context } "
117
102
end
118
- elsif @error_subscriber . events . empty?
103
+ elsif @reports . empty?
119
104
return 'Expected the block to report an error, but none was reported.'
120
105
elsif actual_error . nil?
121
- reported_errors = @error_subscriber . events . map { |event | "#{ event . error . class } : '#{ event . error . message } '" } . join ( ', ' )
106
+ reported_errors = @reports . map { |report | "#{ report . error . class } : '#{ report . error . message } '" } . join ( ', ' )
122
107
if @expected_error && @expected_message
123
108
return "Expected error to be an instance of #{ @expected_error } with message '#{ @expected_message } ', but got: #{ reported_errors } "
124
109
elsif @expected_error
@@ -140,7 +125,7 @@ def failure_message
140
125
end
141
126
142
127
def failure_message_when_negated
143
- error_count = @error_subscriber . events . count
128
+ error_count = @reports . count
144
129
error_word = 'error' . pluralize ( error_count )
145
130
verb = error_count == 1 ? 'has' : 'have'
146
131
@@ -150,10 +135,10 @@ def failure_message_when_negated
150
135
private
151
136
152
137
def error_matches_expectation?
153
- return true if @expected_error . nil? && @expected_message . nil? && @error_subscriber . events . count . positive?
138
+ return true if @expected_error . nil? && @expected_message . nil? && @reports . count . positive?
154
139
155
- @error_subscriber . events . any? do |event |
156
- error_class_matches? ( event . error ) && error_message_matches? ( event . error )
140
+ @reports . any? do |report |
141
+ error_class_matches? ( report . error ) && error_message_matches? ( report . error )
157
142
end
158
143
end
159
144
@@ -177,42 +162,44 @@ def error_message_matches?(error)
177
162
178
163
def attributes_match_if_specified?
179
164
return true if @attributes . empty?
180
- return false unless matching_event
165
+ return false unless matching_report
181
166
182
- event_context = matching_event . attributes [ : context]
183
- attributes_match? ( event_context )
167
+ report_context = matching_report . context
168
+ attributes_match? ( report_context )
184
169
end
185
170
186
171
def actual_error
187
- @actual_error ||= matching_event &.error
172
+ @actual_error ||= matching_report &.error
188
173
end
189
174
190
- def matching_event
191
- @matching_event ||= find_matching_event
175
+ def matching_report
176
+ @matching_report ||= find_matching_report
192
177
end
193
178
194
- def find_matching_event
195
- @error_subscriber . events . find do |event |
196
- error_class_matches? ( event . error ) && error_message_matches? ( event . error )
179
+ def find_matching_report
180
+ @reports . find do |report |
181
+ error_class_matches? ( report . error ) && error_message_matches? ( report . error )
197
182
end
198
183
end
199
184
200
185
def attributes_match? ( actual )
201
186
@attributes . all? do |key , value |
187
+ actual_value = actual [ key ] || actual [ key . to_s ] || actual [ key . to_sym ]
202
188
if value . respond_to? ( :matches? )
203
- value . matches? ( actual [ key ] )
189
+ value . matches? ( actual_value )
204
190
else
205
- actual [ key ] == value
191
+ actual_value == value
206
192
end
207
193
end
208
194
end
209
195
210
196
def unmatched_attributes ( actual )
211
197
@attributes . reject do |key , value |
198
+ actual_value = actual [ key ] || actual [ key . to_s ] || actual [ key . to_sym ]
212
199
if value . respond_to? ( :matches? )
213
- value . matches? ( actual [ key ] )
200
+ value . matches? ( actual_value )
214
201
else
215
- actual [ key ] == value
202
+ actual_value == value
216
203
end
217
204
end
218
205
end
0 commit comments