@@ -104,23 +104,39 @@ export class RequestMatcher {
104
104
this . #query = query ;
105
105
this . #params = params ;
106
106
}
107
-
107
+
108
108
/**
109
- * Checks if the request matches the matcher.
109
+ * Checks if the request matches the matcher. Traces all of the details to help
110
+ * with debugging.
110
111
* @param {RequestPattern } request The request to check.
111
- * @returns {boolean } True if the request matches, false if not.
112
+ * @returns {{matches: boolean, messages:string[]} } True if the request matches, false if not.
112
113
*/
113
- matches ( request ) {
114
- // first check the method
115
- if ( request . method . toLowerCase ( ) !== this . #method . toLowerCase ( ) ) {
116
- return false ;
117
- }
118
-
114
+ traceMatches ( request ) {
115
+
116
+ /*
117
+ * Check the URL first. This is helpful for tracing when requests don't match
118
+ * because people more typically get the method wrong rather than the URL.
119
+ */
119
120
// then check the URL
120
121
const urlMatch = this . #pattern. exec ( request . url ) ;
121
122
if ( ! urlMatch ) {
122
- return false ;
123
+ return {
124
+ matches : false ,
125
+ messages : [ "❌ URL does not match." ] ,
126
+ } ;
127
+ }
128
+
129
+ const messages = [ "✅ URL matches." ] ;
130
+
131
+ // first check the method
132
+ if ( request . method . toLowerCase ( ) !== this . #method. toLowerCase ( ) ) {
133
+ return {
134
+ matches : false ,
135
+ messages : [ ...messages , `❌ Method does not match. Expected ${ this . #method. toUpperCase ( ) } but received ${ request . method . toUpperCase ( ) } .` ] ,
136
+ } ;
123
137
}
138
+
139
+ messages . push ( `✅ Method matches: ${ this . #method. toUpperCase ( ) } .` ) ;
124
140
125
141
// then check query string
126
142
const expectedQuery = this . #query;
@@ -129,12 +145,18 @@ export class RequestMatcher {
129
145
const actualQuery = request . query ;
130
146
131
147
if ( ! actualQuery ) {
132
- return false ;
148
+ return {
149
+ matches : false ,
150
+ messages : [ ...messages , "❌ Query string does not match. Expected query string but received none." ] ,
151
+ } ;
133
152
}
134
153
135
154
for ( const [ key , value ] of Object . entries ( expectedQuery ) ) {
136
155
if ( actualQuery [ key ] !== value ) {
137
- return false ;
156
+ return {
157
+ matches : false ,
158
+ messages : [ ...messages , `❌ Query string does not match. Expected ${ key } =${ value } but received ${ key } =${ actualQuery [ key ] } .` ] ,
159
+ } ;
138
160
}
139
161
}
140
162
}
@@ -146,14 +168,22 @@ export class RequestMatcher {
146
168
const actualParams = urlMatch . pathname . groups ;
147
169
148
170
if ( ! actualParams ) {
149
- return false ;
171
+ return {
172
+ matches : false ,
173
+ messages : [ ...messages , "❌ URL parameters do not match. Expected parameters but received none." ] ,
174
+ } ;
150
175
}
151
176
152
177
for ( const [ key , value ] of Object . entries ( expectedParams ) ) {
153
178
if ( actualParams [ key ] !== value ) {
154
- return false ;
179
+ return {
180
+ matches : false ,
181
+ messages : [ ...messages , `❌ URL parameters do not match. Expected ${ key } =${ value } but received ${ key } =${ actualParams [ key ] } .` ] ,
182
+ } ;
155
183
}
156
184
}
185
+
186
+ messages . push ( "✅ URL parameters match." ) ;
157
187
}
158
188
159
189
// then check the headers in a case-insensitive manner
@@ -170,45 +200,86 @@ export class RequestMatcher {
170
200
( [ actualKey ] ) => actualKey === key ,
171
201
) ;
172
202
if ( ! actualValue || actualValue [ 1 ] !== value ) {
173
- return false ;
203
+ return {
204
+ matches : false ,
205
+ messages : [ ...messages , `❌ Headers do not match. Expected ${ key } =${ value } but received ${ key } =${ actualValue ? actualValue [ 1 ] : "none" } .` ] ,
206
+ } ;
174
207
}
175
208
}
209
+
210
+ messages . push ( "✅ Headers match." ) ;
176
211
}
177
212
178
213
// then check the body
179
214
if ( this . #body !== undefined && this . #body !== null ) {
180
215
// if there's no body on the actual request then it can't match
181
216
if ( request . body === null || request . body === undefined ) {
182
- return false ;
217
+ return {
218
+ matches : false ,
219
+ messages : [ ...messages , "❌ Body does not match. Expected body but received none." ] ,
220
+ } ;
183
221
}
184
222
185
223
if ( typeof this . #body === "string" ) {
186
224
if ( this . #body !== request . body ) {
187
- return false ;
225
+ return {
226
+ matches : false ,
227
+ messages : [ ...messages , `❌ Body does not match. Expected ${ this . #body} but received ${ request . body } ` ] ,
228
+ } ;
188
229
}
230
+
231
+ messages . push ( `✅ Body matches` ) ;
189
232
} else if ( this . #body instanceof FormData ) {
190
233
if ( ! ( request . body instanceof FormData ) ) {
191
- return false ;
234
+ return {
235
+ matches : false ,
236
+ messages : [ ...messages , "❌ Body does not match. Expected FormData but received none." ] ,
237
+ } ;
192
238
}
193
239
194
240
for ( const [ key , value ] of this . #body. entries ( ) ) {
195
241
if ( request . body . get ( key ) !== value ) {
196
- return false ;
242
+ return {
243
+ matches : false ,
244
+ messages : [ ...messages , `❌ Body does not match. Expected ${ key } =${ value } but received ${ key } =${ request . body . get ( key ) } .` ] ,
245
+ } ;
197
246
}
198
247
}
248
+
249
+ messages . push ( "✅ Body matches." ) ;
199
250
} else {
200
251
// body must be an object here to run a check
201
252
if ( typeof request . body !== "object" ) {
202
- return false ;
253
+ return {
254
+ matches : false ,
255
+ messages : [ ...messages , "❌ Body does not match. Expected object but received none." ] ,
256
+ } ;
203
257
}
204
258
205
259
// body is an object so proceed
206
260
if ( ! deepCompare ( request . body , this . #body) ) {
207
- return false ;
261
+ return {
262
+ matches : false ,
263
+ messages : [ ...messages , `❌ Body does not match. Expected ${ JSON . stringify ( this . #body) } but received ${ JSON . stringify ( request . body ) } .` ] ,
264
+ } ;
208
265
}
266
+
267
+ messages . push ( "✅ Body matches." ) ;
209
268
}
210
269
}
211
270
212
- return true ;
271
+ return {
272
+ matches : true ,
273
+ messages,
274
+ } ;
275
+ }
276
+
277
+ /**
278
+ * Checks if the request matches the matcher.
279
+ * @param {RequestPattern } request The request to check.
280
+ * @returns {boolean } True if the request matches, false if not.
281
+ */
282
+ matches ( request ) {
283
+ return this . traceMatches ( request ) . matches ;
213
284
}
214
285
}
0 commit comments