@@ -20,23 +20,34 @@ def report(error, **attrs)
20
20
21
21
# Matcher class for `have_reported_error`. Should not be instantiated directly.
22
22
#
23
+ # Provides a way to test that an error was reported to Rails.error.
24
+ # This matcher follows the same patterns as RSpec's built-in `raise_error` matcher.
25
+ #
23
26
# @api private
24
27
# @see RSpec::Rails::Matchers#have_reported_error
25
28
class HaveReportedError < RSpec ::Rails ::Matchers ::BaseMatcher
29
+ # Initialize the matcher following raise_error patterns
30
+ #
31
+ # @param expected_error_or_message [Class, String, Regexp, nil]
32
+ # Error class, message string, or message pattern
33
+ # @param expected_message [String, Regexp, nil]
34
+ # Expected message when first param is a class
26
35
def initialize ( expected_error_or_message = nil , expected_message = nil )
27
- if expected_error_or_message . is_a? ( Regexp )
28
- @expected_error_class = nil
29
- @expected_message = expected_error_or_message
30
- elsif expected_error_or_message . is_a? ( String )
31
- @expected_error_class = nil
36
+ @actual_error = nil
37
+ @attributes = { }
38
+ @error_subscriber = nil
39
+
40
+ case expected_error_or_message
41
+ when nil
42
+ @expected_error = nil
43
+ @expected_message = expected_message
44
+ when String , Regexp
45
+ @expected_error = nil
32
46
@expected_message = expected_error_or_message
33
47
else
34
- @expected_error_class = expected_error_or_message
48
+ @expected_error = expected_error_or_message
35
49
@expected_message = expected_message
36
50
end
37
-
38
- @attributes = { }
39
- @error_subscriber = nil
40
51
end
41
52
42
53
def with_context ( expected_attributes )
@@ -48,9 +59,10 @@ def and(_)
48
59
raise ArgumentError , "Chaining is not supported"
49
60
end
50
61
62
+ # Check if the block reports an error matching our expectations
51
63
def matches? ( block )
52
64
if block . nil?
53
- raise ArgumentError , "this matcher doesn’ t work with value expectations"
65
+ raise ArgumentError , "this matcher doesn' t work with value expectations"
54
66
end
55
67
56
68
@error_subscriber = ErrorSubscriber . new
@@ -71,8 +83,8 @@ def supports_block_expectations?
71
83
end
72
84
73
85
def description
74
- base_desc = if @expected_error_class
75
- "report a #{ @expected_error_class } error"
86
+ base_desc = if @expected_error
87
+ "report a #{ @expected_error } error"
76
88
else
77
89
"report an error"
78
90
end
@@ -103,8 +115,8 @@ def failure_message
103
115
elsif @error_subscriber . events . empty?
104
116
return 'Expected the block to report an error, but none was reported.'
105
117
else
106
- if @expected_error_class && !actual_error . is_a? ( @expected_error_class )
107
- return "Expected error to be an instance of #{ @expected_error_class } , but got #{ actual_error . class } with message: '#{ actual_error . message } '"
118
+ if @expected_error && !actual_error . is_a? ( @expected_error )
119
+ return "Expected error to be an instance of #{ @expected_error } , but got #{ actual_error . class } with message: '#{ actual_error . message } '"
108
120
elsif @expected_message
109
121
case @expected_message
110
122
when Regexp
@@ -128,26 +140,31 @@ def failure_message_when_negated
128
140
129
141
private
130
142
143
+ # Check if the reported error matches our class and message expectations
131
144
def error_matches_expectation?
132
- # If no events were reported, we can't match anything
133
145
return false if @error_subscriber . events . empty?
134
-
135
- # If no constraints are given, any error should match
136
- return true if @expected_error_class . nil? && @expected_message . nil?
146
+ return true if @expected_error . nil? && @expected_message . nil?
147
+
148
+ error_class_matches? && error_message_matches?
149
+ end
137
150
138
- class_matches = @expected_error_class . nil? || actual_error . is_a? ( @expected_error_class )
151
+ # Check if the actual error class matches the expected error class
152
+ def error_class_matches?
153
+ @expected_error . nil? || actual_error . is_a? ( @expected_error )
154
+ end
155
+
156
+ # Check if the actual error message matches the expected message pattern
157
+ def error_message_matches?
158
+ return true if @expected_message . nil?
139
159
140
- message_matches = if @expected_message . nil?
141
- true
142
- elsif @expected_message . is_a? ( Regexp )
160
+ case @expected_message
161
+ when Regexp
143
162
actual_error . message &.match ( @expected_message )
144
- elsif @expected_message . is_a? ( String )
163
+ when String
145
164
actual_error . message == @expected_message
146
165
else
147
166
false
148
167
end
149
-
150
- class_matches && message_matches
151
168
end
152
169
153
170
def attributes_match_if_specified?
@@ -158,8 +175,9 @@ def attributes_match_if_specified?
158
175
attributes_match? ( event_context )
159
176
end
160
177
178
+ # Get the actual error that was reported (cached)
161
179
def actual_error
162
- @error_subscriber . events . empty? ? nil : @error_subscriber . events . last . error
180
+ @actual_error ||= ( @ error_subscriber. events . empty? ? nil : @error_subscriber . events . last . error )
163
181
end
164
182
165
183
def attributes_match? ( actual )
0 commit comments