@tanstack/solid-query 5.0.0-alpha.7 → 5.0.0-alpha.75
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/cjs/index.js → dist/cjs/index.cjs} +92 -32
- package/dist/cjs/index.cjs.map +1 -0
- package/{build → dist}/esm/index.js +92 -34
- package/dist/esm/index.js.map +1 -0
- package/dist/source/QueryClient.js +6 -0
- package/{build → dist}/source/createBaseQuery.js +49 -23
- package/{build → dist}/source/createMutation.js +1 -1
- package/{build → dist}/source/createQueries.js +13 -7
- package/{build → dist}/source/createQuery.js +3 -0
- package/{build → dist}/source/index.js +2 -1
- package/dist/types/QueryClient.d.ts +29 -0
- package/{build → dist}/types/QueryClientProvider.d.ts +3 -3
- package/{build → dist}/types/createBaseQuery.d.ts +2 -1
- package/{build → dist}/types/createInfiniteQuery.d.ts +2 -1
- package/{build → dist}/types/createMutation.d.ts +2 -1
- package/{build → dist}/types/createQueries.d.ts +11 -9
- package/dist/types/createQuery.d.ts +14 -0
- package/{build → dist}/types/index.d.ts +3 -1
- package/dist/types/types.d.ts +34 -0
- package/{build → dist}/types/useIsFetching.d.ts +2 -1
- package/{build → dist}/types/useIsMutating.d.ts +2 -1
- package/package.json +22 -26
- package/src/QueryClient.ts +84 -0
- package/src/QueryClientProvider.tsx +2 -3
- package/src/__tests__/QueryClientProvider.test.tsx +19 -3
- package/src/__tests__/createInfiniteQuery.test.tsx +83 -19
- package/src/__tests__/createMutation.test.tsx +5 -5
- package/src/__tests__/createQueries.test.tsx +6 -78
- package/src/__tests__/createQuery.test.tsx +66 -334
- package/src/__tests__/createQuery.types.test.tsx +21 -1
- package/src/__tests__/suspense.test.tsx +7 -90
- package/src/__tests__/transition.test.tsx +1 -1
- package/src/__tests__/useIsFetching.test.tsx +1 -1
- package/src/__tests__/useIsMutating.test.tsx +5 -7
- package/src/__tests__/utils.tsx +1 -1
- package/src/createBaseQuery.ts +73 -28
- package/src/createInfiniteQuery.ts +1 -1
- package/src/createMutation.ts +3 -2
- package/src/createQueries.ts +32 -14
- package/src/createQuery.ts +24 -1
- package/src/index.ts +8 -1
- package/src/types.ts +4 -2
- package/src/useIsFetching.ts +2 -1
- package/src/useIsMutating.ts +2 -1
- package/build/cjs/index.js.map +0 -1
- package/build/esm/index.js.map +0 -1
- package/build/source/__tests__/QueryClientProvider.test.jsx +0 -121
- package/build/source/__tests__/createInfiniteQuery.test.jsx +0 -1315
- package/build/source/__tests__/createMutation.test.jsx +0 -867
- package/build/source/__tests__/createQueries.test.jsx +0 -661
- package/build/source/__tests__/createQuery.test.jsx +0 -4608
- package/build/source/__tests__/createQuery.types.test.jsx +0 -135
- package/build/source/__tests__/suspense.test.jsx +0 -721
- package/build/source/__tests__/transition.test.jsx +0 -42
- package/build/source/__tests__/useIsFetching.test.jsx +0 -190
- package/build/source/__tests__/useIsMutating.test.jsx +0 -198
- package/build/source/__tests__/utils.jsx +0 -50
- package/build/types/__tests__/QueryClientProvider.test.d.ts +0 -1
- package/build/types/__tests__/createInfiniteQuery.test.d.ts +0 -1
- package/build/types/__tests__/createMutation.test.d.ts +0 -1
- package/build/types/__tests__/createQueries.test.d.ts +0 -1
- package/build/types/__tests__/createQuery.test.d.ts +0 -1
- package/build/types/__tests__/createQuery.types.test.d.ts +0 -2
- package/build/types/__tests__/suspense.test.d.ts +0 -1
- package/build/types/__tests__/transition.test.d.ts +0 -1
- package/build/types/__tests__/useIsFetching.test.d.ts +0 -1
- package/build/types/__tests__/useIsMutating.test.d.ts +0 -1
- package/build/types/__tests__/utils.d.ts +0 -21
- package/build/types/createQuery.d.ts +0 -11
- package/build/types/types.d.ts +0 -33
- package/build/umd/index.js +0 -2
- package/build/umd/index.js.map +0 -1
- package/{build → dist}/source/QueryClientProvider.jsx +1 -1
- /package/{build → dist}/source/createInfiniteQuery.js +0 -0
- /package/{build → dist}/source/setBatchUpdatesFn.js +0 -0
- /package/{build → dist}/source/types.js +0 -0
- /package/{build → dist}/source/useIsFetching.js +0 -0
- /package/{build → dist}/source/useIsMutating.js +0 -0
- /package/{build → dist}/source/utils.js +0 -0
- /package/{build → dist}/types/setBatchUpdatesFn.d.ts +0 -0
- /package/{build → dist}/types/utils.d.ts +0 -0
|
@@ -1,867 +0,0 @@
|
|
|
1
|
-
import '@testing-library/jest-dom';
|
|
2
|
-
import { createEffect, createRenderEffect, createSignal, ErrorBoundary, } from 'solid-js';
|
|
3
|
-
import { fireEvent, render, screen, waitFor } from 'solid-testing-library';
|
|
4
|
-
import { createMutation, MutationCache, QueryCache, QueryClientProvider, } from '..';
|
|
5
|
-
import { createQueryClient, mockNavigatorOnLine, queryKey, setActTimeout, sleep, } from './utils';
|
|
6
|
-
import { vi } from 'vitest';
|
|
7
|
-
describe('createMutation', () => {
|
|
8
|
-
const queryCache = new QueryCache();
|
|
9
|
-
const mutationCache = new MutationCache();
|
|
10
|
-
const queryClient = createQueryClient({ queryCache, mutationCache });
|
|
11
|
-
it('should be able to reset `data`', async () => {
|
|
12
|
-
function Page() {
|
|
13
|
-
const mutation = createMutation(() => ({
|
|
14
|
-
mutationFn: () => Promise.resolve('mutation'),
|
|
15
|
-
}));
|
|
16
|
-
return (<div>
|
|
17
|
-
<h1>{mutation.data ?? 'empty'}</h1>
|
|
18
|
-
<button onClick={() => mutation.reset()}>reset</button>
|
|
19
|
-
<button onClick={() => mutation.mutate()}>mutate</button>
|
|
20
|
-
</div>);
|
|
21
|
-
}
|
|
22
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
23
|
-
<Page />
|
|
24
|
-
</QueryClientProvider>));
|
|
25
|
-
expect(screen.getByRole('heading').textContent).toBe('empty');
|
|
26
|
-
fireEvent.click(screen.getByRole('button', { name: /mutate/i }));
|
|
27
|
-
await waitFor(() => {
|
|
28
|
-
expect(screen.getByRole('heading').textContent).toBe('mutation');
|
|
29
|
-
});
|
|
30
|
-
fireEvent.click(screen.getByRole('button', { name: /reset/i }));
|
|
31
|
-
await waitFor(() => {
|
|
32
|
-
expect(screen.getByRole('heading').textContent).toBe('empty');
|
|
33
|
-
});
|
|
34
|
-
});
|
|
35
|
-
it('should be able to reset `error`', async () => {
|
|
36
|
-
function Page() {
|
|
37
|
-
const mutation = createMutation(() => ({
|
|
38
|
-
mutationFn: () => {
|
|
39
|
-
const err = new Error('Expected mock error. All is well!');
|
|
40
|
-
err.stack = '';
|
|
41
|
-
return Promise.reject(err);
|
|
42
|
-
},
|
|
43
|
-
}));
|
|
44
|
-
return (<div>
|
|
45
|
-
{mutation.error && <h1>{mutation.error.message}</h1>}
|
|
46
|
-
<button onClick={() => mutation.reset()}>reset</button>
|
|
47
|
-
<button onClick={() => mutation.mutate()}>mutate</button>
|
|
48
|
-
</div>);
|
|
49
|
-
}
|
|
50
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
51
|
-
<Page />
|
|
52
|
-
</QueryClientProvider>));
|
|
53
|
-
await waitFor(() => {
|
|
54
|
-
expect(screen.queryByRole('heading')).toBeNull();
|
|
55
|
-
});
|
|
56
|
-
fireEvent.click(screen.getByRole('button', { name: /mutate/i }));
|
|
57
|
-
await waitFor(() => {
|
|
58
|
-
expect(screen.getByRole('heading').textContent).toBe('Expected mock error. All is well!');
|
|
59
|
-
});
|
|
60
|
-
fireEvent.click(screen.getByRole('button', { name: /reset/i }));
|
|
61
|
-
await waitFor(() => {
|
|
62
|
-
expect(screen.queryByRole('heading')).toBeNull();
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
it('should be able to call `onSuccess` and `onSettled` after each successful mutate', async () => {
|
|
66
|
-
const [count, setCount] = createSignal(0);
|
|
67
|
-
const onSuccessMock = vi.fn();
|
|
68
|
-
const onSettledMock = vi.fn();
|
|
69
|
-
function Page() {
|
|
70
|
-
const mutation = createMutation(() => ({
|
|
71
|
-
mutationFn: (vars) => Promise.resolve(vars.count),
|
|
72
|
-
onSuccess: (data) => {
|
|
73
|
-
onSuccessMock(data);
|
|
74
|
-
},
|
|
75
|
-
onSettled: (data) => {
|
|
76
|
-
onSettledMock(data);
|
|
77
|
-
},
|
|
78
|
-
}));
|
|
79
|
-
return (<div>
|
|
80
|
-
<h1>{count()}</h1>
|
|
81
|
-
<button onClick={() => {
|
|
82
|
-
setCount((c) => c + 1);
|
|
83
|
-
return mutation.mutate({ count: count() });
|
|
84
|
-
}}>
|
|
85
|
-
mutate
|
|
86
|
-
</button>
|
|
87
|
-
</div>);
|
|
88
|
-
}
|
|
89
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
90
|
-
<Page />
|
|
91
|
-
</QueryClientProvider>));
|
|
92
|
-
expect(screen.getByRole('heading').textContent).toBe('0');
|
|
93
|
-
fireEvent.click(screen.getByRole('button', { name: /mutate/i }));
|
|
94
|
-
fireEvent.click(screen.getByRole('button', { name: /mutate/i }));
|
|
95
|
-
fireEvent.click(screen.getByRole('button', { name: /mutate/i }));
|
|
96
|
-
await waitFor(() => {
|
|
97
|
-
expect(screen.getByRole('heading').textContent).toBe('3');
|
|
98
|
-
});
|
|
99
|
-
await waitFor(() => {
|
|
100
|
-
expect(onSuccessMock).toHaveBeenCalledTimes(3);
|
|
101
|
-
});
|
|
102
|
-
expect(onSuccessMock).toHaveBeenCalledWith(1);
|
|
103
|
-
expect(onSuccessMock).toHaveBeenCalledWith(2);
|
|
104
|
-
expect(onSuccessMock).toHaveBeenCalledWith(3);
|
|
105
|
-
await waitFor(() => {
|
|
106
|
-
expect(onSettledMock).toHaveBeenCalledTimes(3);
|
|
107
|
-
});
|
|
108
|
-
expect(onSettledMock).toHaveBeenCalledWith(1);
|
|
109
|
-
expect(onSettledMock).toHaveBeenCalledWith(2);
|
|
110
|
-
expect(onSettledMock).toHaveBeenCalledWith(3);
|
|
111
|
-
});
|
|
112
|
-
it('should set correct values for `failureReason` and `failureCount` on multiple mutate calls', async () => {
|
|
113
|
-
const [count, setCount] = createSignal(0);
|
|
114
|
-
const mutateFn = vi.fn();
|
|
115
|
-
mutateFn.mockImplementationOnce(() => {
|
|
116
|
-
return Promise.reject(new Error('Error test Jonas'));
|
|
117
|
-
});
|
|
118
|
-
mutateFn.mockImplementation(async (value) => {
|
|
119
|
-
await sleep(10);
|
|
120
|
-
return Promise.resolve(value);
|
|
121
|
-
});
|
|
122
|
-
function Page() {
|
|
123
|
-
const mutation = createMutation(() => ({
|
|
124
|
-
mutationFn: mutateFn,
|
|
125
|
-
}));
|
|
126
|
-
return (<div>
|
|
127
|
-
<h1>Data {mutation.data?.count}</h1>
|
|
128
|
-
<h2>Status {mutation.status}</h2>
|
|
129
|
-
<h2>Failed {mutation.failureCount} times</h2>
|
|
130
|
-
<h2>Failed because {mutation.failureReason?.message ?? 'null'}</h2>
|
|
131
|
-
<button onClick={() => {
|
|
132
|
-
setCount((c) => c + 1);
|
|
133
|
-
return mutation.mutate({ count: count() });
|
|
134
|
-
}}>
|
|
135
|
-
mutate
|
|
136
|
-
</button>
|
|
137
|
-
</div>);
|
|
138
|
-
}
|
|
139
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
140
|
-
<Page />
|
|
141
|
-
</QueryClientProvider>));
|
|
142
|
-
await waitFor(() => screen.getByText('Data'));
|
|
143
|
-
fireEvent.click(screen.getByRole('button', { name: /mutate/i }));
|
|
144
|
-
await waitFor(() => screen.getByText('Data'));
|
|
145
|
-
await waitFor(() => screen.getByText('Status error'));
|
|
146
|
-
await waitFor(() => screen.getByText('Failed 1 times'));
|
|
147
|
-
await waitFor(() => screen.getByText('Failed because Error test Jonas'));
|
|
148
|
-
fireEvent.click(screen.getByRole('button', { name: /mutate/i }));
|
|
149
|
-
await waitFor(() => screen.getByText('Status pending'));
|
|
150
|
-
await waitFor(() => screen.getByText('Status success'));
|
|
151
|
-
await waitFor(() => screen.getByText('Data 2'));
|
|
152
|
-
await waitFor(() => screen.getByText('Failed 0 times'));
|
|
153
|
-
await waitFor(() => screen.getByText('Failed because null'));
|
|
154
|
-
});
|
|
155
|
-
it('should be able to call `onError` and `onSettled` after each failed mutate', async () => {
|
|
156
|
-
const onErrorMock = vi.fn();
|
|
157
|
-
const onSettledMock = vi.fn();
|
|
158
|
-
const [count, setCount] = createSignal(0);
|
|
159
|
-
function Page() {
|
|
160
|
-
const mutation = createMutation(() => ({
|
|
161
|
-
mutationFn: (vars) => {
|
|
162
|
-
const error = new Error(`Expected mock error. All is well! ${vars.count}`);
|
|
163
|
-
error.stack = '';
|
|
164
|
-
return Promise.reject(error);
|
|
165
|
-
},
|
|
166
|
-
onError: (error) => {
|
|
167
|
-
onErrorMock(error.message);
|
|
168
|
-
},
|
|
169
|
-
onSettled: (_data, error) => {
|
|
170
|
-
onSettledMock(error?.message);
|
|
171
|
-
},
|
|
172
|
-
}));
|
|
173
|
-
return (<div>
|
|
174
|
-
<h1>{count()}</h1>
|
|
175
|
-
<button onClick={() => {
|
|
176
|
-
setCount((c) => c + 1);
|
|
177
|
-
return mutation.mutate({ count: count() });
|
|
178
|
-
}}>
|
|
179
|
-
mutate
|
|
180
|
-
</button>
|
|
181
|
-
</div>);
|
|
182
|
-
}
|
|
183
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
184
|
-
<Page />
|
|
185
|
-
</QueryClientProvider>));
|
|
186
|
-
expect(screen.getByRole('heading').textContent).toBe('0');
|
|
187
|
-
fireEvent.click(screen.getByRole('button', { name: /mutate/i }));
|
|
188
|
-
fireEvent.click(screen.getByRole('button', { name: /mutate/i }));
|
|
189
|
-
fireEvent.click(screen.getByRole('button', { name: /mutate/i }));
|
|
190
|
-
await waitFor(() => {
|
|
191
|
-
expect(screen.getByRole('heading').textContent).toBe('3');
|
|
192
|
-
});
|
|
193
|
-
await waitFor(() => {
|
|
194
|
-
expect(onErrorMock).toHaveBeenCalledTimes(3);
|
|
195
|
-
});
|
|
196
|
-
expect(onErrorMock).toHaveBeenCalledWith('Expected mock error. All is well! 1');
|
|
197
|
-
expect(onErrorMock).toHaveBeenCalledWith('Expected mock error. All is well! 2');
|
|
198
|
-
expect(onErrorMock).toHaveBeenCalledWith('Expected mock error. All is well! 3');
|
|
199
|
-
await waitFor(() => {
|
|
200
|
-
expect(onSettledMock).toHaveBeenCalledTimes(3);
|
|
201
|
-
});
|
|
202
|
-
expect(onSettledMock).toHaveBeenCalledWith('Expected mock error. All is well! 1');
|
|
203
|
-
expect(onSettledMock).toHaveBeenCalledWith('Expected mock error. All is well! 2');
|
|
204
|
-
expect(onSettledMock).toHaveBeenCalledWith('Expected mock error. All is well! 3');
|
|
205
|
-
});
|
|
206
|
-
it('should be able to override the useMutation success callbacks', async () => {
|
|
207
|
-
const callbacks = [];
|
|
208
|
-
function Page() {
|
|
209
|
-
const mutation = createMutation(() => ({
|
|
210
|
-
mutationFn: async (text) => text,
|
|
211
|
-
onSuccess: async () => {
|
|
212
|
-
callbacks.push('useMutation.onSuccess');
|
|
213
|
-
},
|
|
214
|
-
onSettled: async () => {
|
|
215
|
-
callbacks.push('useMutation.onSettled');
|
|
216
|
-
},
|
|
217
|
-
}));
|
|
218
|
-
createEffect(() => {
|
|
219
|
-
const { mutateAsync } = mutation;
|
|
220
|
-
setActTimeout(async () => {
|
|
221
|
-
try {
|
|
222
|
-
const result = await mutateAsync('todo', {
|
|
223
|
-
onSuccess: async () => {
|
|
224
|
-
callbacks.push('mutateAsync.onSuccess');
|
|
225
|
-
},
|
|
226
|
-
onSettled: async () => {
|
|
227
|
-
callbacks.push('mutateAsync.onSettled');
|
|
228
|
-
},
|
|
229
|
-
});
|
|
230
|
-
callbacks.push(`mutateAsync.result:${result}`);
|
|
231
|
-
}
|
|
232
|
-
catch { }
|
|
233
|
-
}, 10);
|
|
234
|
-
});
|
|
235
|
-
return null;
|
|
236
|
-
}
|
|
237
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
238
|
-
<Page />
|
|
239
|
-
</QueryClientProvider>));
|
|
240
|
-
await sleep(100);
|
|
241
|
-
expect(callbacks).toEqual([
|
|
242
|
-
'useMutation.onSuccess',
|
|
243
|
-
'useMutation.onSettled',
|
|
244
|
-
'mutateAsync.onSuccess',
|
|
245
|
-
'mutateAsync.onSettled',
|
|
246
|
-
'mutateAsync.result:todo',
|
|
247
|
-
]);
|
|
248
|
-
});
|
|
249
|
-
it('should be able to override the error callbacks when using mutateAsync', async () => {
|
|
250
|
-
const callbacks = [];
|
|
251
|
-
function Page() {
|
|
252
|
-
const mutation = createMutation(() => ({
|
|
253
|
-
mutationFn: async (_text) => Promise.reject(new Error('oops')),
|
|
254
|
-
onError: async () => {
|
|
255
|
-
callbacks.push('useMutation.onError');
|
|
256
|
-
},
|
|
257
|
-
onSettled: async () => {
|
|
258
|
-
callbacks.push('useMutation.onSettled');
|
|
259
|
-
},
|
|
260
|
-
}));
|
|
261
|
-
createEffect(() => {
|
|
262
|
-
const { mutateAsync } = mutation;
|
|
263
|
-
setActTimeout(async () => {
|
|
264
|
-
try {
|
|
265
|
-
await mutateAsync('todo', {
|
|
266
|
-
onError: async () => {
|
|
267
|
-
callbacks.push('mutateAsync.onError');
|
|
268
|
-
},
|
|
269
|
-
onSettled: async () => {
|
|
270
|
-
callbacks.push('mutateAsync.onSettled');
|
|
271
|
-
},
|
|
272
|
-
});
|
|
273
|
-
}
|
|
274
|
-
catch (error) {
|
|
275
|
-
callbacks.push(`mutateAsync.error:${error.message}`);
|
|
276
|
-
}
|
|
277
|
-
}, 10);
|
|
278
|
-
});
|
|
279
|
-
return null;
|
|
280
|
-
}
|
|
281
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
282
|
-
<Page />
|
|
283
|
-
</QueryClientProvider>));
|
|
284
|
-
await sleep(100);
|
|
285
|
-
expect(callbacks).toEqual([
|
|
286
|
-
'useMutation.onError',
|
|
287
|
-
'useMutation.onSettled',
|
|
288
|
-
'mutateAsync.onError',
|
|
289
|
-
'mutateAsync.onSettled',
|
|
290
|
-
'mutateAsync.error:oops',
|
|
291
|
-
]);
|
|
292
|
-
});
|
|
293
|
-
it('should be able to use mutation defaults', async () => {
|
|
294
|
-
const key = queryKey();
|
|
295
|
-
queryClient.setMutationDefaults(key, {
|
|
296
|
-
mutationFn: async (text) => {
|
|
297
|
-
await sleep(10);
|
|
298
|
-
return text;
|
|
299
|
-
},
|
|
300
|
-
});
|
|
301
|
-
const states = [];
|
|
302
|
-
function Page() {
|
|
303
|
-
const mutation = createMutation(() => ({
|
|
304
|
-
mutationKey: key,
|
|
305
|
-
}));
|
|
306
|
-
createRenderEffect(() => {
|
|
307
|
-
states.push({ ...mutation });
|
|
308
|
-
});
|
|
309
|
-
createEffect(() => {
|
|
310
|
-
const { mutate } = mutation;
|
|
311
|
-
setActTimeout(() => {
|
|
312
|
-
mutate('todo');
|
|
313
|
-
}, 10);
|
|
314
|
-
});
|
|
315
|
-
return null;
|
|
316
|
-
}
|
|
317
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
318
|
-
<Page />
|
|
319
|
-
</QueryClientProvider>));
|
|
320
|
-
await sleep(100);
|
|
321
|
-
expect(states.length).toBe(3);
|
|
322
|
-
expect(states[0]).toMatchObject({ data: undefined, isPending: false });
|
|
323
|
-
expect(states[1]).toMatchObject({ data: undefined, isPending: true });
|
|
324
|
-
expect(states[2]).toMatchObject({ data: 'todo', isPending: false });
|
|
325
|
-
});
|
|
326
|
-
it('should be able to retry a failed mutation', async () => {
|
|
327
|
-
let count = 0;
|
|
328
|
-
function Page() {
|
|
329
|
-
const mutation = createMutation(() => ({
|
|
330
|
-
mutationFn: (_text) => {
|
|
331
|
-
count++;
|
|
332
|
-
return Promise.reject(new Error('oops'));
|
|
333
|
-
},
|
|
334
|
-
retry: 1,
|
|
335
|
-
retryDelay: 5,
|
|
336
|
-
}));
|
|
337
|
-
createEffect(() => {
|
|
338
|
-
const { mutate } = mutation;
|
|
339
|
-
setActTimeout(() => {
|
|
340
|
-
mutate('todo');
|
|
341
|
-
}, 10);
|
|
342
|
-
});
|
|
343
|
-
return null;
|
|
344
|
-
}
|
|
345
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
346
|
-
<Page />
|
|
347
|
-
</QueryClientProvider>));
|
|
348
|
-
await sleep(100);
|
|
349
|
-
expect(count).toBe(2);
|
|
350
|
-
});
|
|
351
|
-
it('should not retry mutations while offline', async () => {
|
|
352
|
-
const onlineMock = mockNavigatorOnLine(false);
|
|
353
|
-
let count = 0;
|
|
354
|
-
function Page() {
|
|
355
|
-
const mutation = createMutation(() => ({
|
|
356
|
-
mutationFn: (_text) => {
|
|
357
|
-
count++;
|
|
358
|
-
return Promise.reject(new Error('oops'));
|
|
359
|
-
},
|
|
360
|
-
retry: 1,
|
|
361
|
-
retryDelay: 5,
|
|
362
|
-
}));
|
|
363
|
-
return (<div>
|
|
364
|
-
<button onClick={() => mutation.mutate('todo')}>mutate</button>
|
|
365
|
-
<div>
|
|
366
|
-
{`error: ${mutation.error instanceof Error ? mutation.error.message : 'null'}, status: ${mutation.status}, isPaused: ${String(mutation.isPaused)}`}
|
|
367
|
-
</div>
|
|
368
|
-
</div>);
|
|
369
|
-
}
|
|
370
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
371
|
-
<Page />
|
|
372
|
-
</QueryClientProvider>));
|
|
373
|
-
await waitFor(() => {
|
|
374
|
-
expect(screen.getByText('error: null, status: idle, isPaused: false')).toBeInTheDocument();
|
|
375
|
-
});
|
|
376
|
-
fireEvent.click(screen.getByRole('button', { name: /mutate/i }));
|
|
377
|
-
await waitFor(() => {
|
|
378
|
-
expect(screen.getByText('error: null, status: pending, isPaused: true')).toBeInTheDocument();
|
|
379
|
-
});
|
|
380
|
-
expect(count).toBe(0);
|
|
381
|
-
onlineMock.mockReturnValue(true);
|
|
382
|
-
window.dispatchEvent(new Event('online'));
|
|
383
|
-
await sleep(100);
|
|
384
|
-
await waitFor(() => {
|
|
385
|
-
expect(screen.getByText('error: oops, status: error, isPaused: false')).toBeInTheDocument();
|
|
386
|
-
});
|
|
387
|
-
expect(count).toBe(2);
|
|
388
|
-
onlineMock.mockRestore();
|
|
389
|
-
});
|
|
390
|
-
it('should call onMutate even if paused', async () => {
|
|
391
|
-
const onlineMock = mockNavigatorOnLine(false);
|
|
392
|
-
const onMutate = vi.fn();
|
|
393
|
-
let count = 0;
|
|
394
|
-
function Page() {
|
|
395
|
-
const mutation = createMutation(() => ({
|
|
396
|
-
mutationFn: async (_text) => {
|
|
397
|
-
count++;
|
|
398
|
-
await sleep(10);
|
|
399
|
-
return count;
|
|
400
|
-
},
|
|
401
|
-
onMutate,
|
|
402
|
-
}));
|
|
403
|
-
return (<div>
|
|
404
|
-
<button onClick={() => mutation.mutate('todo')}>mutate</button>
|
|
405
|
-
<div>
|
|
406
|
-
data: {mutation.data ?? 'null'}, status: {mutation.status},
|
|
407
|
-
isPaused: {String(mutation.isPaused)}
|
|
408
|
-
</div>
|
|
409
|
-
</div>);
|
|
410
|
-
}
|
|
411
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
412
|
-
<Page />
|
|
413
|
-
</QueryClientProvider>));
|
|
414
|
-
await screen.findByText('data: null, status: idle, isPaused: false');
|
|
415
|
-
fireEvent.click(screen.getByRole('button', { name: /mutate/i }));
|
|
416
|
-
await screen.findByText('data: null, status: pending, isPaused: true');
|
|
417
|
-
expect(onMutate).toHaveBeenCalledTimes(1);
|
|
418
|
-
expect(onMutate).toHaveBeenCalledWith('todo');
|
|
419
|
-
onlineMock.mockReturnValue(true);
|
|
420
|
-
window.dispatchEvent(new Event('online'));
|
|
421
|
-
await screen.findByText('data: 1, status: success, isPaused: false');
|
|
422
|
-
expect(onMutate).toHaveBeenCalledTimes(1);
|
|
423
|
-
expect(count).toBe(1);
|
|
424
|
-
onlineMock.mockRestore();
|
|
425
|
-
});
|
|
426
|
-
it('should optimistically go to paused state if offline', async () => {
|
|
427
|
-
const onlineMock = mockNavigatorOnLine(false);
|
|
428
|
-
let count = 0;
|
|
429
|
-
const states = [];
|
|
430
|
-
function Page() {
|
|
431
|
-
const mutation = createMutation(() => ({
|
|
432
|
-
mutationFn: async (_text) => {
|
|
433
|
-
count++;
|
|
434
|
-
await sleep(10);
|
|
435
|
-
return count;
|
|
436
|
-
},
|
|
437
|
-
}));
|
|
438
|
-
createRenderEffect(() => {
|
|
439
|
-
states.push(`${mutation.status}, ${mutation.isPaused}`);
|
|
440
|
-
});
|
|
441
|
-
return (<div>
|
|
442
|
-
<button onClick={() => mutation.mutate('todo')}>mutate</button>
|
|
443
|
-
<div>
|
|
444
|
-
data: {mutation.data ?? 'null'}, status: {mutation.status},
|
|
445
|
-
isPaused: {String(mutation.isPaused)}
|
|
446
|
-
</div>
|
|
447
|
-
</div>);
|
|
448
|
-
}
|
|
449
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
450
|
-
<Page />
|
|
451
|
-
</QueryClientProvider>));
|
|
452
|
-
await screen.findByText('data: null, status: idle, isPaused: false');
|
|
453
|
-
fireEvent.click(screen.getByRole('button', { name: /mutate/i }));
|
|
454
|
-
await screen.findByText('data: null, status: pending, isPaused: true');
|
|
455
|
-
// no intermediate 'pending, false' state is expected because we don't start mutating!
|
|
456
|
-
expect(states[0]).toBe('idle, false');
|
|
457
|
-
expect(states[1]).toBe('pending, true');
|
|
458
|
-
onlineMock.mockReturnValue(true);
|
|
459
|
-
window.dispatchEvent(new Event('online'));
|
|
460
|
-
await screen.findByText('data: 1, status: success, isPaused: false');
|
|
461
|
-
onlineMock.mockRestore();
|
|
462
|
-
});
|
|
463
|
-
it('should be able to retry a mutation when online', async () => {
|
|
464
|
-
const onlineMock = mockNavigatorOnLine(false);
|
|
465
|
-
let count = 0;
|
|
466
|
-
const states = [];
|
|
467
|
-
function Page() {
|
|
468
|
-
const mutation = createMutation(() => ({
|
|
469
|
-
mutationFn: async (_text) => {
|
|
470
|
-
await sleep(1);
|
|
471
|
-
count++;
|
|
472
|
-
return count > 1
|
|
473
|
-
? Promise.resolve('data')
|
|
474
|
-
: Promise.reject(new Error('oops'));
|
|
475
|
-
},
|
|
476
|
-
retry: 1,
|
|
477
|
-
retryDelay: 5,
|
|
478
|
-
networkMode: 'offlineFirst',
|
|
479
|
-
}));
|
|
480
|
-
createRenderEffect(() => {
|
|
481
|
-
states.push({ ...mutation });
|
|
482
|
-
});
|
|
483
|
-
createEffect(() => {
|
|
484
|
-
const { mutate } = mutation;
|
|
485
|
-
setActTimeout(() => {
|
|
486
|
-
mutate('todo');
|
|
487
|
-
}, 10);
|
|
488
|
-
});
|
|
489
|
-
return null;
|
|
490
|
-
}
|
|
491
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
492
|
-
<Page />
|
|
493
|
-
</QueryClientProvider>));
|
|
494
|
-
await sleep(50);
|
|
495
|
-
expect(states.length).toBe(4);
|
|
496
|
-
expect(states[0]).toMatchObject({
|
|
497
|
-
isPending: false,
|
|
498
|
-
isPaused: false,
|
|
499
|
-
failureCount: 0,
|
|
500
|
-
failureReason: null,
|
|
501
|
-
});
|
|
502
|
-
expect(states[1]).toMatchObject({
|
|
503
|
-
isPending: true,
|
|
504
|
-
isPaused: false,
|
|
505
|
-
failureCount: 0,
|
|
506
|
-
failureReason: null,
|
|
507
|
-
});
|
|
508
|
-
expect(states[2]).toMatchObject({
|
|
509
|
-
isPending: true,
|
|
510
|
-
isPaused: false,
|
|
511
|
-
failureCount: 1,
|
|
512
|
-
failureReason: new Error('oops'),
|
|
513
|
-
});
|
|
514
|
-
expect(states[3]).toMatchObject({
|
|
515
|
-
isPending: true,
|
|
516
|
-
isPaused: true,
|
|
517
|
-
failureCount: 1,
|
|
518
|
-
failureReason: new Error('oops'),
|
|
519
|
-
});
|
|
520
|
-
onlineMock.mockReturnValue(true);
|
|
521
|
-
window.dispatchEvent(new Event('online'));
|
|
522
|
-
await sleep(50);
|
|
523
|
-
expect(states.length).toBe(6);
|
|
524
|
-
expect(states[4]).toMatchObject({
|
|
525
|
-
isPending: true,
|
|
526
|
-
isPaused: false,
|
|
527
|
-
failureCount: 1,
|
|
528
|
-
failureReason: new Error('oops'),
|
|
529
|
-
});
|
|
530
|
-
expect(states[5]).toMatchObject({
|
|
531
|
-
isPending: false,
|
|
532
|
-
isPaused: false,
|
|
533
|
-
failureCount: 0,
|
|
534
|
-
failureReason: null,
|
|
535
|
-
data: 'data',
|
|
536
|
-
});
|
|
537
|
-
onlineMock.mockRestore();
|
|
538
|
-
});
|
|
539
|
-
it('should not change state if unmounted', async () => {
|
|
540
|
-
function Mutates() {
|
|
541
|
-
const mutation = createMutation(() => ({ mutationFn: () => sleep(10) }));
|
|
542
|
-
return <button onClick={() => mutation.mutate()}>mutate</button>;
|
|
543
|
-
}
|
|
544
|
-
function Page() {
|
|
545
|
-
const [mounted, setMounted] = createSignal(true);
|
|
546
|
-
return (<div>
|
|
547
|
-
<button onClick={() => setMounted(false)}>unmount</button>
|
|
548
|
-
{mounted() && <Mutates />}
|
|
549
|
-
</div>);
|
|
550
|
-
}
|
|
551
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
552
|
-
<Page />
|
|
553
|
-
</QueryClientProvider>));
|
|
554
|
-
fireEvent.click(screen.getByText('mutate'));
|
|
555
|
-
fireEvent.click(screen.getByText('unmount'));
|
|
556
|
-
});
|
|
557
|
-
it('should be able to throw an error when throwErrors is set to true', async () => {
|
|
558
|
-
function Page() {
|
|
559
|
-
const mutation = createMutation(() => ({
|
|
560
|
-
mutationFn: () => {
|
|
561
|
-
const err = new Error('Expected mock error. All is well!');
|
|
562
|
-
err.stack = '';
|
|
563
|
-
return Promise.reject(err);
|
|
564
|
-
},
|
|
565
|
-
throwErrors: true,
|
|
566
|
-
}));
|
|
567
|
-
return (<div>
|
|
568
|
-
<button onClick={() => mutation.mutate()}>mutate</button>
|
|
569
|
-
</div>);
|
|
570
|
-
}
|
|
571
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
572
|
-
<ErrorBoundary fallback={() => (<div>
|
|
573
|
-
<span>error</span>
|
|
574
|
-
</div>)}>
|
|
575
|
-
<Page />
|
|
576
|
-
</ErrorBoundary>
|
|
577
|
-
</QueryClientProvider>));
|
|
578
|
-
fireEvent.click(screen.getByText('mutate'));
|
|
579
|
-
await waitFor(() => {
|
|
580
|
-
expect(screen.queryByText('error')).not.toBeNull();
|
|
581
|
-
});
|
|
582
|
-
});
|
|
583
|
-
it('should be able to throw an error when throwErrors is a function that returns true', async () => {
|
|
584
|
-
let boundary = false;
|
|
585
|
-
function Page() {
|
|
586
|
-
const mutation = createMutation(() => ({
|
|
587
|
-
mutationFn: () => {
|
|
588
|
-
const err = new Error('mock error');
|
|
589
|
-
err.stack = '';
|
|
590
|
-
return Promise.reject(err);
|
|
591
|
-
},
|
|
592
|
-
throwErrors: () => {
|
|
593
|
-
boundary = !boundary;
|
|
594
|
-
return !boundary;
|
|
595
|
-
},
|
|
596
|
-
}));
|
|
597
|
-
return (<div>
|
|
598
|
-
<button onClick={() => mutation.mutate()}>mutate</button>
|
|
599
|
-
{mutation.error && mutation.error.message}
|
|
600
|
-
</div>);
|
|
601
|
-
}
|
|
602
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
603
|
-
<ErrorBoundary fallback={() => (<div>
|
|
604
|
-
<span>error boundary</span>
|
|
605
|
-
</div>)}>
|
|
606
|
-
<Page />
|
|
607
|
-
</ErrorBoundary>
|
|
608
|
-
</QueryClientProvider>));
|
|
609
|
-
// first error goes to component
|
|
610
|
-
fireEvent.click(screen.getByText('mutate'));
|
|
611
|
-
await waitFor(() => {
|
|
612
|
-
expect(screen.queryByText('mock error')).not.toBeNull();
|
|
613
|
-
});
|
|
614
|
-
// second error goes to boundary
|
|
615
|
-
fireEvent.click(screen.getByText('mutate'));
|
|
616
|
-
await waitFor(() => {
|
|
617
|
-
expect(screen.queryByText('error boundary')).not.toBeNull();
|
|
618
|
-
});
|
|
619
|
-
});
|
|
620
|
-
it('should pass meta to mutation', async () => {
|
|
621
|
-
const errorMock = vi.fn();
|
|
622
|
-
const successMock = vi.fn();
|
|
623
|
-
const queryClientMutationMeta = createQueryClient({
|
|
624
|
-
mutationCache: new MutationCache({
|
|
625
|
-
onSuccess: (_, __, ___, mutation) => {
|
|
626
|
-
successMock(mutation.meta?.metaSuccessMessage);
|
|
627
|
-
},
|
|
628
|
-
onError: (_, __, ___, mutation) => {
|
|
629
|
-
errorMock(mutation.meta?.metaErrorMessage);
|
|
630
|
-
},
|
|
631
|
-
}),
|
|
632
|
-
});
|
|
633
|
-
const metaSuccessMessage = 'mutation succeeded';
|
|
634
|
-
const metaErrorMessage = 'mutation failed';
|
|
635
|
-
function Page() {
|
|
636
|
-
const mutationSucceed = createMutation(() => ({
|
|
637
|
-
mutationFn: async () => '',
|
|
638
|
-
meta: { metaSuccessMessage },
|
|
639
|
-
}));
|
|
640
|
-
const mutationError = createMutation(() => ({
|
|
641
|
-
mutationFn: async () => {
|
|
642
|
-
throw new Error('');
|
|
643
|
-
},
|
|
644
|
-
meta: { metaErrorMessage },
|
|
645
|
-
}));
|
|
646
|
-
return (<div>
|
|
647
|
-
<button onClick={() => mutationSucceed.mutate()}>succeed</button>
|
|
648
|
-
<button onClick={() => mutationError.mutate()}>error</button>
|
|
649
|
-
{mutationSucceed.isSuccess && <div>successTest</div>}
|
|
650
|
-
{mutationError.isError && <div>errorTest</div>}
|
|
651
|
-
</div>);
|
|
652
|
-
}
|
|
653
|
-
render(() => (<QueryClientProvider client={queryClientMutationMeta}>
|
|
654
|
-
<Page />
|
|
655
|
-
</QueryClientProvider>));
|
|
656
|
-
fireEvent.click(screen.getByText('succeed'));
|
|
657
|
-
fireEvent.click(screen.getByText('error'));
|
|
658
|
-
await waitFor(() => {
|
|
659
|
-
expect(screen.queryByText('successTest')).not.toBeNull();
|
|
660
|
-
expect(screen.queryByText('errorTest')).not.toBeNull();
|
|
661
|
-
});
|
|
662
|
-
expect(successMock).toHaveBeenCalledTimes(1);
|
|
663
|
-
expect(successMock).toHaveBeenCalledWith(metaSuccessMessage);
|
|
664
|
-
expect(errorMock).toHaveBeenCalledTimes(1);
|
|
665
|
-
expect(errorMock).toHaveBeenCalledWith(metaErrorMessage);
|
|
666
|
-
});
|
|
667
|
-
it('should call cache callbacks when unmounted', async () => {
|
|
668
|
-
const onSuccess = vi.fn();
|
|
669
|
-
const onSuccessMutate = vi.fn();
|
|
670
|
-
const onSettled = vi.fn();
|
|
671
|
-
const onSettledMutate = vi.fn();
|
|
672
|
-
const mutationKey = queryKey();
|
|
673
|
-
let count = 0;
|
|
674
|
-
function Page() {
|
|
675
|
-
const [show, setShow] = createSignal(true);
|
|
676
|
-
return (<div>
|
|
677
|
-
<button onClick={() => setShow(false)}>hide</button>
|
|
678
|
-
{show() && <Component />}
|
|
679
|
-
</div>);
|
|
680
|
-
}
|
|
681
|
-
function Component() {
|
|
682
|
-
const mutation = createMutation(() => ({
|
|
683
|
-
mutationFn: async (_text) => {
|
|
684
|
-
count++;
|
|
685
|
-
await sleep(10);
|
|
686
|
-
return count;
|
|
687
|
-
},
|
|
688
|
-
mutationKey: mutationKey,
|
|
689
|
-
gcTime: 0,
|
|
690
|
-
onSuccess,
|
|
691
|
-
onSettled,
|
|
692
|
-
}));
|
|
693
|
-
return (<div>
|
|
694
|
-
<button onClick={() => mutation.mutate('todo', {
|
|
695
|
-
onSuccess: onSuccessMutate,
|
|
696
|
-
onSettled: onSettledMutate,
|
|
697
|
-
})}>
|
|
698
|
-
mutate
|
|
699
|
-
</button>
|
|
700
|
-
<div>
|
|
701
|
-
data: {mutation.data ?? 'null'}, status: {mutation.status},
|
|
702
|
-
isPaused: {String(mutation.isPaused)}
|
|
703
|
-
</div>
|
|
704
|
-
</div>);
|
|
705
|
-
}
|
|
706
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
707
|
-
<Page />
|
|
708
|
-
</QueryClientProvider>));
|
|
709
|
-
await screen.findByText('data: null, status: idle, isPaused: false');
|
|
710
|
-
fireEvent.click(screen.getByRole('button', { name: /mutate/i }));
|
|
711
|
-
fireEvent.click(screen.getByRole('button', { name: /hide/i }));
|
|
712
|
-
await waitFor(() => {
|
|
713
|
-
expect(queryClient.getMutationCache().findAll({ mutationKey: mutationKey })).toHaveLength(0);
|
|
714
|
-
});
|
|
715
|
-
expect(count).toBe(1);
|
|
716
|
-
expect(onSuccess).toHaveBeenCalledTimes(1);
|
|
717
|
-
expect(onSettled).toHaveBeenCalledTimes(1);
|
|
718
|
-
expect(onSuccessMutate).toHaveBeenCalledTimes(0);
|
|
719
|
-
expect(onSettledMutate).toHaveBeenCalledTimes(0);
|
|
720
|
-
});
|
|
721
|
-
it('should call mutate callbacks only for the last observer', async () => {
|
|
722
|
-
const onSuccess = vi.fn();
|
|
723
|
-
const onSuccessMutate = vi.fn();
|
|
724
|
-
const onSettled = vi.fn();
|
|
725
|
-
const onSettledMutate = vi.fn();
|
|
726
|
-
let count = 0;
|
|
727
|
-
function Page() {
|
|
728
|
-
const mutation = createMutation(() => ({
|
|
729
|
-
mutationFn: async (_text) => {
|
|
730
|
-
count++;
|
|
731
|
-
await sleep(10);
|
|
732
|
-
return `result${count}`;
|
|
733
|
-
},
|
|
734
|
-
onSuccess,
|
|
735
|
-
onSettled,
|
|
736
|
-
}));
|
|
737
|
-
return (<div>
|
|
738
|
-
<button onClick={() => mutation.mutate('todo', {
|
|
739
|
-
onSuccess: onSuccessMutate,
|
|
740
|
-
onSettled: onSettledMutate,
|
|
741
|
-
})}>
|
|
742
|
-
mutate
|
|
743
|
-
</button>
|
|
744
|
-
<div>
|
|
745
|
-
data: {mutation.data ?? 'null'}, status: {mutation.status}
|
|
746
|
-
</div>
|
|
747
|
-
</div>);
|
|
748
|
-
}
|
|
749
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
750
|
-
<Page />
|
|
751
|
-
</QueryClientProvider>));
|
|
752
|
-
await screen.findByText('data: null, status: idle');
|
|
753
|
-
fireEvent.click(screen.getByRole('button', { name: /mutate/i }));
|
|
754
|
-
fireEvent.click(screen.getByRole('button', { name: /mutate/i }));
|
|
755
|
-
await screen.findByText('data: result2, status: success');
|
|
756
|
-
expect(count).toBe(2);
|
|
757
|
-
expect(onSuccess).toHaveBeenCalledTimes(2);
|
|
758
|
-
expect(onSettled).toHaveBeenCalledTimes(2);
|
|
759
|
-
expect(onSuccessMutate).toHaveBeenCalledTimes(1);
|
|
760
|
-
expect(onSuccessMutate).toHaveBeenCalledWith('result2', 'todo', undefined);
|
|
761
|
-
expect(onSettledMutate).toHaveBeenCalledTimes(1);
|
|
762
|
-
expect(onSettledMutate).toHaveBeenCalledWith('result2', null, 'todo', undefined);
|
|
763
|
-
});
|
|
764
|
-
it('should go to error state if onSuccess callback errors', async () => {
|
|
765
|
-
const error = new Error('error from onSuccess');
|
|
766
|
-
const onError = vi.fn();
|
|
767
|
-
function Page() {
|
|
768
|
-
const mutation = createMutation(() => ({
|
|
769
|
-
mutationFn: async (_text) => {
|
|
770
|
-
await sleep(10);
|
|
771
|
-
return 'result';
|
|
772
|
-
},
|
|
773
|
-
onSuccess: () => Promise.reject(error),
|
|
774
|
-
onError,
|
|
775
|
-
}));
|
|
776
|
-
return (<div>
|
|
777
|
-
<button onClick={() => mutation.mutate('todo')}>mutate</button>
|
|
778
|
-
<div>status: {mutation.status}</div>
|
|
779
|
-
</div>);
|
|
780
|
-
}
|
|
781
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
782
|
-
<Page />
|
|
783
|
-
</QueryClientProvider>));
|
|
784
|
-
await screen.findByText('status: idle');
|
|
785
|
-
screen.getByRole('button', { name: /mutate/i }).click();
|
|
786
|
-
await screen.findByText('status: error');
|
|
787
|
-
expect(onError).toHaveBeenCalledWith(error, 'todo', undefined);
|
|
788
|
-
});
|
|
789
|
-
it('should go to error state if onError callback errors', async () => {
|
|
790
|
-
const error = new Error('error from onError');
|
|
791
|
-
const mutateFnError = new Error('mutateFnError');
|
|
792
|
-
function Page() {
|
|
793
|
-
const mutation = createMutation(() => ({
|
|
794
|
-
mutationFn: async (_text) => {
|
|
795
|
-
await sleep(10);
|
|
796
|
-
throw mutateFnError;
|
|
797
|
-
},
|
|
798
|
-
onError: () => Promise.reject(error),
|
|
799
|
-
}));
|
|
800
|
-
return (<div>
|
|
801
|
-
<button onClick={() => mutation.mutate('todo')}>mutate</button>
|
|
802
|
-
<div>
|
|
803
|
-
error:{' '}
|
|
804
|
-
{mutation.error instanceof Error ? mutation.error.message : 'null'},
|
|
805
|
-
status: {mutation.status}
|
|
806
|
-
</div>
|
|
807
|
-
</div>);
|
|
808
|
-
}
|
|
809
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
810
|
-
<Page />
|
|
811
|
-
</QueryClientProvider>));
|
|
812
|
-
await screen.findByText('error: null, status: idle');
|
|
813
|
-
screen.getByRole('button', { name: /mutate/i }).click();
|
|
814
|
-
await screen.findByText('error: mutateFnError, status: error');
|
|
815
|
-
});
|
|
816
|
-
it('should go to error state if onSettled callback errors', async () => {
|
|
817
|
-
const error = new Error('error from onSettled');
|
|
818
|
-
const mutateFnError = new Error('mutateFnError');
|
|
819
|
-
const onError = vi.fn();
|
|
820
|
-
function Page() {
|
|
821
|
-
const mutation = createMutation(() => ({
|
|
822
|
-
mutationFn: async (_text) => {
|
|
823
|
-
await sleep(10);
|
|
824
|
-
throw mutateFnError;
|
|
825
|
-
},
|
|
826
|
-
onSettled: () => Promise.reject(error),
|
|
827
|
-
onError,
|
|
828
|
-
}));
|
|
829
|
-
return (<div>
|
|
830
|
-
<button onClick={() => mutation.mutate('todo')}>mutate</button>
|
|
831
|
-
<div>
|
|
832
|
-
error:{' '}
|
|
833
|
-
{mutation.error instanceof Error ? mutation.error.message : 'null'},
|
|
834
|
-
status: {mutation.status}
|
|
835
|
-
</div>
|
|
836
|
-
</div>);
|
|
837
|
-
}
|
|
838
|
-
render(() => (<QueryClientProvider client={queryClient}>
|
|
839
|
-
<Page />
|
|
840
|
-
</QueryClientProvider>));
|
|
841
|
-
await screen.findByText('error: null, status: idle');
|
|
842
|
-
screen.getByRole('button', { name: /mutate/i }).click();
|
|
843
|
-
await screen.findByText('error: mutateFnError, status: error');
|
|
844
|
-
expect(onError).toHaveBeenCalledWith(mutateFnError, 'todo', undefined);
|
|
845
|
-
});
|
|
846
|
-
it('should use provided custom queryClient', async () => {
|
|
847
|
-
function Page() {
|
|
848
|
-
const mutation = createMutation(() => ({
|
|
849
|
-
mutationFn: async (text) => {
|
|
850
|
-
return Promise.resolve(text);
|
|
851
|
-
},
|
|
852
|
-
}), () => queryClient);
|
|
853
|
-
return (<div>
|
|
854
|
-
<button onClick={() => mutation.mutate('custom client')}>
|
|
855
|
-
mutate
|
|
856
|
-
</button>
|
|
857
|
-
<div>
|
|
858
|
-
data: {mutation.data ?? 'null'}, status: {mutation.status}
|
|
859
|
-
</div>
|
|
860
|
-
</div>);
|
|
861
|
-
}
|
|
862
|
-
render(() => <Page></Page>);
|
|
863
|
-
await screen.findByText('data: null, status: idle');
|
|
864
|
-
fireEvent.click(screen.getByRole('button', { name: /mutate/i }));
|
|
865
|
-
await screen.findByText('data: custom client, status: success');
|
|
866
|
-
});
|
|
867
|
-
});
|