1
- require "rspec/rails/matchers/base_matcher"
2
-
3
1
module RSpec
4
2
module Rails
5
3
module Matchers
@@ -26,25 +24,20 @@ def report(error, **attrs)
26
24
# Matcher class for `have_reported_error`. Should not be instantiated directly.
27
25
#
28
26
# Provides a way to test that an error was reported to Rails.error.
29
- # This matcher follows the same patterns as RSpec's built-in `raise_error` matcher.
30
27
#
31
28
# @api private
32
29
# @see RSpec::Rails::Matchers#have_reported_error
33
30
class HaveReportedError < RSpec ::Rails ::Matchers ::BaseMatcher
34
- # Initialize the matcher following raise_error patterns
35
- #
36
- # Uses UndefinedValue as default to distinguish between no argument
37
- # passed vs explicitly passed nil (same as raise_error matcher).
31
+ # Uses UndefinedValue as default to distinguish between no argument
32
+ # passed vs explicitly passed nil.
38
33
#
39
- # @param expected_error_or_message [Class, String, Regexp, nil]
34
+ # @param expected_error_or_message [Class, String, Regexp, nil]
40
35
# Error class, message string, or message pattern
41
- # @param expected_message [String, Regexp, nil]
36
+ # @param expected_message [String, Regexp, nil]
42
37
# Expected message when first param is a class
43
38
def initialize ( expected_error_or_message = UndefinedValue , expected_message = nil )
44
- @actual_error = nil
45
39
@attributes = { }
46
- @error_subscriber = nil
47
-
40
+
48
41
case expected_error_or_message
49
42
when nil , UndefinedValue
50
43
@expected_error = nil
@@ -67,7 +60,6 @@ def and(_)
67
60
raise ArgumentError , "Chaining is not supported"
68
61
end
69
62
70
- # Check if the block reports an error matching our expectations
71
63
def matches? ( block )
72
64
if block . nil?
73
65
raise ArgumentError , "this matcher doesn't work with value expectations"
@@ -122,16 +114,22 @@ def failure_message
122
114
end
123
115
elsif @error_subscriber . events . empty?
124
116
return 'Expected the block to report an error, but none was reported.'
117
+ elsif actual_error . nil?
118
+ reported_errors = @error_subscriber . events . map { |event | "#{ event . error . class } : '#{ event . error . message } '" } . join ( ', ' )
119
+ if @expected_error && @expected_message
120
+ return "Expected error to be an instance of #{ @expected_error } with message '#{ @expected_message } ', but got: #{ reported_errors } "
121
+ elsif @expected_error
122
+ return "Expected error to be an instance of #{ @expected_error } , but got: #{ reported_errors } "
123
+ elsif @expected_message . is_a? ( Regexp )
124
+ return "Expected error message to match #{ @expected_message } , but got: #{ reported_errors } "
125
+ elsif @expected_message . is_a? ( String )
126
+ return "Expected error message to be '#{ @expected_message } ', but got: #{ reported_errors } "
127
+ end
125
128
else
126
129
if @expected_error && !actual_error . is_a? ( @expected_error )
127
130
return "Expected error to be an instance of #{ @expected_error } , but got #{ actual_error . class } with message: '#{ actual_error . message } '"
128
131
elsif @expected_message
129
- case @expected_message
130
- when Regexp
131
- return "Expected error message to match #{ @expected_message } , but got: '#{ actual_error . message } '"
132
- when String
133
- return "Expected error message to be '#{ @expected_message } ', but got: '#{ actual_error . message } '"
134
- end
132
+ return "Expected error message to #{ @expected_message . is_a? ( Regexp ) ? "match" : "be" } #{ @expected_message } , but got: '#{ actual_error . message } '"
135
133
else
136
134
return "Expected specific error, but got #{ actual_error . class } with message: '#{ actual_error . message } '"
137
135
end
@@ -148,44 +146,52 @@ def failure_message_when_negated
148
146
149
147
private
150
148
151
- # Check if the reported error matches our class and message expectations
152
149
def error_matches_expectation?
153
- return false if @error_subscriber . events . empty?
154
- return true if @expected_error . nil? && @expected_message . nil?
150
+ return true if @expected_error . nil? && @expected_message . nil? && @error_subscriber . events . count . positive?
155
151
156
- error_class_matches? && error_message_matches?
152
+ @error_subscriber . events . any? do |event |
153
+ error_class_matches? ( event . error ) && error_message_matches? ( event . error )
154
+ end
157
155
end
158
156
159
- # Check if the actual error class matches the expected error class
160
- def error_class_matches?
161
- @expected_error . nil? || actual_error . is_a? ( @expected_error )
157
+ def error_class_matches? ( error )
158
+ @expected_error . nil? || error . is_a? ( @expected_error )
162
159
end
163
160
164
- # Check if the actual error message matches the expected message pattern
165
- def error_message_matches?
161
+ # Check if the given error message matches the expected message pattern
162
+ def error_message_matches? ( error )
166
163
return true if @expected_message . nil?
167
-
164
+
168
165
case @expected_message
169
166
when Regexp
170
- actual_error . message &.match ( @expected_message )
167
+ error . message &.match ( @expected_message )
171
168
when String
172
- actual_error . message == @expected_message
169
+ error . message == @expected_message
173
170
else
174
171
false
175
172
end
176
173
end
177
174
178
175
def attributes_match_if_specified?
179
176
return true if @attributes . empty?
180
- return false if @error_subscriber . events . empty?
177
+ return false unless matching_event
181
178
182
- event_context = @error_subscriber . events . last . attributes [ :context ]
179
+ event_context = matching_event . attributes [ :context ]
183
180
attributes_match? ( event_context )
184
181
end
185
182
186
- # Get the actual error that was reported (cached)
187
183
def actual_error
188
- @actual_error ||= ( @error_subscriber . events . empty? ? nil : @error_subscriber . events . last . error )
184
+ @actual_error ||= matching_event &.error
185
+ end
186
+
187
+ def matching_event
188
+ @matching_event ||= find_matching_event
189
+ end
190
+
191
+ def find_matching_event
192
+ @error_subscriber . events . find do |event |
193
+ error_class_matches? ( event . error ) && error_message_matches? ( event . error )
194
+ end
189
195
end
190
196
191
197
def attributes_match? ( actual )
0 commit comments