@@ -14,7 +14,7 @@ class XhrMock {
14
14
public static captures : any [ ] = [ ] ;
15
15
public static DONE = 4 ;
16
16
17
- private captureArgs =
17
+ protected captureArgs =
18
18
( caller : string ) =>
19
19
( ...args : any [ ] ) => {
20
20
XhrMock . captures . push ( [ caller , ...args ] ) ;
@@ -244,4 +244,94 @@ describe(XhrHttpHandler.name, () => {
244
244
[ "getAllResponseHeaders" ] ,
245
245
] ) ;
246
246
} ) ;
247
+
248
+ describe ( "per-request requestTimeout" , ( ) => {
249
+ it ( "should use per-request timeout over handler config timeout" , async ( ) => {
250
+ const handler = new XhrHttpHandler ( { requestTimeout : 5000 } ) ;
251
+
252
+ const requestTimeoutSpy = vi . spyOn ( await import ( "./request-timeout" ) , "requestTimeout" ) ;
253
+
254
+ const mockRequest = new HttpRequest ( {
255
+ method : "GET" ,
256
+ hostname : "example.com" ,
257
+ protocol : "https:" ,
258
+ path : "/" ,
259
+ headers : { } ,
260
+ } ) ;
261
+
262
+ class TimeoutXhrMock extends XhrMock {
263
+ send ( ...args : any [ ] ) {
264
+ this . captureArgs ( "send" ) ( ...args ) ;
265
+ // let it timeout
266
+ }
267
+ }
268
+
269
+ ( global as any ) . XMLHttpRequest = TimeoutXhrMock ;
270
+
271
+ try {
272
+ await handler . handle ( mockRequest , { requestTimeout : 100 } ) ;
273
+ } catch ( error ) {
274
+ // expected to timeout
275
+ }
276
+
277
+ // verify requestTimeout function was called with per-request timeout (100), not handler timeout (5000)
278
+ expect ( requestTimeoutSpy ) . toHaveBeenCalledWith ( 100 ) ;
279
+
280
+ requestTimeoutSpy . mockRestore ( ) ;
281
+ ( global as any ) . XMLHttpRequest = XhrMock ; // restore original mock
282
+ } ) ;
283
+
284
+ it ( "should fall back to handler config timeout when per-request timeout not provided" , async ( ) => {
285
+ const handler = new XhrHttpHandler ( { requestTimeout : 200 } ) ;
286
+
287
+ const requestTimeoutSpy = vi . spyOn ( await import ( "./request-timeout" ) , "requestTimeout" ) ;
288
+
289
+ const mockRequest = new HttpRequest ( {
290
+ method : "GET" ,
291
+ hostname : "example.com" ,
292
+ protocol : "https:" ,
293
+ path : "/" ,
294
+ headers : { } ,
295
+ } ) ;
296
+
297
+ class TimeoutXhrMock extends XhrMock {
298
+ send ( ...args : any [ ] ) {
299
+ this . captureArgs ( "send" ) ( ...args ) ;
300
+ }
301
+ }
302
+
303
+ ( global as any ) . XMLHttpRequest = TimeoutXhrMock ;
304
+
305
+ try {
306
+ await handler . handle ( mockRequest , { } ) ;
307
+ } catch ( error ) { }
308
+
309
+ expect ( requestTimeoutSpy ) . toHaveBeenCalledWith ( 200 ) ;
310
+
311
+ requestTimeoutSpy . mockRestore ( ) ;
312
+ ( global as any ) . XMLHttpRequest = XhrMock ;
313
+ } ) ;
314
+
315
+ it ( "should handle zero timeout correctly" , async ( ) => {
316
+ const handler = new XhrHttpHandler ( { requestTimeout : 1000 } ) ;
317
+
318
+ const requestTimeoutSpy = vi . spyOn ( await import ( "./request-timeout" ) , "requestTimeout" ) ;
319
+
320
+ const mockRequest = new HttpRequest ( {
321
+ method : "GET" ,
322
+ hostname : "example.com" ,
323
+ protocol : "https:" ,
324
+ path : "/" ,
325
+ headers : { } ,
326
+ } ) ;
327
+
328
+ try {
329
+ await handler . handle ( mockRequest , { requestTimeout : 0 } ) ;
330
+ } catch ( error ) { }
331
+
332
+ expect ( requestTimeoutSpy ) . toHaveBeenCalledWith ( 0 ) ;
333
+
334
+ requestTimeoutSpy . mockRestore ( ) ;
335
+ } ) ;
336
+ } ) ;
247
337
} ) ;
0 commit comments