@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.
Files changed (85) hide show
  1. package/build/cjs/focusManager.js +101 -0
  2. package/build/cjs/focusManager.js.map +1 -0
  3. package/build/cjs/hydration.js +112 -0
  4. package/build/cjs/hydration.js.map +1 -0
  5. package/build/cjs/index.js +51 -0
  6. package/build/cjs/index.js.map +1 -0
  7. package/build/cjs/infiniteQueryBehavior.js +160 -0
  8. package/build/cjs/infiniteQueryBehavior.js.map +1 -0
  9. package/build/cjs/infiniteQueryObserver.js +92 -0
  10. package/build/cjs/infiniteQueryObserver.js.map +1 -0
  11. package/build/cjs/logger.js +18 -0
  12. package/build/cjs/logger.js.map +1 -0
  13. package/build/cjs/mutation.js +258 -0
  14. package/build/cjs/mutation.js.map +1 -0
  15. package/build/cjs/mutationCache.js +99 -0
  16. package/build/cjs/mutationCache.js.map +1 -0
  17. package/build/cjs/mutationObserver.js +130 -0
  18. package/build/cjs/mutationObserver.js.map +1 -0
  19. package/build/cjs/notifyManager.js +114 -0
  20. package/build/cjs/notifyManager.js.map +1 -0
  21. package/build/cjs/onlineManager.js +100 -0
  22. package/build/cjs/onlineManager.js.map +1 -0
  23. package/build/cjs/queriesObserver.js +170 -0
  24. package/build/cjs/queriesObserver.js.map +1 -0
  25. package/build/cjs/query.js +474 -0
  26. package/build/cjs/query.js.map +1 -0
  27. package/build/cjs/queryCache.js +140 -0
  28. package/build/cjs/queryCache.js.map +1 -0
  29. package/build/cjs/queryClient.js +357 -0
  30. package/build/cjs/queryClient.js.map +1 -0
  31. package/build/cjs/queryObserver.js +521 -0
  32. package/build/cjs/queryObserver.js.map +1 -0
  33. package/build/cjs/removable.js +47 -0
  34. package/build/cjs/removable.js.map +1 -0
  35. package/build/cjs/retryer.js +177 -0
  36. package/build/cjs/retryer.js.map +1 -0
  37. package/build/cjs/subscribable.js +43 -0
  38. package/build/cjs/subscribable.js.map +1 -0
  39. package/build/cjs/utils.js +356 -0
  40. package/build/cjs/utils.js.map +1 -0
  41. package/build/esm/index.js +3077 -0
  42. package/build/esm/index.js.map +1 -0
  43. package/build/stats-html.html +2689 -0
  44. package/build/umd/index.development.js +3106 -0
  45. package/build/umd/index.development.js.map +1 -0
  46. package/build/umd/index.production.js +12 -0
  47. package/build/umd/index.production.js.map +1 -0
  48. package/package.json +25 -0
  49. package/src/focusManager.ts +89 -0
  50. package/src/hydration.ts +164 -0
  51. package/src/index.ts +35 -0
  52. package/src/infiniteQueryBehavior.ts +214 -0
  53. package/src/infiniteQueryObserver.ts +159 -0
  54. package/src/logger.native.ts +11 -0
  55. package/src/logger.ts +9 -0
  56. package/src/mutation.ts +349 -0
  57. package/src/mutationCache.ts +157 -0
  58. package/src/mutationObserver.ts +195 -0
  59. package/src/notifyManager.ts +96 -0
  60. package/src/onlineManager.ts +89 -0
  61. package/src/queriesObserver.ts +211 -0
  62. package/src/query.ts +612 -0
  63. package/src/queryCache.ts +206 -0
  64. package/src/queryClient.ts +716 -0
  65. package/src/queryObserver.ts +748 -0
  66. package/src/removable.ts +37 -0
  67. package/src/retryer.ts +215 -0
  68. package/src/subscribable.ts +33 -0
  69. package/src/tests/focusManager.test.tsx +155 -0
  70. package/src/tests/hydration.test.tsx +429 -0
  71. package/src/tests/infiniteQueryBehavior.test.tsx +124 -0
  72. package/src/tests/infiniteQueryObserver.test.tsx +64 -0
  73. package/src/tests/mutationCache.test.tsx +260 -0
  74. package/src/tests/mutationObserver.test.tsx +75 -0
  75. package/src/tests/mutations.test.tsx +363 -0
  76. package/src/tests/notifyManager.test.tsx +51 -0
  77. package/src/tests/onlineManager.test.tsx +148 -0
  78. package/src/tests/queriesObserver.test.tsx +330 -0
  79. package/src/tests/query.test.tsx +888 -0
  80. package/src/tests/queryCache.test.tsx +236 -0
  81. package/src/tests/queryClient.test.tsx +1435 -0
  82. package/src/tests/queryObserver.test.tsx +802 -0
  83. package/src/tests/utils.test.tsx +360 -0
  84. package/src/types.ts +705 -0
  85. 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
+ })