/ src / lib / fetch-timeout.test.ts
fetch-timeout.test.ts
 1  import test from 'node:test'
 2  import assert from 'node:assert/strict'
 3  
 4  import { fetchWithTimeout } from './fetch-timeout'
 5  
 6  const originalFetch = global.fetch
 7  const originalSetTimeout = global.setTimeout
 8  const originalClearTimeout = global.clearTimeout
 9  
10  test.afterEach(() => {
11    global.fetch = originalFetch
12    global.setTimeout = originalSetTimeout
13    global.clearTimeout = originalClearTimeout
14  })
15  
16  test('fetchWithTimeout throws TimeoutError with a clear message on timeout', async () => {
17    global.setTimeout = (((callback: (...args: unknown[]) => void) => {
18      queueMicrotask(() => callback())
19      return 1 as unknown as ReturnType<typeof setTimeout>
20    }) as typeof setTimeout)
21    global.clearTimeout = (() => {}) as typeof clearTimeout
22    global.fetch = (((_input: RequestInfo | URL, init?: RequestInit) => new Promise<Response>((_resolve, reject) => {
23      const onAbort = () => reject(init?.signal?.reason ?? new DOMException('Aborted', 'AbortError'))
24      if (init?.signal?.aborted) onAbort()
25      else init?.signal?.addEventListener('abort', onAbort, { once: true })
26    })) as typeof fetch)
27  
28    await assert.rejects(
29      () => fetchWithTimeout('/slow', {}, 5_000),
30      (err: unknown) => {
31        assert.ok(err instanceof Error)
32        assert.equal(err.name, 'TimeoutError')
33        assert.match(err.message, /5000ms/)
34        return true
35      },
36    )
37  })
38  
39  test('fetchWithTimeout preserves caller abort signals', async () => {
40    const controller = new AbortController()
41    const expectedError = new DOMException('Manual cancel', 'AbortError')
42    controller.abort(expectedError)
43    global.fetch = (((_input: RequestInfo | URL, init?: RequestInit) => {
44      return Promise.reject(init?.signal?.reason ?? expectedError)
45    }) as typeof fetch)
46  
47    await assert.rejects(
48      () => fetchWithTimeout('/aborted', { signal: controller.signal }, 5_000),
49      (err: unknown) => {
50        assert.strictEqual(err, expectedError)
51        return true
52      },
53    )
54  })