-
Notifications
You must be signed in to change notification settings - Fork 111
Expand file tree
/
Copy pathfetchWrapper.test.ts
More file actions
113 lines (95 loc) · 3.38 KB
/
Copy pathfetchWrapper.test.ts
File metadata and controls
113 lines (95 loc) · 3.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { FetchError } from '@opencode-manager/shared'
import { fetchWrapper } from './fetchWrapper'
describe('fetchWrapper', () => {
const fetchMock = vi.fn()
beforeEach(() => {
fetchMock.mockReset()
vi.stubGlobal('fetch', fetchMock)
})
afterEach(() => {
vi.unstubAllGlobals()
})
it('sends credentials: include on every request', async () => {
fetchMock.mockResolvedValue(new Response(JSON.stringify({ ok: true }), { status: 200 }))
await fetchWrapper('/api/x')
expect(fetchMock.mock.calls[0][1].credentials).toBe('include')
})
it('keeps credentials: include when the caller passes its own options', async () => {
fetchMock.mockResolvedValue(new Response(JSON.stringify({ ok: true }), { status: 200 }))
await fetchWrapper('/api/x', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: '{}',
})
const init = fetchMock.mock.calls[0][1]
expect(init.credentials).toBe('include')
expect(init.method).toBe('POST')
})
it('aborts the request when the caller signal aborts', async () => {
fetchMock.mockImplementation((_url, init) =>
new Promise((_resolve, reject) => {
init.signal.addEventListener('abort', () =>
reject(new DOMException('Aborted', 'AbortError')),
)
}),
)
const controller = new AbortController()
const promise = fetchWrapper('/api/x', { signal: controller.signal })
controller.abort()
await expect(promise).rejects.toThrow()
})
it('rejects with a 408 TIMEOUT FetchError when the timeout elapses', async () => {
vi.useFakeTimers()
fetchMock.mockImplementation((_url, init) => {
const signal = init.signal as AbortSignal
return new Promise((_resolve, reject) => {
if (signal.aborted) {
const err = new Error('The operation was aborted')
err.name = 'AbortError'
reject(err)
return
}
signal.addEventListener('abort', () => {
const err = new Error('The operation was aborted')
err.name = 'AbortError'
reject(err)
}, { once: true })
})
})
const promise = fetchWrapper('/api/x', { timeout: 100 })
vi.advanceTimersByTime(101)
await expect(promise).rejects.toBeInstanceOf(FetchError)
await expect(promise).rejects.toMatchObject({
statusCode: 408,
code: 'TIMEOUT',
})
vi.useRealTimers()
})
it('rejects with 499 CANCELED when the caller aborts, not 408 TIMEOUT', async () => {
fetchMock.mockImplementation((_url, init) => {
const signal = init.signal as AbortSignal
return new Promise((_resolve, reject) => {
if (signal.aborted) {
const err = new Error('The operation was aborted')
err.name = 'AbortError'
reject(err)
return
}
signal.addEventListener('abort', () => {
const err = new Error('The operation was aborted')
err.name = 'AbortError'
reject(err)
}, { once: true })
})
})
const controller = new AbortController()
const promise = fetchWrapper('/api/x', { signal: controller.signal })
controller.abort()
await expect(promise).rejects.toBeInstanceOf(FetchError)
await expect(promise).rejects.toMatchObject({
statusCode: 499,
code: 'CANCELED',
})
})
})