@tanstack/solid-query 5.59.17 → 5.60.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/dev.cjs +2 -2
- package/build/dev.js +2 -2
- package/build/index.cjs +2 -2
- package/build/index.js +2 -2
- package/package.json +4 -3
- package/src/createQueries.ts +3 -2
- package/src/utils.ts +1 -1
- package/src/__tests__/QueryClientProvider.test.tsx +0 -180
- package/src/__tests__/createInfiniteQuery.test.tsx +0 -2093
- package/src/__tests__/createMutation.test.tsx +0 -1238
- package/src/__tests__/createQueries.test.tsx +0 -732
- package/src/__tests__/createQuery.test-d.tsx +0 -98
- package/src/__tests__/createQuery.test.tsx +0 -6055
- package/src/__tests__/infiniteQueryOptions.test-d.tsx +0 -81
- package/src/__tests__/queryOptions.test-d.tsx +0 -146
- package/src/__tests__/suspense.test.tsx +0 -956
- package/src/__tests__/transition.test.tsx +0 -60
- package/src/__tests__/useIsFetching.test.tsx +0 -251
- package/src/__tests__/useIsMutating.test.tsx +0 -248
- package/src/__tests__/useMutationState.test-d.tsx +0 -21
- package/src/__tests__/useMutationState.test.tsx +0 -68
- package/src/__tests__/utils.tsx +0 -61
|
@@ -1,1238 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from 'vitest'
|
|
2
|
-
import {
|
|
3
|
-
ErrorBoundary,
|
|
4
|
-
createEffect,
|
|
5
|
-
createRenderEffect,
|
|
6
|
-
createSignal,
|
|
7
|
-
} from 'solid-js'
|
|
8
|
-
import { fireEvent, render, waitFor } from '@solidjs/testing-library'
|
|
9
|
-
import {
|
|
10
|
-
MutationCache,
|
|
11
|
-
QueryCache,
|
|
12
|
-
QueryClientProvider,
|
|
13
|
-
createMutation,
|
|
14
|
-
} from '..'
|
|
15
|
-
import {
|
|
16
|
-
createQueryClient,
|
|
17
|
-
mockOnlineManagerIsOnline,
|
|
18
|
-
queryKey,
|
|
19
|
-
setActTimeout,
|
|
20
|
-
sleep,
|
|
21
|
-
} from './utils'
|
|
22
|
-
import type { CreateMutationResult } from '../types'
|
|
23
|
-
|
|
24
|
-
describe('createMutation', () => {
|
|
25
|
-
const queryCache = new QueryCache()
|
|
26
|
-
const mutationCache = new MutationCache()
|
|
27
|
-
const queryClient = createQueryClient({ queryCache, mutationCache })
|
|
28
|
-
|
|
29
|
-
it('should be able to reset `data`', async () => {
|
|
30
|
-
function Page() {
|
|
31
|
-
const mutation = createMutation(() => ({
|
|
32
|
-
mutationFn: () => Promise.resolve('mutation'),
|
|
33
|
-
}))
|
|
34
|
-
|
|
35
|
-
return (
|
|
36
|
-
<div>
|
|
37
|
-
<h1>{mutation.data ?? 'empty'}</h1>
|
|
38
|
-
<button onClick={() => mutation.reset()}>reset</button>
|
|
39
|
-
<button onClick={() => mutation.mutate()}>mutate</button>
|
|
40
|
-
</div>
|
|
41
|
-
)
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const rendered = render(() => (
|
|
45
|
-
<QueryClientProvider client={queryClient}>
|
|
46
|
-
<Page />
|
|
47
|
-
</QueryClientProvider>
|
|
48
|
-
))
|
|
49
|
-
|
|
50
|
-
expect(rendered.getByRole('heading').textContent).toBe('empty')
|
|
51
|
-
|
|
52
|
-
fireEvent.click(rendered.getByRole('button', { name: /mutate/i }))
|
|
53
|
-
|
|
54
|
-
await waitFor(() => {
|
|
55
|
-
expect(rendered.getByRole('heading').textContent).toBe('mutation')
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
fireEvent.click(rendered.getByRole('button', { name: /reset/i }))
|
|
59
|
-
|
|
60
|
-
await waitFor(() => {
|
|
61
|
-
expect(rendered.getByRole('heading').textContent).toBe('empty')
|
|
62
|
-
})
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
it('should be able to reset `error`', async () => {
|
|
66
|
-
const consoleMock = vi
|
|
67
|
-
.spyOn(console, 'error')
|
|
68
|
-
.mockImplementation(() => undefined)
|
|
69
|
-
|
|
70
|
-
function Page() {
|
|
71
|
-
const mutation = createMutation<string, Error>(() => ({
|
|
72
|
-
mutationFn: () => {
|
|
73
|
-
const err = new Error('Expected mock error. All is well!')
|
|
74
|
-
err.stack = ''
|
|
75
|
-
return Promise.reject(err)
|
|
76
|
-
},
|
|
77
|
-
}))
|
|
78
|
-
|
|
79
|
-
return (
|
|
80
|
-
<div>
|
|
81
|
-
{mutation.error && <h1>{mutation.error.message}</h1>}
|
|
82
|
-
<button onClick={() => mutation.reset()}>reset</button>
|
|
83
|
-
<button onClick={() => mutation.mutate()}>mutate</button>
|
|
84
|
-
</div>
|
|
85
|
-
)
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const rendered = render(() => (
|
|
89
|
-
<QueryClientProvider client={queryClient}>
|
|
90
|
-
<Page />
|
|
91
|
-
</QueryClientProvider>
|
|
92
|
-
))
|
|
93
|
-
|
|
94
|
-
await waitFor(() => {
|
|
95
|
-
expect(rendered.queryByRole('heading')).toBeNull()
|
|
96
|
-
})
|
|
97
|
-
|
|
98
|
-
fireEvent.click(rendered.getByRole('button', { name: /mutate/i }))
|
|
99
|
-
|
|
100
|
-
await waitFor(() => {
|
|
101
|
-
expect(rendered.getByRole('heading').textContent).toBe(
|
|
102
|
-
'Expected mock error. All is well!',
|
|
103
|
-
)
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
fireEvent.click(rendered.getByRole('button', { name: /reset/i }))
|
|
107
|
-
|
|
108
|
-
await waitFor(() => {
|
|
109
|
-
expect(rendered.queryByRole('heading')).toBeNull()
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
consoleMock.mockRestore()
|
|
113
|
-
})
|
|
114
|
-
|
|
115
|
-
it('should be able to call `onSuccess` and `onSettled` after each successful mutate', async () => {
|
|
116
|
-
const [count, setCount] = createSignal(0)
|
|
117
|
-
const onSuccessMock = vi.fn()
|
|
118
|
-
const onSettledMock = vi.fn()
|
|
119
|
-
|
|
120
|
-
function Page() {
|
|
121
|
-
const mutation = createMutation(() => ({
|
|
122
|
-
mutationFn: (vars: { count: number }) => Promise.resolve(vars.count),
|
|
123
|
-
onSuccess: (data) => {
|
|
124
|
-
onSuccessMock(data)
|
|
125
|
-
},
|
|
126
|
-
onSettled: (data) => {
|
|
127
|
-
onSettledMock(data)
|
|
128
|
-
},
|
|
129
|
-
}))
|
|
130
|
-
|
|
131
|
-
return (
|
|
132
|
-
<div>
|
|
133
|
-
<h1>{count()}</h1>
|
|
134
|
-
<button
|
|
135
|
-
onClick={() => {
|
|
136
|
-
setCount((c) => c + 1)
|
|
137
|
-
return mutation.mutate({ count: count() })
|
|
138
|
-
}}
|
|
139
|
-
>
|
|
140
|
-
mutate
|
|
141
|
-
</button>
|
|
142
|
-
</div>
|
|
143
|
-
)
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
const rendered = render(() => (
|
|
147
|
-
<QueryClientProvider client={queryClient}>
|
|
148
|
-
<Page />
|
|
149
|
-
</QueryClientProvider>
|
|
150
|
-
))
|
|
151
|
-
|
|
152
|
-
expect(rendered.getByRole('heading').textContent).toBe('0')
|
|
153
|
-
|
|
154
|
-
fireEvent.click(rendered.getByRole('button', { name: /mutate/i }))
|
|
155
|
-
fireEvent.click(rendered.getByRole('button', { name: /mutate/i }))
|
|
156
|
-
fireEvent.click(rendered.getByRole('button', { name: /mutate/i }))
|
|
157
|
-
|
|
158
|
-
await waitFor(() => {
|
|
159
|
-
expect(rendered.getByRole('heading').textContent).toBe('3')
|
|
160
|
-
})
|
|
161
|
-
|
|
162
|
-
await waitFor(() => {
|
|
163
|
-
expect(onSuccessMock).toHaveBeenCalledTimes(3)
|
|
164
|
-
})
|
|
165
|
-
|
|
166
|
-
expect(onSuccessMock).toHaveBeenCalledWith(1)
|
|
167
|
-
expect(onSuccessMock).toHaveBeenCalledWith(2)
|
|
168
|
-
expect(onSuccessMock).toHaveBeenCalledWith(3)
|
|
169
|
-
|
|
170
|
-
await waitFor(() => {
|
|
171
|
-
expect(onSettledMock).toHaveBeenCalledTimes(3)
|
|
172
|
-
})
|
|
173
|
-
|
|
174
|
-
expect(onSettledMock).toHaveBeenCalledWith(1)
|
|
175
|
-
expect(onSettledMock).toHaveBeenCalledWith(2)
|
|
176
|
-
expect(onSettledMock).toHaveBeenCalledWith(3)
|
|
177
|
-
})
|
|
178
|
-
|
|
179
|
-
it('should set correct values for `failureReason` and `failureCount` on multiple mutate calls', async () => {
|
|
180
|
-
const [count, setCount] = createSignal(0)
|
|
181
|
-
type Value = { count: number }
|
|
182
|
-
|
|
183
|
-
const mutateFn = vi.fn<(value: Value) => Promise<Value>>()
|
|
184
|
-
|
|
185
|
-
mutateFn.mockImplementationOnce(() => {
|
|
186
|
-
return Promise.reject(new Error('Error test Jonas'))
|
|
187
|
-
})
|
|
188
|
-
|
|
189
|
-
mutateFn.mockImplementation(async (value) => {
|
|
190
|
-
await sleep(10)
|
|
191
|
-
return Promise.resolve(value)
|
|
192
|
-
})
|
|
193
|
-
|
|
194
|
-
function Page() {
|
|
195
|
-
const mutation = createMutation(() => ({
|
|
196
|
-
mutationFn: mutateFn,
|
|
197
|
-
}))
|
|
198
|
-
|
|
199
|
-
return (
|
|
200
|
-
<div>
|
|
201
|
-
<h1>Data {mutation.data?.count}</h1>
|
|
202
|
-
<h2>Status {mutation.status}</h2>
|
|
203
|
-
<h2>Failed {mutation.failureCount} times</h2>
|
|
204
|
-
<h2>Failed because {mutation.failureReason?.message ?? 'null'}</h2>
|
|
205
|
-
<button
|
|
206
|
-
onClick={() => {
|
|
207
|
-
setCount((c) => c + 1)
|
|
208
|
-
return mutation.mutate({ count: count() })
|
|
209
|
-
}}
|
|
210
|
-
>
|
|
211
|
-
mutate
|
|
212
|
-
</button>
|
|
213
|
-
</div>
|
|
214
|
-
)
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
const rendered = render(() => (
|
|
218
|
-
<QueryClientProvider client={queryClient}>
|
|
219
|
-
<Page />
|
|
220
|
-
</QueryClientProvider>
|
|
221
|
-
))
|
|
222
|
-
|
|
223
|
-
await waitFor(() => rendered.getByText('Data'))
|
|
224
|
-
|
|
225
|
-
fireEvent.click(rendered.getByRole('button', { name: /mutate/i }))
|
|
226
|
-
await waitFor(() => rendered.getByText('Data'))
|
|
227
|
-
await waitFor(() => rendered.getByText('Status error'))
|
|
228
|
-
await waitFor(() => rendered.getByText('Failed 1 times'))
|
|
229
|
-
await waitFor(() => rendered.getByText('Failed because Error test Jonas'))
|
|
230
|
-
|
|
231
|
-
fireEvent.click(rendered.getByRole('button', { name: /mutate/i }))
|
|
232
|
-
await waitFor(() => rendered.getByText('Status pending'))
|
|
233
|
-
await waitFor(() => rendered.getByText('Status success'))
|
|
234
|
-
await waitFor(() => rendered.getByText('Data 2'))
|
|
235
|
-
await waitFor(() => rendered.getByText('Failed 0 times'))
|
|
236
|
-
await waitFor(() => rendered.getByText('Failed because null'))
|
|
237
|
-
})
|
|
238
|
-
|
|
239
|
-
it('should be able to call `onError` and `onSettled` after each failed mutate', async () => {
|
|
240
|
-
const onErrorMock = vi.fn()
|
|
241
|
-
const onSettledMock = vi.fn()
|
|
242
|
-
const [count, setCount] = createSignal(0)
|
|
243
|
-
|
|
244
|
-
function Page() {
|
|
245
|
-
const mutation = createMutation(() => ({
|
|
246
|
-
mutationFn: (vars: { count: number }) => {
|
|
247
|
-
const error = new Error(
|
|
248
|
-
`Expected mock error. All is well! ${vars.count}`,
|
|
249
|
-
)
|
|
250
|
-
error.stack = ''
|
|
251
|
-
return Promise.reject(error)
|
|
252
|
-
},
|
|
253
|
-
onError: (error: Error) => {
|
|
254
|
-
onErrorMock(error.message)
|
|
255
|
-
},
|
|
256
|
-
onSettled: (_data, error) => {
|
|
257
|
-
onSettledMock(error?.message)
|
|
258
|
-
},
|
|
259
|
-
}))
|
|
260
|
-
|
|
261
|
-
return (
|
|
262
|
-
<div>
|
|
263
|
-
<h1>{count()}</h1>
|
|
264
|
-
<button
|
|
265
|
-
onClick={() => {
|
|
266
|
-
setCount((c) => c + 1)
|
|
267
|
-
return mutation.mutate({ count: count() })
|
|
268
|
-
}}
|
|
269
|
-
>
|
|
270
|
-
mutate
|
|
271
|
-
</button>
|
|
272
|
-
</div>
|
|
273
|
-
)
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
const rendered = render(() => (
|
|
277
|
-
<QueryClientProvider client={queryClient}>
|
|
278
|
-
<Page />
|
|
279
|
-
</QueryClientProvider>
|
|
280
|
-
))
|
|
281
|
-
|
|
282
|
-
expect(rendered.getByRole('heading').textContent).toBe('0')
|
|
283
|
-
|
|
284
|
-
fireEvent.click(rendered.getByRole('button', { name: /mutate/i }))
|
|
285
|
-
fireEvent.click(rendered.getByRole('button', { name: /mutate/i }))
|
|
286
|
-
fireEvent.click(rendered.getByRole('button', { name: /mutate/i }))
|
|
287
|
-
|
|
288
|
-
await waitFor(() => {
|
|
289
|
-
expect(rendered.getByRole('heading').textContent).toBe('3')
|
|
290
|
-
})
|
|
291
|
-
|
|
292
|
-
await waitFor(() => {
|
|
293
|
-
expect(onErrorMock).toHaveBeenCalledTimes(3)
|
|
294
|
-
})
|
|
295
|
-
expect(onErrorMock).toHaveBeenCalledWith(
|
|
296
|
-
'Expected mock error. All is well! 1',
|
|
297
|
-
)
|
|
298
|
-
expect(onErrorMock).toHaveBeenCalledWith(
|
|
299
|
-
'Expected mock error. All is well! 2',
|
|
300
|
-
)
|
|
301
|
-
expect(onErrorMock).toHaveBeenCalledWith(
|
|
302
|
-
'Expected mock error. All is well! 3',
|
|
303
|
-
)
|
|
304
|
-
|
|
305
|
-
await waitFor(() => {
|
|
306
|
-
expect(onSettledMock).toHaveBeenCalledTimes(3)
|
|
307
|
-
})
|
|
308
|
-
expect(onSettledMock).toHaveBeenCalledWith(
|
|
309
|
-
'Expected mock error. All is well! 1',
|
|
310
|
-
)
|
|
311
|
-
expect(onSettledMock).toHaveBeenCalledWith(
|
|
312
|
-
'Expected mock error. All is well! 2',
|
|
313
|
-
)
|
|
314
|
-
expect(onSettledMock).toHaveBeenCalledWith(
|
|
315
|
-
'Expected mock error. All is well! 3',
|
|
316
|
-
)
|
|
317
|
-
})
|
|
318
|
-
|
|
319
|
-
it('should be able to override the useMutation success callbacks', async () => {
|
|
320
|
-
const callbacks: Array<string> = []
|
|
321
|
-
|
|
322
|
-
function Page() {
|
|
323
|
-
const mutation = createMutation(() => ({
|
|
324
|
-
mutationFn: async (text: string) => text,
|
|
325
|
-
onSuccess: async () => {
|
|
326
|
-
callbacks.push('useMutation.onSuccess')
|
|
327
|
-
},
|
|
328
|
-
onSettled: async () => {
|
|
329
|
-
callbacks.push('useMutation.onSettled')
|
|
330
|
-
},
|
|
331
|
-
}))
|
|
332
|
-
|
|
333
|
-
createEffect(() => {
|
|
334
|
-
const { mutateAsync } = mutation
|
|
335
|
-
setActTimeout(async () => {
|
|
336
|
-
try {
|
|
337
|
-
const result = await mutateAsync('todo', {
|
|
338
|
-
onSuccess: async () => {
|
|
339
|
-
callbacks.push('mutateAsync.onSuccess')
|
|
340
|
-
},
|
|
341
|
-
onSettled: async () => {
|
|
342
|
-
callbacks.push('mutateAsync.onSettled')
|
|
343
|
-
},
|
|
344
|
-
})
|
|
345
|
-
callbacks.push(`mutateAsync.result:${result}`)
|
|
346
|
-
} catch {}
|
|
347
|
-
}, 10)
|
|
348
|
-
})
|
|
349
|
-
|
|
350
|
-
return null
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
render(() => (
|
|
354
|
-
<QueryClientProvider client={queryClient}>
|
|
355
|
-
<Page />
|
|
356
|
-
</QueryClientProvider>
|
|
357
|
-
))
|
|
358
|
-
|
|
359
|
-
await sleep(100)
|
|
360
|
-
|
|
361
|
-
expect(callbacks).toEqual([
|
|
362
|
-
'useMutation.onSuccess',
|
|
363
|
-
'useMutation.onSettled',
|
|
364
|
-
'mutateAsync.onSuccess',
|
|
365
|
-
'mutateAsync.onSettled',
|
|
366
|
-
'mutateAsync.result:todo',
|
|
367
|
-
])
|
|
368
|
-
})
|
|
369
|
-
|
|
370
|
-
it('should be able to override the error callbacks when using mutateAsync', async () => {
|
|
371
|
-
const callbacks: Array<string> = []
|
|
372
|
-
|
|
373
|
-
function Page() {
|
|
374
|
-
const mutation = createMutation(() => ({
|
|
375
|
-
mutationFn: async (_text: string) => Promise.reject(new Error('oops')),
|
|
376
|
-
|
|
377
|
-
onError: async () => {
|
|
378
|
-
callbacks.push('useMutation.onError')
|
|
379
|
-
},
|
|
380
|
-
onSettled: async () => {
|
|
381
|
-
callbacks.push('useMutation.onSettled')
|
|
382
|
-
},
|
|
383
|
-
}))
|
|
384
|
-
|
|
385
|
-
createEffect(() => {
|
|
386
|
-
const { mutateAsync } = mutation
|
|
387
|
-
setActTimeout(async () => {
|
|
388
|
-
try {
|
|
389
|
-
await mutateAsync('todo', {
|
|
390
|
-
onError: async () => {
|
|
391
|
-
callbacks.push('mutateAsync.onError')
|
|
392
|
-
},
|
|
393
|
-
onSettled: async () => {
|
|
394
|
-
callbacks.push('mutateAsync.onSettled')
|
|
395
|
-
},
|
|
396
|
-
})
|
|
397
|
-
} catch (error) {
|
|
398
|
-
callbacks.push(`mutateAsync.error:${(error as Error).message}`)
|
|
399
|
-
}
|
|
400
|
-
}, 10)
|
|
401
|
-
})
|
|
402
|
-
|
|
403
|
-
return null
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
render(() => (
|
|
407
|
-
<QueryClientProvider client={queryClient}>
|
|
408
|
-
<Page />
|
|
409
|
-
</QueryClientProvider>
|
|
410
|
-
))
|
|
411
|
-
|
|
412
|
-
await sleep(100)
|
|
413
|
-
|
|
414
|
-
expect(callbacks).toEqual([
|
|
415
|
-
'useMutation.onError',
|
|
416
|
-
'useMutation.onSettled',
|
|
417
|
-
'mutateAsync.onError',
|
|
418
|
-
'mutateAsync.onSettled',
|
|
419
|
-
'mutateAsync.error:oops',
|
|
420
|
-
])
|
|
421
|
-
})
|
|
422
|
-
|
|
423
|
-
it('should be able to use mutation defaults', async () => {
|
|
424
|
-
const key = queryKey()
|
|
425
|
-
|
|
426
|
-
queryClient.setMutationDefaults(key, {
|
|
427
|
-
mutationFn: async (text: string) => {
|
|
428
|
-
await sleep(10)
|
|
429
|
-
return text
|
|
430
|
-
},
|
|
431
|
-
})
|
|
432
|
-
|
|
433
|
-
const states: Array<CreateMutationResult<any, any, any, any>> = []
|
|
434
|
-
|
|
435
|
-
function Page() {
|
|
436
|
-
const mutation = createMutation<string, unknown, string>(() => ({
|
|
437
|
-
mutationKey: key,
|
|
438
|
-
}))
|
|
439
|
-
|
|
440
|
-
createRenderEffect(() => {
|
|
441
|
-
states.push({ ...mutation })
|
|
442
|
-
})
|
|
443
|
-
|
|
444
|
-
createEffect(() => {
|
|
445
|
-
const { mutate } = mutation
|
|
446
|
-
setActTimeout(() => {
|
|
447
|
-
mutate('todo')
|
|
448
|
-
}, 10)
|
|
449
|
-
})
|
|
450
|
-
|
|
451
|
-
return null
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
render(() => (
|
|
455
|
-
<QueryClientProvider client={queryClient}>
|
|
456
|
-
<Page />
|
|
457
|
-
</QueryClientProvider>
|
|
458
|
-
))
|
|
459
|
-
|
|
460
|
-
await sleep(100)
|
|
461
|
-
|
|
462
|
-
expect(states.length).toBe(3)
|
|
463
|
-
expect(states[0]).toMatchObject({ data: undefined, isPending: false })
|
|
464
|
-
expect(states[1]).toMatchObject({ data: undefined, isPending: true })
|
|
465
|
-
expect(states[2]).toMatchObject({ data: 'todo', isPending: false })
|
|
466
|
-
})
|
|
467
|
-
|
|
468
|
-
it('should be able to retry a failed mutation', async () => {
|
|
469
|
-
let count = 0
|
|
470
|
-
|
|
471
|
-
function Page() {
|
|
472
|
-
const mutation = createMutation(() => ({
|
|
473
|
-
mutationFn: (_text: string) => {
|
|
474
|
-
count++
|
|
475
|
-
return Promise.reject(new Error('oops'))
|
|
476
|
-
},
|
|
477
|
-
retry: 1,
|
|
478
|
-
retryDelay: 5,
|
|
479
|
-
}))
|
|
480
|
-
|
|
481
|
-
createEffect(() => {
|
|
482
|
-
const { mutate } = mutation
|
|
483
|
-
setActTimeout(() => {
|
|
484
|
-
mutate('todo')
|
|
485
|
-
}, 10)
|
|
486
|
-
})
|
|
487
|
-
|
|
488
|
-
return null
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
render(() => (
|
|
492
|
-
<QueryClientProvider client={queryClient}>
|
|
493
|
-
<Page />
|
|
494
|
-
</QueryClientProvider>
|
|
495
|
-
))
|
|
496
|
-
|
|
497
|
-
await sleep(100)
|
|
498
|
-
|
|
499
|
-
expect(count).toBe(2)
|
|
500
|
-
})
|
|
501
|
-
|
|
502
|
-
it('should not retry mutations while offline', async () => {
|
|
503
|
-
const onlineMock = mockOnlineManagerIsOnline(false)
|
|
504
|
-
|
|
505
|
-
let count = 0
|
|
506
|
-
|
|
507
|
-
function Page() {
|
|
508
|
-
const mutation = createMutation(() => ({
|
|
509
|
-
mutationFn: (_text: string) => {
|
|
510
|
-
count++
|
|
511
|
-
return Promise.reject(new Error('oops'))
|
|
512
|
-
},
|
|
513
|
-
retry: 1,
|
|
514
|
-
retryDelay: 5,
|
|
515
|
-
}))
|
|
516
|
-
|
|
517
|
-
return (
|
|
518
|
-
<div>
|
|
519
|
-
<button onClick={() => mutation.mutate('todo')}>mutate</button>
|
|
520
|
-
<div>
|
|
521
|
-
{`error: ${
|
|
522
|
-
mutation.error instanceof Error ? mutation.error.message : 'null'
|
|
523
|
-
}, status: ${mutation.status}, isPaused: ${String(
|
|
524
|
-
mutation.isPaused,
|
|
525
|
-
)}`}
|
|
526
|
-
</div>
|
|
527
|
-
</div>
|
|
528
|
-
)
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
const rendered = render(() => (
|
|
532
|
-
<QueryClientProvider client={queryClient}>
|
|
533
|
-
<Page />
|
|
534
|
-
</QueryClientProvider>
|
|
535
|
-
))
|
|
536
|
-
|
|
537
|
-
await waitFor(() => {
|
|
538
|
-
expect(
|
|
539
|
-
rendered.getByText('error: null, status: idle, isPaused: false'),
|
|
540
|
-
).toBeInTheDocument()
|
|
541
|
-
})
|
|
542
|
-
|
|
543
|
-
window.dispatchEvent(new Event('offline'))
|
|
544
|
-
|
|
545
|
-
fireEvent.click(rendered.getByRole('button', { name: /mutate/i }))
|
|
546
|
-
|
|
547
|
-
await waitFor(() => {
|
|
548
|
-
expect(
|
|
549
|
-
rendered.getByText('error: null, status: pending, isPaused: true'),
|
|
550
|
-
).toBeInTheDocument()
|
|
551
|
-
})
|
|
552
|
-
|
|
553
|
-
expect(count).toBe(0)
|
|
554
|
-
|
|
555
|
-
onlineMock.mockRestore()
|
|
556
|
-
window.dispatchEvent(new Event('online'))
|
|
557
|
-
|
|
558
|
-
await sleep(100)
|
|
559
|
-
|
|
560
|
-
await waitFor(() => {
|
|
561
|
-
expect(
|
|
562
|
-
rendered.getByText('error: oops, status: error, isPaused: false'),
|
|
563
|
-
).toBeInTheDocument()
|
|
564
|
-
})
|
|
565
|
-
|
|
566
|
-
expect(count).toBe(2)
|
|
567
|
-
})
|
|
568
|
-
|
|
569
|
-
it('should call onMutate even if paused', async () => {
|
|
570
|
-
const onlineMock = mockOnlineManagerIsOnline(false)
|
|
571
|
-
const onMutate = vi.fn()
|
|
572
|
-
let count = 0
|
|
573
|
-
|
|
574
|
-
function Page() {
|
|
575
|
-
const mutation = createMutation(() => ({
|
|
576
|
-
mutationFn: async (_text: string) => {
|
|
577
|
-
count++
|
|
578
|
-
await sleep(10)
|
|
579
|
-
return count
|
|
580
|
-
},
|
|
581
|
-
onMutate,
|
|
582
|
-
}))
|
|
583
|
-
|
|
584
|
-
return (
|
|
585
|
-
<div>
|
|
586
|
-
<button onClick={() => mutation.mutate('todo')}>mutate</button>
|
|
587
|
-
<div>
|
|
588
|
-
data: {mutation.data ?? 'null'}, status: {mutation.status},
|
|
589
|
-
isPaused: {String(mutation.isPaused)}
|
|
590
|
-
</div>
|
|
591
|
-
</div>
|
|
592
|
-
)
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
const rendered = render(() => (
|
|
596
|
-
<QueryClientProvider client={queryClient}>
|
|
597
|
-
<Page />
|
|
598
|
-
</QueryClientProvider>
|
|
599
|
-
))
|
|
600
|
-
|
|
601
|
-
await rendered.findByText('data: null, status: idle, isPaused: false')
|
|
602
|
-
|
|
603
|
-
window.dispatchEvent(new Event('offline'))
|
|
604
|
-
|
|
605
|
-
fireEvent.click(rendered.getByRole('button', { name: /mutate/i }))
|
|
606
|
-
|
|
607
|
-
await rendered.findByText('data: null, status: pending, isPaused: true')
|
|
608
|
-
|
|
609
|
-
expect(onMutate).toHaveBeenCalledTimes(1)
|
|
610
|
-
expect(onMutate).toHaveBeenCalledWith('todo')
|
|
611
|
-
|
|
612
|
-
onlineMock.mockRestore()
|
|
613
|
-
window.dispatchEvent(new Event('online'))
|
|
614
|
-
|
|
615
|
-
await rendered.findByText('data: 1, status: success, isPaused: false')
|
|
616
|
-
|
|
617
|
-
expect(onMutate).toHaveBeenCalledTimes(1)
|
|
618
|
-
expect(count).toBe(1)
|
|
619
|
-
})
|
|
620
|
-
|
|
621
|
-
it('should optimistically go to paused state if offline', async () => {
|
|
622
|
-
const onlineMock = mockOnlineManagerIsOnline(false)
|
|
623
|
-
let count = 0
|
|
624
|
-
const states: Array<string> = []
|
|
625
|
-
|
|
626
|
-
function Page() {
|
|
627
|
-
const mutation = createMutation(() => ({
|
|
628
|
-
mutationFn: async (_text: string) => {
|
|
629
|
-
count++
|
|
630
|
-
await sleep(10)
|
|
631
|
-
return count
|
|
632
|
-
},
|
|
633
|
-
}))
|
|
634
|
-
|
|
635
|
-
createRenderEffect(() => {
|
|
636
|
-
states.push(`${mutation.status}, ${mutation.isPaused}`)
|
|
637
|
-
})
|
|
638
|
-
|
|
639
|
-
return (
|
|
640
|
-
<div>
|
|
641
|
-
<button onClick={() => mutation.mutate('todo')}>mutate</button>
|
|
642
|
-
<div>
|
|
643
|
-
data: {mutation.data ?? 'null'}, status: {mutation.status},
|
|
644
|
-
isPaused: {String(mutation.isPaused)}
|
|
645
|
-
</div>
|
|
646
|
-
</div>
|
|
647
|
-
)
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
const rendered = render(() => (
|
|
651
|
-
<QueryClientProvider client={queryClient}>
|
|
652
|
-
<Page />
|
|
653
|
-
</QueryClientProvider>
|
|
654
|
-
))
|
|
655
|
-
|
|
656
|
-
await rendered.findByText('data: null, status: idle, isPaused: false')
|
|
657
|
-
|
|
658
|
-
fireEvent.click(rendered.getByRole('button', { name: /mutate/i }))
|
|
659
|
-
|
|
660
|
-
await rendered.findByText('data: null, status: pending, isPaused: true')
|
|
661
|
-
|
|
662
|
-
// no intermediate 'pending, false' state is expected because we don't start mutating!
|
|
663
|
-
expect(states[0]).toBe('idle, false')
|
|
664
|
-
expect(states[1]).toBe('pending, true')
|
|
665
|
-
|
|
666
|
-
onlineMock.mockReturnValue(true)
|
|
667
|
-
window.dispatchEvent(new Event('online'))
|
|
668
|
-
|
|
669
|
-
await rendered.findByText('data: 1, status: success, isPaused: false')
|
|
670
|
-
|
|
671
|
-
onlineMock.mockRestore()
|
|
672
|
-
})
|
|
673
|
-
|
|
674
|
-
it('should be able to retry a mutation when online', async () => {
|
|
675
|
-
const onlineMock = mockOnlineManagerIsOnline(false)
|
|
676
|
-
|
|
677
|
-
let count = 0
|
|
678
|
-
const states: Array<CreateMutationResult<any, any, any, any>> = []
|
|
679
|
-
|
|
680
|
-
function Page() {
|
|
681
|
-
const mutation = createMutation(() => ({
|
|
682
|
-
mutationFn: async (_text: string) => {
|
|
683
|
-
await sleep(1)
|
|
684
|
-
count++
|
|
685
|
-
return count > 1
|
|
686
|
-
? Promise.resolve('data')
|
|
687
|
-
: Promise.reject(new Error('oops'))
|
|
688
|
-
},
|
|
689
|
-
retry: 1,
|
|
690
|
-
retryDelay: 5,
|
|
691
|
-
networkMode: 'offlineFirst',
|
|
692
|
-
}))
|
|
693
|
-
|
|
694
|
-
createRenderEffect(() => {
|
|
695
|
-
states.push({ ...mutation })
|
|
696
|
-
})
|
|
697
|
-
|
|
698
|
-
createEffect(() => {
|
|
699
|
-
const { mutate } = mutation
|
|
700
|
-
setActTimeout(() => {
|
|
701
|
-
window.dispatchEvent(new Event('offline'))
|
|
702
|
-
mutate('todo')
|
|
703
|
-
}, 10)
|
|
704
|
-
})
|
|
705
|
-
|
|
706
|
-
return null
|
|
707
|
-
}
|
|
708
|
-
|
|
709
|
-
render(() => (
|
|
710
|
-
<QueryClientProvider client={queryClient}>
|
|
711
|
-
<Page />
|
|
712
|
-
</QueryClientProvider>
|
|
713
|
-
))
|
|
714
|
-
|
|
715
|
-
await sleep(50)
|
|
716
|
-
|
|
717
|
-
expect(states.length).toBe(4)
|
|
718
|
-
expect(states[0]).toMatchObject({
|
|
719
|
-
isPending: false,
|
|
720
|
-
isPaused: false,
|
|
721
|
-
failureCount: 0,
|
|
722
|
-
failureReason: null,
|
|
723
|
-
})
|
|
724
|
-
expect(states[1]).toMatchObject({
|
|
725
|
-
isPending: true,
|
|
726
|
-
isPaused: false,
|
|
727
|
-
failureCount: 0,
|
|
728
|
-
failureReason: null,
|
|
729
|
-
})
|
|
730
|
-
expect(states[2]).toMatchObject({
|
|
731
|
-
isPending: true,
|
|
732
|
-
isPaused: false,
|
|
733
|
-
failureCount: 1,
|
|
734
|
-
failureReason: new Error('oops'),
|
|
735
|
-
})
|
|
736
|
-
expect(states[3]).toMatchObject({
|
|
737
|
-
isPending: true,
|
|
738
|
-
isPaused: true,
|
|
739
|
-
failureCount: 1,
|
|
740
|
-
failureReason: new Error('oops'),
|
|
741
|
-
})
|
|
742
|
-
|
|
743
|
-
onlineMock.mockRestore()
|
|
744
|
-
window.dispatchEvent(new Event('online'))
|
|
745
|
-
|
|
746
|
-
await sleep(50)
|
|
747
|
-
|
|
748
|
-
expect(states.length).toBe(6)
|
|
749
|
-
expect(states[4]).toMatchObject({
|
|
750
|
-
isPending: true,
|
|
751
|
-
isPaused: false,
|
|
752
|
-
failureCount: 1,
|
|
753
|
-
failureReason: new Error('oops'),
|
|
754
|
-
})
|
|
755
|
-
expect(states[5]).toMatchObject({
|
|
756
|
-
isPending: false,
|
|
757
|
-
isPaused: false,
|
|
758
|
-
failureCount: 0,
|
|
759
|
-
failureReason: null,
|
|
760
|
-
data: 'data',
|
|
761
|
-
})
|
|
762
|
-
})
|
|
763
|
-
|
|
764
|
-
it('should not change state if unmounted', async () => {
|
|
765
|
-
function Mutates() {
|
|
766
|
-
const mutation = createMutation(() => ({ mutationFn: () => sleep(10) }))
|
|
767
|
-
return <button onClick={() => mutation.mutate()}>mutate</button>
|
|
768
|
-
}
|
|
769
|
-
function Page() {
|
|
770
|
-
const [mounted, setMounted] = createSignal(true)
|
|
771
|
-
return (
|
|
772
|
-
<div>
|
|
773
|
-
<button onClick={() => setMounted(false)}>unmount</button>
|
|
774
|
-
{mounted() && <Mutates />}
|
|
775
|
-
</div>
|
|
776
|
-
)
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
const rendered = render(() => (
|
|
780
|
-
<QueryClientProvider client={queryClient}>
|
|
781
|
-
<Page />
|
|
782
|
-
</QueryClientProvider>
|
|
783
|
-
))
|
|
784
|
-
fireEvent.click(rendered.getByText('mutate'))
|
|
785
|
-
fireEvent.click(rendered.getByText('unmount'))
|
|
786
|
-
})
|
|
787
|
-
|
|
788
|
-
it('should be able to throw an error when throwOnError is set to true', async () => {
|
|
789
|
-
const consoleMock = vi
|
|
790
|
-
.spyOn(console, 'error')
|
|
791
|
-
.mockImplementation(() => undefined)
|
|
792
|
-
|
|
793
|
-
function Page() {
|
|
794
|
-
const mutation = createMutation<string, Error>(() => ({
|
|
795
|
-
mutationFn: () => {
|
|
796
|
-
const err = new Error('Expected mock error. All is well!')
|
|
797
|
-
err.stack = ''
|
|
798
|
-
return Promise.reject(err)
|
|
799
|
-
},
|
|
800
|
-
throwOnError: true,
|
|
801
|
-
}))
|
|
802
|
-
|
|
803
|
-
return (
|
|
804
|
-
<div>
|
|
805
|
-
<button onClick={() => mutation.mutate()}>mutate</button>
|
|
806
|
-
</div>
|
|
807
|
-
)
|
|
808
|
-
}
|
|
809
|
-
|
|
810
|
-
const rendered = render(() => (
|
|
811
|
-
<QueryClientProvider client={queryClient}>
|
|
812
|
-
<ErrorBoundary
|
|
813
|
-
fallback={() => (
|
|
814
|
-
<div>
|
|
815
|
-
<span>error</span>
|
|
816
|
-
</div>
|
|
817
|
-
)}
|
|
818
|
-
>
|
|
819
|
-
<Page />
|
|
820
|
-
</ErrorBoundary>
|
|
821
|
-
</QueryClientProvider>
|
|
822
|
-
))
|
|
823
|
-
|
|
824
|
-
fireEvent.click(rendered.getByText('mutate'))
|
|
825
|
-
|
|
826
|
-
await waitFor(() => {
|
|
827
|
-
expect(rendered.queryByText('error')).not.toBeNull()
|
|
828
|
-
})
|
|
829
|
-
|
|
830
|
-
consoleMock.mockRestore()
|
|
831
|
-
})
|
|
832
|
-
|
|
833
|
-
it('should be able to throw an error when throwOnError is a function that returns true', async () => {
|
|
834
|
-
const consoleMock = vi
|
|
835
|
-
.spyOn(console, 'error')
|
|
836
|
-
.mockImplementation(() => undefined)
|
|
837
|
-
|
|
838
|
-
let boundary = false
|
|
839
|
-
function Page() {
|
|
840
|
-
const mutation = createMutation<string, Error>(() => ({
|
|
841
|
-
mutationFn: () => {
|
|
842
|
-
const err = new Error('mock error')
|
|
843
|
-
err.stack = ''
|
|
844
|
-
return Promise.reject(err)
|
|
845
|
-
},
|
|
846
|
-
throwOnError: () => {
|
|
847
|
-
boundary = !boundary
|
|
848
|
-
return !boundary
|
|
849
|
-
},
|
|
850
|
-
}))
|
|
851
|
-
|
|
852
|
-
return (
|
|
853
|
-
<div>
|
|
854
|
-
<button onClick={() => mutation.mutate()}>mutate</button>
|
|
855
|
-
{mutation.error && mutation.error.message}
|
|
856
|
-
</div>
|
|
857
|
-
)
|
|
858
|
-
}
|
|
859
|
-
|
|
860
|
-
const rendered = render(() => (
|
|
861
|
-
<QueryClientProvider client={queryClient}>
|
|
862
|
-
<ErrorBoundary
|
|
863
|
-
fallback={() => (
|
|
864
|
-
<div>
|
|
865
|
-
<span>error boundary</span>
|
|
866
|
-
</div>
|
|
867
|
-
)}
|
|
868
|
-
>
|
|
869
|
-
<Page />
|
|
870
|
-
</ErrorBoundary>
|
|
871
|
-
</QueryClientProvider>
|
|
872
|
-
))
|
|
873
|
-
|
|
874
|
-
// first error goes to component
|
|
875
|
-
fireEvent.click(rendered.getByText('mutate'))
|
|
876
|
-
await waitFor(() => {
|
|
877
|
-
expect(rendered.queryByText('mock error')).not.toBeNull()
|
|
878
|
-
})
|
|
879
|
-
|
|
880
|
-
// second error goes to boundary
|
|
881
|
-
fireEvent.click(rendered.getByText('mutate'))
|
|
882
|
-
await waitFor(() => {
|
|
883
|
-
expect(rendered.queryByText('error boundary')).not.toBeNull()
|
|
884
|
-
})
|
|
885
|
-
|
|
886
|
-
consoleMock.mockRestore()
|
|
887
|
-
})
|
|
888
|
-
|
|
889
|
-
it('should pass meta to mutation', async () => {
|
|
890
|
-
const errorMock = vi.fn()
|
|
891
|
-
const successMock = vi.fn()
|
|
892
|
-
|
|
893
|
-
const queryClientMutationMeta = createQueryClient({
|
|
894
|
-
mutationCache: new MutationCache({
|
|
895
|
-
onSuccess: (_, __, ___, mutation) => {
|
|
896
|
-
successMock(mutation.meta?.metaSuccessMessage)
|
|
897
|
-
},
|
|
898
|
-
onError: (_, __, ___, mutation) => {
|
|
899
|
-
errorMock(mutation.meta?.metaErrorMessage)
|
|
900
|
-
},
|
|
901
|
-
}),
|
|
902
|
-
})
|
|
903
|
-
|
|
904
|
-
const metaSuccessMessage = 'mutation succeeded'
|
|
905
|
-
const metaErrorMessage = 'mutation failed'
|
|
906
|
-
|
|
907
|
-
function Page() {
|
|
908
|
-
const mutationSucceed = createMutation(() => ({
|
|
909
|
-
mutationFn: async () => '',
|
|
910
|
-
meta: { metaSuccessMessage },
|
|
911
|
-
}))
|
|
912
|
-
const mutationError = createMutation(() => ({
|
|
913
|
-
mutationFn: async () => {
|
|
914
|
-
throw new Error('')
|
|
915
|
-
},
|
|
916
|
-
meta: { metaErrorMessage },
|
|
917
|
-
}))
|
|
918
|
-
|
|
919
|
-
return (
|
|
920
|
-
<div>
|
|
921
|
-
<button onClick={() => mutationSucceed.mutate()}>succeed</button>
|
|
922
|
-
<button onClick={() => mutationError.mutate()}>error</button>
|
|
923
|
-
{mutationSucceed.isSuccess && <div>successTest</div>}
|
|
924
|
-
{mutationError.isError && <div>errorTest</div>}
|
|
925
|
-
</div>
|
|
926
|
-
)
|
|
927
|
-
}
|
|
928
|
-
|
|
929
|
-
const rendered = render(() => (
|
|
930
|
-
<QueryClientProvider client={queryClientMutationMeta}>
|
|
931
|
-
<Page />
|
|
932
|
-
</QueryClientProvider>
|
|
933
|
-
))
|
|
934
|
-
|
|
935
|
-
fireEvent.click(rendered.getByText('succeed'))
|
|
936
|
-
fireEvent.click(rendered.getByText('error'))
|
|
937
|
-
|
|
938
|
-
await waitFor(() => {
|
|
939
|
-
expect(rendered.queryByText('successTest')).not.toBeNull()
|
|
940
|
-
expect(rendered.queryByText('errorTest')).not.toBeNull()
|
|
941
|
-
})
|
|
942
|
-
|
|
943
|
-
expect(successMock).toHaveBeenCalledTimes(1)
|
|
944
|
-
expect(successMock).toHaveBeenCalledWith(metaSuccessMessage)
|
|
945
|
-
expect(errorMock).toHaveBeenCalledTimes(1)
|
|
946
|
-
expect(errorMock).toHaveBeenCalledWith(metaErrorMessage)
|
|
947
|
-
})
|
|
948
|
-
|
|
949
|
-
it('should call cache callbacks when unmounted', async () => {
|
|
950
|
-
const onSuccess = vi.fn()
|
|
951
|
-
const onSuccessMutate = vi.fn()
|
|
952
|
-
const onSettled = vi.fn()
|
|
953
|
-
const onSettledMutate = vi.fn()
|
|
954
|
-
const mutationKey = queryKey()
|
|
955
|
-
let count = 0
|
|
956
|
-
|
|
957
|
-
function Page() {
|
|
958
|
-
const [show, setShow] = createSignal(true)
|
|
959
|
-
return (
|
|
960
|
-
<div>
|
|
961
|
-
<button onClick={() => setShow(false)}>hide</button>
|
|
962
|
-
{show() && <Component />}
|
|
963
|
-
</div>
|
|
964
|
-
)
|
|
965
|
-
}
|
|
966
|
-
|
|
967
|
-
function Component() {
|
|
968
|
-
const mutation = createMutation(() => ({
|
|
969
|
-
mutationFn: async (_text: string) => {
|
|
970
|
-
count++
|
|
971
|
-
await sleep(10)
|
|
972
|
-
return count
|
|
973
|
-
},
|
|
974
|
-
mutationKey: mutationKey,
|
|
975
|
-
gcTime: 0,
|
|
976
|
-
onSuccess,
|
|
977
|
-
onSettled,
|
|
978
|
-
}))
|
|
979
|
-
|
|
980
|
-
return (
|
|
981
|
-
<div>
|
|
982
|
-
<button
|
|
983
|
-
onClick={() =>
|
|
984
|
-
mutation.mutate('todo', {
|
|
985
|
-
onSuccess: onSuccessMutate,
|
|
986
|
-
onSettled: onSettledMutate,
|
|
987
|
-
})
|
|
988
|
-
}
|
|
989
|
-
>
|
|
990
|
-
mutate
|
|
991
|
-
</button>
|
|
992
|
-
<div>
|
|
993
|
-
data: {mutation.data ?? 'null'}, status: {mutation.status},
|
|
994
|
-
isPaused: {String(mutation.isPaused)}
|
|
995
|
-
</div>
|
|
996
|
-
</div>
|
|
997
|
-
)
|
|
998
|
-
}
|
|
999
|
-
|
|
1000
|
-
const rendered = render(() => (
|
|
1001
|
-
<QueryClientProvider client={queryClient}>
|
|
1002
|
-
<Page />
|
|
1003
|
-
</QueryClientProvider>
|
|
1004
|
-
))
|
|
1005
|
-
|
|
1006
|
-
await rendered.findByText('data: null, status: idle, isPaused: false')
|
|
1007
|
-
|
|
1008
|
-
fireEvent.click(rendered.getByRole('button', { name: /mutate/i }))
|
|
1009
|
-
fireEvent.click(rendered.getByRole('button', { name: /hide/i }))
|
|
1010
|
-
|
|
1011
|
-
await waitFor(() => {
|
|
1012
|
-
expect(
|
|
1013
|
-
queryClient.getMutationCache().findAll({ mutationKey: mutationKey }),
|
|
1014
|
-
).toHaveLength(0)
|
|
1015
|
-
})
|
|
1016
|
-
|
|
1017
|
-
expect(count).toBe(1)
|
|
1018
|
-
|
|
1019
|
-
expect(onSuccess).toHaveBeenCalledTimes(1)
|
|
1020
|
-
expect(onSettled).toHaveBeenCalledTimes(1)
|
|
1021
|
-
expect(onSuccessMutate).toHaveBeenCalledTimes(0)
|
|
1022
|
-
expect(onSettledMutate).toHaveBeenCalledTimes(0)
|
|
1023
|
-
})
|
|
1024
|
-
|
|
1025
|
-
it('should call mutate callbacks only for the last observer', async () => {
|
|
1026
|
-
const onSuccess = vi.fn()
|
|
1027
|
-
const onSuccessMutate = vi.fn()
|
|
1028
|
-
const onSettled = vi.fn()
|
|
1029
|
-
const onSettledMutate = vi.fn()
|
|
1030
|
-
let count = 0
|
|
1031
|
-
|
|
1032
|
-
function Page() {
|
|
1033
|
-
const mutation = createMutation(() => ({
|
|
1034
|
-
mutationFn: async (_text: string) => {
|
|
1035
|
-
count++
|
|
1036
|
-
await sleep(10)
|
|
1037
|
-
return `result${count}`
|
|
1038
|
-
},
|
|
1039
|
-
onSuccess,
|
|
1040
|
-
onSettled,
|
|
1041
|
-
}))
|
|
1042
|
-
|
|
1043
|
-
return (
|
|
1044
|
-
<div>
|
|
1045
|
-
<button
|
|
1046
|
-
onClick={() =>
|
|
1047
|
-
mutation.mutate('todo', {
|
|
1048
|
-
onSuccess: onSuccessMutate,
|
|
1049
|
-
onSettled: onSettledMutate,
|
|
1050
|
-
})
|
|
1051
|
-
}
|
|
1052
|
-
>
|
|
1053
|
-
mutate
|
|
1054
|
-
</button>
|
|
1055
|
-
<div>
|
|
1056
|
-
data: {mutation.data ?? 'null'}, status: {mutation.status}
|
|
1057
|
-
</div>
|
|
1058
|
-
</div>
|
|
1059
|
-
)
|
|
1060
|
-
}
|
|
1061
|
-
|
|
1062
|
-
const rendered = render(() => (
|
|
1063
|
-
<QueryClientProvider client={queryClient}>
|
|
1064
|
-
<Page />
|
|
1065
|
-
</QueryClientProvider>
|
|
1066
|
-
))
|
|
1067
|
-
|
|
1068
|
-
await rendered.findByText('data: null, status: idle')
|
|
1069
|
-
|
|
1070
|
-
fireEvent.click(rendered.getByRole('button', { name: /mutate/i }))
|
|
1071
|
-
fireEvent.click(rendered.getByRole('button', { name: /mutate/i }))
|
|
1072
|
-
|
|
1073
|
-
await rendered.findByText('data: result2, status: success')
|
|
1074
|
-
|
|
1075
|
-
expect(count).toBe(2)
|
|
1076
|
-
|
|
1077
|
-
expect(onSuccess).toHaveBeenCalledTimes(2)
|
|
1078
|
-
expect(onSettled).toHaveBeenCalledTimes(2)
|
|
1079
|
-
expect(onSuccessMutate).toHaveBeenCalledTimes(1)
|
|
1080
|
-
expect(onSuccessMutate).toHaveBeenCalledWith('result2', 'todo', undefined)
|
|
1081
|
-
expect(onSettledMutate).toHaveBeenCalledTimes(1)
|
|
1082
|
-
expect(onSettledMutate).toHaveBeenCalledWith(
|
|
1083
|
-
'result2',
|
|
1084
|
-
null,
|
|
1085
|
-
'todo',
|
|
1086
|
-
undefined,
|
|
1087
|
-
)
|
|
1088
|
-
})
|
|
1089
|
-
|
|
1090
|
-
it('should go to error state if onSuccess callback errors', async () => {
|
|
1091
|
-
const error = new Error('error from onSuccess')
|
|
1092
|
-
const onError = vi.fn()
|
|
1093
|
-
|
|
1094
|
-
function Page() {
|
|
1095
|
-
const mutation = createMutation(() => ({
|
|
1096
|
-
mutationFn: async (_text: string) => {
|
|
1097
|
-
await sleep(10)
|
|
1098
|
-
return 'result'
|
|
1099
|
-
},
|
|
1100
|
-
onSuccess: () => Promise.reject(error),
|
|
1101
|
-
onError,
|
|
1102
|
-
}))
|
|
1103
|
-
|
|
1104
|
-
return (
|
|
1105
|
-
<div>
|
|
1106
|
-
<button onClick={() => mutation.mutate('todo')}>mutate</button>
|
|
1107
|
-
<div>status: {mutation.status}</div>
|
|
1108
|
-
</div>
|
|
1109
|
-
)
|
|
1110
|
-
}
|
|
1111
|
-
|
|
1112
|
-
const rendered = render(() => (
|
|
1113
|
-
<QueryClientProvider client={queryClient}>
|
|
1114
|
-
<Page />
|
|
1115
|
-
</QueryClientProvider>
|
|
1116
|
-
))
|
|
1117
|
-
|
|
1118
|
-
await rendered.findByText('status: idle')
|
|
1119
|
-
|
|
1120
|
-
rendered.getByRole('button', { name: /mutate/i }).click()
|
|
1121
|
-
|
|
1122
|
-
await rendered.findByText('status: error')
|
|
1123
|
-
|
|
1124
|
-
expect(onError).toHaveBeenCalledWith(error, 'todo', undefined)
|
|
1125
|
-
})
|
|
1126
|
-
|
|
1127
|
-
it('should go to error state if onError callback errors', async () => {
|
|
1128
|
-
const error = new Error('error from onError')
|
|
1129
|
-
const mutateFnError = new Error('mutateFnError')
|
|
1130
|
-
|
|
1131
|
-
function Page() {
|
|
1132
|
-
const mutation = createMutation(() => ({
|
|
1133
|
-
mutationFn: async (_text: string) => {
|
|
1134
|
-
await sleep(10)
|
|
1135
|
-
throw mutateFnError
|
|
1136
|
-
},
|
|
1137
|
-
onError: () => Promise.reject(error),
|
|
1138
|
-
}))
|
|
1139
|
-
|
|
1140
|
-
return (
|
|
1141
|
-
<div>
|
|
1142
|
-
<button onClick={() => mutation.mutate('todo')}>mutate</button>
|
|
1143
|
-
<div>
|
|
1144
|
-
error:{' '}
|
|
1145
|
-
{mutation.error instanceof Error ? mutation.error.message : 'null'},
|
|
1146
|
-
status: {mutation.status}
|
|
1147
|
-
</div>
|
|
1148
|
-
</div>
|
|
1149
|
-
)
|
|
1150
|
-
}
|
|
1151
|
-
|
|
1152
|
-
const rendered = render(() => (
|
|
1153
|
-
<QueryClientProvider client={queryClient}>
|
|
1154
|
-
<Page />
|
|
1155
|
-
</QueryClientProvider>
|
|
1156
|
-
))
|
|
1157
|
-
|
|
1158
|
-
await rendered.findByText('error: null, status: idle')
|
|
1159
|
-
|
|
1160
|
-
rendered.getByRole('button', { name: /mutate/i }).click()
|
|
1161
|
-
|
|
1162
|
-
await rendered.findByText('error: mutateFnError, status: error')
|
|
1163
|
-
})
|
|
1164
|
-
|
|
1165
|
-
it('should go to error state if onSettled callback errors', async () => {
|
|
1166
|
-
const error = new Error('error from onSettled')
|
|
1167
|
-
const mutateFnError = new Error('mutateFnError')
|
|
1168
|
-
const onError = vi.fn()
|
|
1169
|
-
|
|
1170
|
-
function Page() {
|
|
1171
|
-
const mutation = createMutation(() => ({
|
|
1172
|
-
mutationFn: async (_text: string) => {
|
|
1173
|
-
await sleep(10)
|
|
1174
|
-
throw mutateFnError
|
|
1175
|
-
},
|
|
1176
|
-
onSettled: () => Promise.reject(error),
|
|
1177
|
-
onError,
|
|
1178
|
-
}))
|
|
1179
|
-
|
|
1180
|
-
return (
|
|
1181
|
-
<div>
|
|
1182
|
-
<button onClick={() => mutation.mutate('todo')}>mutate</button>
|
|
1183
|
-
<div>
|
|
1184
|
-
error:{' '}
|
|
1185
|
-
{mutation.error instanceof Error ? mutation.error.message : 'null'},
|
|
1186
|
-
status: {mutation.status}
|
|
1187
|
-
</div>
|
|
1188
|
-
</div>
|
|
1189
|
-
)
|
|
1190
|
-
}
|
|
1191
|
-
|
|
1192
|
-
const rendered = render(() => (
|
|
1193
|
-
<QueryClientProvider client={queryClient}>
|
|
1194
|
-
<Page />
|
|
1195
|
-
</QueryClientProvider>
|
|
1196
|
-
))
|
|
1197
|
-
|
|
1198
|
-
await rendered.findByText('error: null, status: idle')
|
|
1199
|
-
|
|
1200
|
-
rendered.getByRole('button', { name: /mutate/i }).click()
|
|
1201
|
-
|
|
1202
|
-
await rendered.findByText('error: mutateFnError, status: error')
|
|
1203
|
-
|
|
1204
|
-
expect(onError).toHaveBeenCalledWith(mutateFnError, 'todo', undefined)
|
|
1205
|
-
})
|
|
1206
|
-
|
|
1207
|
-
it('should use provided custom queryClient', async () => {
|
|
1208
|
-
function Page() {
|
|
1209
|
-
const mutation = createMutation(
|
|
1210
|
-
() => ({
|
|
1211
|
-
mutationFn: async (text: string) => {
|
|
1212
|
-
return Promise.resolve(text)
|
|
1213
|
-
},
|
|
1214
|
-
}),
|
|
1215
|
-
() => queryClient,
|
|
1216
|
-
)
|
|
1217
|
-
|
|
1218
|
-
return (
|
|
1219
|
-
<div>
|
|
1220
|
-
<button onClick={() => mutation.mutate('custom client')}>
|
|
1221
|
-
mutate
|
|
1222
|
-
</button>
|
|
1223
|
-
<div>
|
|
1224
|
-
data: {mutation.data ?? 'null'}, status: {mutation.status}
|
|
1225
|
-
</div>
|
|
1226
|
-
</div>
|
|
1227
|
-
)
|
|
1228
|
-
}
|
|
1229
|
-
|
|
1230
|
-
const rendered = render(() => <Page></Page>)
|
|
1231
|
-
|
|
1232
|
-
await rendered.findByText('data: null, status: idle')
|
|
1233
|
-
|
|
1234
|
-
fireEvent.click(rendered.getByRole('button', { name: /mutate/i }))
|
|
1235
|
-
|
|
1236
|
-
await rendered.findByText('data: custom client, status: success')
|
|
1237
|
-
})
|
|
1238
|
-
})
|