@tanstack/query-core 4.0.0
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/focusManager.js +101 -0
- package/build/cjs/focusManager.js.map +1 -0
- package/build/cjs/hydration.js +112 -0
- package/build/cjs/hydration.js.map +1 -0
- package/build/cjs/index.js +51 -0
- package/build/cjs/index.js.map +1 -0
- package/build/cjs/infiniteQueryBehavior.js +160 -0
- package/build/cjs/infiniteQueryBehavior.js.map +1 -0
- package/build/cjs/infiniteQueryObserver.js +92 -0
- package/build/cjs/infiniteQueryObserver.js.map +1 -0
- package/build/cjs/logger.js +18 -0
- package/build/cjs/logger.js.map +1 -0
- package/build/cjs/mutation.js +258 -0
- package/build/cjs/mutation.js.map +1 -0
- package/build/cjs/mutationCache.js +99 -0
- package/build/cjs/mutationCache.js.map +1 -0
- package/build/cjs/mutationObserver.js +130 -0
- package/build/cjs/mutationObserver.js.map +1 -0
- package/build/cjs/notifyManager.js +114 -0
- package/build/cjs/notifyManager.js.map +1 -0
- package/build/cjs/onlineManager.js +100 -0
- package/build/cjs/onlineManager.js.map +1 -0
- package/build/cjs/queriesObserver.js +170 -0
- package/build/cjs/queriesObserver.js.map +1 -0
- package/build/cjs/query.js +474 -0
- package/build/cjs/query.js.map +1 -0
- package/build/cjs/queryCache.js +140 -0
- package/build/cjs/queryCache.js.map +1 -0
- package/build/cjs/queryClient.js +357 -0
- package/build/cjs/queryClient.js.map +1 -0
- package/build/cjs/queryObserver.js +521 -0
- package/build/cjs/queryObserver.js.map +1 -0
- package/build/cjs/removable.js +47 -0
- package/build/cjs/removable.js.map +1 -0
- package/build/cjs/retryer.js +177 -0
- package/build/cjs/retryer.js.map +1 -0
- package/build/cjs/subscribable.js +43 -0
- package/build/cjs/subscribable.js.map +1 -0
- package/build/cjs/utils.js +356 -0
- package/build/cjs/utils.js.map +1 -0
- package/build/esm/index.js +3077 -0
- package/build/esm/index.js.map +1 -0
- package/build/stats-html.html +2689 -0
- package/build/umd/index.development.js +3106 -0
- package/build/umd/index.development.js.map +1 -0
- package/build/umd/index.production.js +12 -0
- package/build/umd/index.production.js.map +1 -0
- package/package.json +25 -0
- package/src/focusManager.ts +89 -0
- package/src/hydration.ts +164 -0
- package/src/index.ts +35 -0
- package/src/infiniteQueryBehavior.ts +214 -0
- package/src/infiniteQueryObserver.ts +159 -0
- package/src/logger.native.ts +11 -0
- package/src/logger.ts +9 -0
- package/src/mutation.ts +349 -0
- package/src/mutationCache.ts +157 -0
- package/src/mutationObserver.ts +195 -0
- package/src/notifyManager.ts +96 -0
- package/src/onlineManager.ts +89 -0
- package/src/queriesObserver.ts +211 -0
- package/src/query.ts +612 -0
- package/src/queryCache.ts +206 -0
- package/src/queryClient.ts +716 -0
- package/src/queryObserver.ts +748 -0
- package/src/removable.ts +37 -0
- package/src/retryer.ts +215 -0
- package/src/subscribable.ts +33 -0
- package/src/tests/focusManager.test.tsx +155 -0
- package/src/tests/hydration.test.tsx +429 -0
- package/src/tests/infiniteQueryBehavior.test.tsx +124 -0
- package/src/tests/infiniteQueryObserver.test.tsx +64 -0
- package/src/tests/mutationCache.test.tsx +260 -0
- package/src/tests/mutationObserver.test.tsx +75 -0
- package/src/tests/mutations.test.tsx +363 -0
- package/src/tests/notifyManager.test.tsx +51 -0
- package/src/tests/onlineManager.test.tsx +148 -0
- package/src/tests/queriesObserver.test.tsx +330 -0
- package/src/tests/query.test.tsx +888 -0
- package/src/tests/queryCache.test.tsx +236 -0
- package/src/tests/queryClient.test.tsx +1435 -0
- package/src/tests/queryObserver.test.tsx +802 -0
- package/src/tests/utils.test.tsx +360 -0
- package/src/types.ts +705 -0
- package/src/utils.ts +435 -0
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import { waitFor } from '@testing-library/react'
|
|
2
|
+
import {
|
|
3
|
+
queryKey,
|
|
4
|
+
sleep,
|
|
5
|
+
executeMutation,
|
|
6
|
+
createQueryClient,
|
|
7
|
+
} from '../../../../tests/utils'
|
|
8
|
+
import { MutationCache, MutationObserver } from '..'
|
|
9
|
+
|
|
10
|
+
describe('mutationCache', () => {
|
|
11
|
+
describe('MutationCacheConfig.onError', () => {
|
|
12
|
+
test('should be called when a mutation errors', async () => {
|
|
13
|
+
const key = queryKey()
|
|
14
|
+
const onError = jest.fn()
|
|
15
|
+
const testCache = new MutationCache({ onError })
|
|
16
|
+
const testClient = createQueryClient({ mutationCache: testCache })
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
await executeMutation(testClient, {
|
|
20
|
+
mutationKey: key,
|
|
21
|
+
variables: 'vars',
|
|
22
|
+
mutationFn: () => Promise.reject('error'),
|
|
23
|
+
onMutate: () => 'context',
|
|
24
|
+
})
|
|
25
|
+
} catch {}
|
|
26
|
+
|
|
27
|
+
const mutation = testCache.getAll()[0]
|
|
28
|
+
expect(onError).toHaveBeenCalledWith('error', 'vars', 'context', mutation)
|
|
29
|
+
})
|
|
30
|
+
})
|
|
31
|
+
describe('MutationCacheConfig.onSuccess', () => {
|
|
32
|
+
test('should be called when a mutation is successful', async () => {
|
|
33
|
+
const key = queryKey()
|
|
34
|
+
const onSuccess = jest.fn()
|
|
35
|
+
const testCache = new MutationCache({ onSuccess })
|
|
36
|
+
const testClient = createQueryClient({ mutationCache: testCache })
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
await executeMutation(testClient, {
|
|
40
|
+
mutationKey: key,
|
|
41
|
+
variables: 'vars',
|
|
42
|
+
mutationFn: () => Promise.resolve({ data: 5 }),
|
|
43
|
+
onMutate: () => 'context',
|
|
44
|
+
})
|
|
45
|
+
} catch {}
|
|
46
|
+
|
|
47
|
+
const mutation = testCache.getAll()[0]
|
|
48
|
+
expect(onSuccess).toHaveBeenCalledWith(
|
|
49
|
+
{ data: 5 },
|
|
50
|
+
'vars',
|
|
51
|
+
'context',
|
|
52
|
+
mutation,
|
|
53
|
+
)
|
|
54
|
+
})
|
|
55
|
+
})
|
|
56
|
+
describe('MutationCacheConfig.onMutate', () => {
|
|
57
|
+
test('should be called before a mutation executes', async () => {
|
|
58
|
+
const key = queryKey()
|
|
59
|
+
const onMutate = jest.fn()
|
|
60
|
+
const testCache = new MutationCache({ onMutate })
|
|
61
|
+
const testClient = createQueryClient({ mutationCache: testCache })
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
await executeMutation(testClient, {
|
|
65
|
+
mutationKey: key,
|
|
66
|
+
variables: 'vars',
|
|
67
|
+
mutationFn: () => Promise.resolve({ data: 5 }),
|
|
68
|
+
onMutate: () => 'context',
|
|
69
|
+
})
|
|
70
|
+
} catch {}
|
|
71
|
+
|
|
72
|
+
const mutation = testCache.getAll()[0]
|
|
73
|
+
expect(onMutate).toHaveBeenCalledWith('vars', mutation)
|
|
74
|
+
})
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
describe('find', () => {
|
|
78
|
+
test('should filter correctly', async () => {
|
|
79
|
+
const testCache = new MutationCache()
|
|
80
|
+
const testClient = createQueryClient({ mutationCache: testCache })
|
|
81
|
+
const key = ['mutation', 'vars']
|
|
82
|
+
await executeMutation(testClient, {
|
|
83
|
+
mutationKey: key,
|
|
84
|
+
variables: 'vars',
|
|
85
|
+
mutationFn: () => Promise.resolve(),
|
|
86
|
+
})
|
|
87
|
+
const [mutation] = testCache.getAll()
|
|
88
|
+
expect(testCache.find({ mutationKey: key })).toEqual(mutation)
|
|
89
|
+
expect(
|
|
90
|
+
testCache.find({ mutationKey: ['mutation'], exact: false }),
|
|
91
|
+
).toEqual(mutation)
|
|
92
|
+
expect(testCache.find({ mutationKey: ['unknown'] })).toEqual(undefined)
|
|
93
|
+
expect(
|
|
94
|
+
testCache.find({ predicate: (m) => m.options.variables === 'vars' }),
|
|
95
|
+
).toEqual(mutation)
|
|
96
|
+
})
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
describe('findAll', () => {
|
|
100
|
+
test('should filter correctly', async () => {
|
|
101
|
+
const testCache = new MutationCache()
|
|
102
|
+
const testClient = createQueryClient({ mutationCache: testCache })
|
|
103
|
+
await executeMutation(testClient, {
|
|
104
|
+
mutationKey: ['a', 1],
|
|
105
|
+
variables: 1,
|
|
106
|
+
mutationFn: () => Promise.resolve(),
|
|
107
|
+
})
|
|
108
|
+
await executeMutation(testClient, {
|
|
109
|
+
mutationKey: ['a', 2],
|
|
110
|
+
variables: 2,
|
|
111
|
+
mutationFn: () => Promise.resolve(),
|
|
112
|
+
})
|
|
113
|
+
await executeMutation(testClient, {
|
|
114
|
+
mutationKey: ['b'],
|
|
115
|
+
mutationFn: () => Promise.resolve(),
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
const [mutation1, mutation2] = testCache.getAll()
|
|
119
|
+
expect(
|
|
120
|
+
testCache.findAll({ mutationKey: ['a'], exact: false }),
|
|
121
|
+
).toHaveLength(2)
|
|
122
|
+
expect(testCache.find({ mutationKey: ['a', 1] })).toEqual(mutation1)
|
|
123
|
+
expect(testCache.findAll({ mutationKey: ['unknown'] })).toEqual([])
|
|
124
|
+
expect(
|
|
125
|
+
testCache.findAll({ predicate: (m) => m.options.variables === 2 }),
|
|
126
|
+
).toEqual([mutation2])
|
|
127
|
+
})
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
describe('garbage collection', () => {
|
|
131
|
+
test('should remove unused mutations after cacheTime has elapsed', async () => {
|
|
132
|
+
const testCache = new MutationCache()
|
|
133
|
+
const testClient = createQueryClient({ mutationCache: testCache })
|
|
134
|
+
const onSuccess = jest.fn()
|
|
135
|
+
await executeMutation(testClient, {
|
|
136
|
+
mutationKey: ['a', 1],
|
|
137
|
+
variables: 1,
|
|
138
|
+
cacheTime: 10,
|
|
139
|
+
mutationFn: () => Promise.resolve(),
|
|
140
|
+
onSuccess,
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
expect(testCache.getAll()).toHaveLength(1)
|
|
144
|
+
await sleep(10)
|
|
145
|
+
await waitFor(() => {
|
|
146
|
+
expect(testCache.getAll()).toHaveLength(0)
|
|
147
|
+
})
|
|
148
|
+
expect(onSuccess).toHaveBeenCalledTimes(1)
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
test('should not remove mutations if there are active observers', async () => {
|
|
152
|
+
const queryClient = createQueryClient()
|
|
153
|
+
const observer = new MutationObserver(queryClient, {
|
|
154
|
+
variables: 1,
|
|
155
|
+
cacheTime: 10,
|
|
156
|
+
mutationFn: () => Promise.resolve(),
|
|
157
|
+
})
|
|
158
|
+
const unsubscribe = observer.subscribe(() => undefined)
|
|
159
|
+
|
|
160
|
+
expect(queryClient.getMutationCache().getAll()).toHaveLength(0)
|
|
161
|
+
observer.mutate(1)
|
|
162
|
+
expect(queryClient.getMutationCache().getAll()).toHaveLength(1)
|
|
163
|
+
await sleep(10)
|
|
164
|
+
expect(queryClient.getMutationCache().getAll()).toHaveLength(1)
|
|
165
|
+
unsubscribe()
|
|
166
|
+
expect(queryClient.getMutationCache().getAll()).toHaveLength(1)
|
|
167
|
+
await sleep(10)
|
|
168
|
+
await waitFor(() => {
|
|
169
|
+
expect(queryClient.getMutationCache().getAll()).toHaveLength(0)
|
|
170
|
+
})
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
test('should only remove when the last observer unsubscribes', async () => {
|
|
174
|
+
const queryClient = createQueryClient()
|
|
175
|
+
const observer1 = new MutationObserver(queryClient, {
|
|
176
|
+
variables: 1,
|
|
177
|
+
cacheTime: 10,
|
|
178
|
+
mutationFn: async () => {
|
|
179
|
+
await sleep(10)
|
|
180
|
+
return 'update1'
|
|
181
|
+
},
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
const observer2 = new MutationObserver(queryClient, {
|
|
185
|
+
cacheTime: 10,
|
|
186
|
+
mutationFn: async () => {
|
|
187
|
+
await sleep(10)
|
|
188
|
+
return 'update2'
|
|
189
|
+
},
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
await observer1.mutate()
|
|
193
|
+
|
|
194
|
+
// we currently have no way to add multiple observers to the same mutation
|
|
195
|
+
const currentMutation = observer1['currentMutation']!
|
|
196
|
+
currentMutation.addObserver(observer1)
|
|
197
|
+
currentMutation.addObserver(observer2)
|
|
198
|
+
|
|
199
|
+
expect(currentMutation['observers'].length).toEqual(2)
|
|
200
|
+
expect(queryClient.getMutationCache().getAll()).toHaveLength(1)
|
|
201
|
+
|
|
202
|
+
currentMutation.removeObserver(observer1)
|
|
203
|
+
currentMutation.removeObserver(observer2)
|
|
204
|
+
expect(currentMutation['observers'].length).toEqual(0)
|
|
205
|
+
expect(queryClient.getMutationCache().getAll()).toHaveLength(1)
|
|
206
|
+
// wait for cacheTime to gc
|
|
207
|
+
await sleep(10)
|
|
208
|
+
await waitFor(() => {
|
|
209
|
+
expect(queryClient.getMutationCache().getAll()).toHaveLength(0)
|
|
210
|
+
})
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
test('should be garbage collected later when unsubscribed and mutation is loading', async () => {
|
|
214
|
+
const queryClient = createQueryClient()
|
|
215
|
+
const onSuccess = jest.fn()
|
|
216
|
+
const observer = new MutationObserver(queryClient, {
|
|
217
|
+
variables: 1,
|
|
218
|
+
cacheTime: 10,
|
|
219
|
+
mutationFn: async () => {
|
|
220
|
+
await sleep(20)
|
|
221
|
+
return 'data'
|
|
222
|
+
},
|
|
223
|
+
onSuccess,
|
|
224
|
+
})
|
|
225
|
+
const unsubscribe = observer.subscribe(() => undefined)
|
|
226
|
+
observer.mutate(1)
|
|
227
|
+
unsubscribe()
|
|
228
|
+
expect(queryClient.getMutationCache().getAll()).toHaveLength(1)
|
|
229
|
+
await sleep(10)
|
|
230
|
+
// unsubscribe should not remove even though cacheTime has elapsed b/c mutation is still loading
|
|
231
|
+
expect(queryClient.getMutationCache().getAll()).toHaveLength(1)
|
|
232
|
+
await sleep(10)
|
|
233
|
+
// should be removed after an additional cacheTime wait
|
|
234
|
+
await waitFor(() => {
|
|
235
|
+
expect(queryClient.getMutationCache().getAll()).toHaveLength(0)
|
|
236
|
+
})
|
|
237
|
+
expect(onSuccess).toHaveBeenCalledTimes(1)
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
test('should call callbacks even with cacheTime 0 and mutation still loading', async () => {
|
|
241
|
+
const queryClient = createQueryClient()
|
|
242
|
+
const onSuccess = jest.fn()
|
|
243
|
+
const observer = new MutationObserver(queryClient, {
|
|
244
|
+
variables: 1,
|
|
245
|
+
cacheTime: 0,
|
|
246
|
+
mutationFn: async () => {
|
|
247
|
+
return 'data'
|
|
248
|
+
},
|
|
249
|
+
onSuccess,
|
|
250
|
+
})
|
|
251
|
+
const unsubscribe = observer.subscribe(() => undefined)
|
|
252
|
+
observer.mutate(1)
|
|
253
|
+
unsubscribe()
|
|
254
|
+
await waitFor(() => {
|
|
255
|
+
expect(queryClient.getMutationCache().getAll()).toHaveLength(0)
|
|
256
|
+
})
|
|
257
|
+
expect(onSuccess).toHaveBeenCalledTimes(1)
|
|
258
|
+
})
|
|
259
|
+
})
|
|
260
|
+
})
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { waitFor } from '@testing-library/react'
|
|
2
|
+
import { createQueryClient, sleep } from '../../../../tests/utils'
|
|
3
|
+
import { QueryClient, MutationObserver } from '..'
|
|
4
|
+
|
|
5
|
+
describe('mutationObserver', () => {
|
|
6
|
+
let queryClient: QueryClient
|
|
7
|
+
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
queryClient = createQueryClient()
|
|
10
|
+
queryClient.mount()
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
afterEach(() => {
|
|
14
|
+
queryClient.clear()
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
test('onUnsubscribe should not remove the current mutation observer if there is still a subscription', async () => {
|
|
18
|
+
const mutation = new MutationObserver(queryClient, {
|
|
19
|
+
mutationFn: async (text: string) => {
|
|
20
|
+
await sleep(20)
|
|
21
|
+
return text
|
|
22
|
+
},
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
const subscription1Handler = jest.fn()
|
|
26
|
+
const subscription2Handler = jest.fn()
|
|
27
|
+
|
|
28
|
+
const unsubscribe1 = mutation.subscribe(subscription1Handler)
|
|
29
|
+
const unsubscribe2 = mutation.subscribe(subscription2Handler)
|
|
30
|
+
|
|
31
|
+
mutation.mutate()
|
|
32
|
+
|
|
33
|
+
unsubscribe1()
|
|
34
|
+
|
|
35
|
+
await waitFor(() => {
|
|
36
|
+
// 1 call: loading
|
|
37
|
+
expect(subscription1Handler).toBeCalledTimes(1)
|
|
38
|
+
// 2 calls: loading, success
|
|
39
|
+
expect(subscription2Handler).toBeCalledTimes(2)
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
// Clean-up
|
|
43
|
+
unsubscribe2()
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
test('should not notify listeners if options.listeners is set to false', async () => {
|
|
47
|
+
const mutation = new MutationObserver(queryClient, {
|
|
48
|
+
mutationFn: async (text: string) => {
|
|
49
|
+
await sleep(20)
|
|
50
|
+
return text
|
|
51
|
+
},
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
const subscriptionHandler = jest.fn()
|
|
55
|
+
const unsubscribe = mutation.subscribe(subscriptionHandler)
|
|
56
|
+
mutation.mutate()
|
|
57
|
+
|
|
58
|
+
await waitFor(() => {
|
|
59
|
+
// 2 calls: loading, success
|
|
60
|
+
expect(subscriptionHandler).toBeCalledTimes(2)
|
|
61
|
+
})
|
|
62
|
+
subscriptionHandler.mockReset()
|
|
63
|
+
|
|
64
|
+
// Force a notification with listeners set to false
|
|
65
|
+
// because there is no existing usage of notify with listeners set to false
|
|
66
|
+
mutation['notify']({ listeners: false })
|
|
67
|
+
|
|
68
|
+
await waitFor(() => {
|
|
69
|
+
// 0 call because no notification has been sent
|
|
70
|
+
expect(subscriptionHandler).toBeCalledTimes(0)
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
unsubscribe()
|
|
74
|
+
})
|
|
75
|
+
})
|
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
import { QueryClient } from '..'
|
|
2
|
+
import {
|
|
3
|
+
createQueryClient,
|
|
4
|
+
executeMutation,
|
|
5
|
+
queryKey,
|
|
6
|
+
sleep,
|
|
7
|
+
} from '../../../../tests/utils'
|
|
8
|
+
import { MutationState } from '../mutation'
|
|
9
|
+
import { MutationObserver } from '../mutationObserver'
|
|
10
|
+
|
|
11
|
+
describe('mutations', () => {
|
|
12
|
+
let queryClient: QueryClient
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
queryClient = createQueryClient()
|
|
16
|
+
queryClient.mount()
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
afterEach(() => {
|
|
20
|
+
queryClient.clear()
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
test('mutate should trigger a mutation', async () => {
|
|
24
|
+
const result = await executeMutation(queryClient, {
|
|
25
|
+
mutationFn: async (text: string) => text,
|
|
26
|
+
variables: 'todo',
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
expect(result).toBe(result)
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
test('mutate should accept null values', async () => {
|
|
33
|
+
let variables
|
|
34
|
+
|
|
35
|
+
const mutation = new MutationObserver(queryClient, {
|
|
36
|
+
mutationFn: async (vars: unknown) => {
|
|
37
|
+
variables = vars
|
|
38
|
+
return vars
|
|
39
|
+
},
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
mutation.mutate(null)
|
|
43
|
+
|
|
44
|
+
await sleep(10)
|
|
45
|
+
|
|
46
|
+
expect(variables).toBe(null)
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
test('setMutationDefaults should be able to set defaults', async () => {
|
|
50
|
+
const key = queryKey()
|
|
51
|
+
|
|
52
|
+
queryClient.setMutationDefaults(key, {
|
|
53
|
+
mutationFn: async (text: string) => text,
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
const result = await executeMutation(queryClient, {
|
|
57
|
+
mutationKey: key,
|
|
58
|
+
variables: 'todo',
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
expect(result).toBe(result)
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
test('mutation should set correct success states', async () => {
|
|
65
|
+
const mutation = new MutationObserver(queryClient, {
|
|
66
|
+
mutationFn: async (text: string) => {
|
|
67
|
+
await sleep(10)
|
|
68
|
+
return text
|
|
69
|
+
},
|
|
70
|
+
onMutate: (text) => text,
|
|
71
|
+
variables: 'todo',
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
expect(mutation.getCurrentResult()).toEqual({
|
|
75
|
+
context: undefined,
|
|
76
|
+
data: undefined,
|
|
77
|
+
error: null,
|
|
78
|
+
failureCount: 0,
|
|
79
|
+
isError: false,
|
|
80
|
+
isIdle: true,
|
|
81
|
+
isLoading: false,
|
|
82
|
+
isPaused: false,
|
|
83
|
+
isSuccess: false,
|
|
84
|
+
mutate: expect.any(Function),
|
|
85
|
+
reset: expect.any(Function),
|
|
86
|
+
status: 'idle',
|
|
87
|
+
variables: undefined,
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
const states: MutationState<string, unknown, string, string>[] = []
|
|
91
|
+
|
|
92
|
+
mutation.subscribe((state) => {
|
|
93
|
+
states.push(state)
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
mutation.mutate()
|
|
97
|
+
|
|
98
|
+
await sleep(0)
|
|
99
|
+
|
|
100
|
+
expect(states[0]).toEqual({
|
|
101
|
+
context: undefined,
|
|
102
|
+
data: undefined,
|
|
103
|
+
error: null,
|
|
104
|
+
failureCount: 0,
|
|
105
|
+
isError: false,
|
|
106
|
+
isIdle: false,
|
|
107
|
+
isLoading: true,
|
|
108
|
+
isPaused: false,
|
|
109
|
+
isSuccess: false,
|
|
110
|
+
mutate: expect.any(Function),
|
|
111
|
+
reset: expect.any(Function),
|
|
112
|
+
status: 'loading',
|
|
113
|
+
variables: 'todo',
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
await sleep(5)
|
|
117
|
+
|
|
118
|
+
expect(states[1]).toEqual({
|
|
119
|
+
context: 'todo',
|
|
120
|
+
data: undefined,
|
|
121
|
+
error: null,
|
|
122
|
+
failureCount: 0,
|
|
123
|
+
isError: false,
|
|
124
|
+
isIdle: false,
|
|
125
|
+
isLoading: true,
|
|
126
|
+
isPaused: false,
|
|
127
|
+
isSuccess: false,
|
|
128
|
+
mutate: expect.any(Function),
|
|
129
|
+
reset: expect.any(Function),
|
|
130
|
+
status: 'loading',
|
|
131
|
+
variables: 'todo',
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
await sleep(20)
|
|
135
|
+
|
|
136
|
+
expect(states[2]).toEqual({
|
|
137
|
+
context: 'todo',
|
|
138
|
+
data: 'todo',
|
|
139
|
+
error: null,
|
|
140
|
+
failureCount: 0,
|
|
141
|
+
isError: false,
|
|
142
|
+
isIdle: false,
|
|
143
|
+
isLoading: false,
|
|
144
|
+
isPaused: false,
|
|
145
|
+
isSuccess: true,
|
|
146
|
+
mutate: expect.any(Function),
|
|
147
|
+
reset: expect.any(Function),
|
|
148
|
+
status: 'success',
|
|
149
|
+
variables: 'todo',
|
|
150
|
+
})
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
test('mutation should set correct error states', async () => {
|
|
154
|
+
const mutation = new MutationObserver(queryClient, {
|
|
155
|
+
mutationFn: async () => {
|
|
156
|
+
await sleep(20)
|
|
157
|
+
return Promise.reject('err')
|
|
158
|
+
},
|
|
159
|
+
onMutate: (text) => text,
|
|
160
|
+
variables: 'todo',
|
|
161
|
+
retry: 1,
|
|
162
|
+
retryDelay: 1,
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
const states: MutationState<string, unknown, string, string>[] = []
|
|
166
|
+
|
|
167
|
+
mutation.subscribe((state) => {
|
|
168
|
+
states.push(state)
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
mutation.mutate().catch(() => undefined)
|
|
172
|
+
|
|
173
|
+
await sleep(0)
|
|
174
|
+
|
|
175
|
+
expect(states[0]).toEqual({
|
|
176
|
+
context: undefined,
|
|
177
|
+
data: undefined,
|
|
178
|
+
error: null,
|
|
179
|
+
failureCount: 0,
|
|
180
|
+
isError: false,
|
|
181
|
+
isIdle: false,
|
|
182
|
+
isLoading: true,
|
|
183
|
+
isPaused: false,
|
|
184
|
+
isSuccess: false,
|
|
185
|
+
mutate: expect.any(Function),
|
|
186
|
+
reset: expect.any(Function),
|
|
187
|
+
status: 'loading',
|
|
188
|
+
variables: 'todo',
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
await sleep(10)
|
|
192
|
+
|
|
193
|
+
expect(states[1]).toEqual({
|
|
194
|
+
context: 'todo',
|
|
195
|
+
data: undefined,
|
|
196
|
+
error: null,
|
|
197
|
+
failureCount: 0,
|
|
198
|
+
isError: false,
|
|
199
|
+
isIdle: false,
|
|
200
|
+
isLoading: true,
|
|
201
|
+
isPaused: false,
|
|
202
|
+
isSuccess: false,
|
|
203
|
+
mutate: expect.any(Function),
|
|
204
|
+
reset: expect.any(Function),
|
|
205
|
+
status: 'loading',
|
|
206
|
+
variables: 'todo',
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
await sleep(20)
|
|
210
|
+
|
|
211
|
+
expect(states[2]).toEqual({
|
|
212
|
+
context: 'todo',
|
|
213
|
+
data: undefined,
|
|
214
|
+
error: null,
|
|
215
|
+
failureCount: 1,
|
|
216
|
+
isError: false,
|
|
217
|
+
isIdle: false,
|
|
218
|
+
isLoading: true,
|
|
219
|
+
isPaused: false,
|
|
220
|
+
isSuccess: false,
|
|
221
|
+
mutate: expect.any(Function),
|
|
222
|
+
reset: expect.any(Function),
|
|
223
|
+
status: 'loading',
|
|
224
|
+
variables: 'todo',
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
await sleep(30)
|
|
228
|
+
|
|
229
|
+
expect(states[3]).toEqual({
|
|
230
|
+
context: 'todo',
|
|
231
|
+
data: undefined,
|
|
232
|
+
error: 'err',
|
|
233
|
+
failureCount: 2,
|
|
234
|
+
isError: true,
|
|
235
|
+
isIdle: false,
|
|
236
|
+
isLoading: false,
|
|
237
|
+
isPaused: false,
|
|
238
|
+
isSuccess: false,
|
|
239
|
+
mutate: expect.any(Function),
|
|
240
|
+
reset: expect.any(Function),
|
|
241
|
+
status: 'error',
|
|
242
|
+
variables: 'todo',
|
|
243
|
+
})
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
test('should be able to restore a mutation', async () => {
|
|
247
|
+
const key = queryKey()
|
|
248
|
+
|
|
249
|
+
const onMutate = jest.fn()
|
|
250
|
+
const onSuccess = jest.fn()
|
|
251
|
+
const onSettled = jest.fn()
|
|
252
|
+
|
|
253
|
+
queryClient.setMutationDefaults(key, {
|
|
254
|
+
mutationFn: async (text: string) => text,
|
|
255
|
+
onMutate,
|
|
256
|
+
onSuccess,
|
|
257
|
+
onSettled,
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
const mutation = queryClient
|
|
261
|
+
.getMutationCache()
|
|
262
|
+
.build<string, unknown, string, string>(
|
|
263
|
+
queryClient,
|
|
264
|
+
{
|
|
265
|
+
mutationKey: key,
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
context: 'todo',
|
|
269
|
+
data: undefined,
|
|
270
|
+
error: null,
|
|
271
|
+
failureCount: 1,
|
|
272
|
+
isPaused: true,
|
|
273
|
+
status: 'loading',
|
|
274
|
+
variables: 'todo',
|
|
275
|
+
},
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
expect(mutation.state).toEqual({
|
|
279
|
+
context: 'todo',
|
|
280
|
+
data: undefined,
|
|
281
|
+
error: null,
|
|
282
|
+
failureCount: 1,
|
|
283
|
+
isPaused: true,
|
|
284
|
+
status: 'loading',
|
|
285
|
+
variables: 'todo',
|
|
286
|
+
})
|
|
287
|
+
|
|
288
|
+
await queryClient.resumePausedMutations()
|
|
289
|
+
|
|
290
|
+
expect(mutation.state).toEqual({
|
|
291
|
+
context: 'todo',
|
|
292
|
+
data: 'todo',
|
|
293
|
+
error: null,
|
|
294
|
+
failureCount: 1,
|
|
295
|
+
isPaused: false,
|
|
296
|
+
status: 'success',
|
|
297
|
+
variables: 'todo',
|
|
298
|
+
})
|
|
299
|
+
|
|
300
|
+
expect(onMutate).not.toHaveBeenCalled()
|
|
301
|
+
expect(onSuccess).toHaveBeenCalled()
|
|
302
|
+
expect(onSettled).toHaveBeenCalled()
|
|
303
|
+
})
|
|
304
|
+
|
|
305
|
+
test('setState should update the mutation state', async () => {
|
|
306
|
+
const mutation = new MutationObserver(queryClient, {
|
|
307
|
+
mutationFn: async () => {
|
|
308
|
+
return 'update'
|
|
309
|
+
},
|
|
310
|
+
onMutate: (text) => text,
|
|
311
|
+
})
|
|
312
|
+
await mutation.mutate()
|
|
313
|
+
expect(mutation.getCurrentResult().data).toEqual('update')
|
|
314
|
+
|
|
315
|
+
// Force setState usage
|
|
316
|
+
// because no use case has been found using mutation.setState
|
|
317
|
+
const currentMutation = mutation['currentMutation']
|
|
318
|
+
currentMutation?.setState({
|
|
319
|
+
context: undefined,
|
|
320
|
+
variables: undefined,
|
|
321
|
+
data: 'new',
|
|
322
|
+
error: undefined,
|
|
323
|
+
failureCount: 0,
|
|
324
|
+
isPaused: false,
|
|
325
|
+
status: 'success',
|
|
326
|
+
})
|
|
327
|
+
|
|
328
|
+
expect(mutation.getCurrentResult().data).toEqual('new')
|
|
329
|
+
})
|
|
330
|
+
|
|
331
|
+
test('addObserver should not add an existing observer', async () => {
|
|
332
|
+
const mutation = new MutationObserver(queryClient, {
|
|
333
|
+
mutationFn: async () => {
|
|
334
|
+
return 'update'
|
|
335
|
+
},
|
|
336
|
+
onMutate: (text) => text,
|
|
337
|
+
})
|
|
338
|
+
await mutation.mutate()
|
|
339
|
+
|
|
340
|
+
// Force addObserver usage to add an existing observer
|
|
341
|
+
// because no use case has been found
|
|
342
|
+
const currentMutation = mutation['currentMutation']!
|
|
343
|
+
expect(currentMutation['observers'].length).toEqual(1)
|
|
344
|
+
currentMutation.addObserver(mutation)
|
|
345
|
+
|
|
346
|
+
expect(currentMutation['observers'].length).toEqual(1)
|
|
347
|
+
})
|
|
348
|
+
|
|
349
|
+
test('mutate should throw an error if no mutationFn found', async () => {
|
|
350
|
+
const mutation = new MutationObserver(queryClient, {
|
|
351
|
+
mutationFn: undefined,
|
|
352
|
+
retry: false,
|
|
353
|
+
})
|
|
354
|
+
|
|
355
|
+
let error: any
|
|
356
|
+
try {
|
|
357
|
+
await mutation.mutate()
|
|
358
|
+
} catch (err) {
|
|
359
|
+
error = err
|
|
360
|
+
}
|
|
361
|
+
expect(error).toEqual('No mutationFn found')
|
|
362
|
+
})
|
|
363
|
+
})
|