@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,51 @@
|
|
|
1
|
+
import { createNotifyManager } from '../notifyManager'
|
|
2
|
+
import { sleep } from '../../../../tests/utils'
|
|
3
|
+
|
|
4
|
+
describe('notifyManager', () => {
|
|
5
|
+
it('should use default notifyFn', async () => {
|
|
6
|
+
const notifyManagerTest = createNotifyManager()
|
|
7
|
+
const callbackSpy = jest.fn()
|
|
8
|
+
notifyManagerTest.schedule(callbackSpy)
|
|
9
|
+
await sleep(1)
|
|
10
|
+
expect(callbackSpy).toHaveBeenCalled()
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
it('should use default batchNotifyFn', async () => {
|
|
14
|
+
const notifyManagerTest = createNotifyManager()
|
|
15
|
+
const callbackScheduleSpy = jest
|
|
16
|
+
.fn()
|
|
17
|
+
.mockImplementation(async () => await sleep(20))
|
|
18
|
+
const callbackBatchLevel2Spy = jest.fn().mockImplementation(async () => {
|
|
19
|
+
notifyManagerTest.schedule(callbackScheduleSpy)
|
|
20
|
+
})
|
|
21
|
+
const callbackBatchLevel1Spy = jest.fn().mockImplementation(async () => {
|
|
22
|
+
notifyManagerTest.batch(callbackBatchLevel2Spy)
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
notifyManagerTest.batch(callbackBatchLevel1Spy)
|
|
26
|
+
|
|
27
|
+
await sleep(30)
|
|
28
|
+
expect(callbackBatchLevel1Spy).toHaveBeenCalledTimes(1)
|
|
29
|
+
expect(callbackBatchLevel2Spy).toHaveBeenCalledTimes(1)
|
|
30
|
+
expect(callbackScheduleSpy).toHaveBeenCalledTimes(1)
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('should notify if error is thrown', async () => {
|
|
34
|
+
const notifyManagerTest = createNotifyManager()
|
|
35
|
+
const notifySpy = jest.fn()
|
|
36
|
+
|
|
37
|
+
notifyManagerTest.setNotifyFunction(notifySpy)
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
notifyManagerTest.batch(() => {
|
|
41
|
+
notifyManagerTest.schedule(jest.fn)
|
|
42
|
+
throw new Error('Foo')
|
|
43
|
+
})
|
|
44
|
+
} catch {}
|
|
45
|
+
|
|
46
|
+
// needed for scheduleMicroTask to kick in
|
|
47
|
+
await sleep(1)
|
|
48
|
+
|
|
49
|
+
expect(notifySpy).toHaveBeenCalledTimes(1)
|
|
50
|
+
})
|
|
51
|
+
})
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { OnlineManager } from '../onlineManager'
|
|
2
|
+
import { setIsServer, sleep } from '../../../../tests/utils'
|
|
3
|
+
|
|
4
|
+
describe('onlineManager', () => {
|
|
5
|
+
let onlineManager: OnlineManager
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
onlineManager = new OnlineManager()
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
test('isOnline should return true if navigator is undefined', () => {
|
|
11
|
+
const navigatorSpy = jest.spyOn(globalThis, 'navigator', 'get')
|
|
12
|
+
|
|
13
|
+
// Force navigator to be undefined
|
|
14
|
+
//@ts-expect-error
|
|
15
|
+
navigatorSpy.mockImplementation(() => undefined)
|
|
16
|
+
expect(onlineManager.isOnline()).toBeTruthy()
|
|
17
|
+
|
|
18
|
+
navigatorSpy.mockRestore()
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
test('isOnline should return true if navigator.onLine is true', () => {
|
|
22
|
+
const navigatorSpy = jest.spyOn(navigator, 'onLine', 'get')
|
|
23
|
+
navigatorSpy.mockImplementation(() => true)
|
|
24
|
+
|
|
25
|
+
expect(onlineManager.isOnline()).toBeTruthy()
|
|
26
|
+
|
|
27
|
+
navigatorSpy.mockRestore()
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
test('setEventListener should use online boolean arg', async () => {
|
|
31
|
+
let count = 0
|
|
32
|
+
|
|
33
|
+
const setup = (setOnline: (online?: boolean) => void) => {
|
|
34
|
+
setTimeout(() => {
|
|
35
|
+
count++
|
|
36
|
+
setOnline(false)
|
|
37
|
+
}, 20)
|
|
38
|
+
return () => void 0
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
onlineManager.setEventListener(setup)
|
|
42
|
+
|
|
43
|
+
await sleep(30)
|
|
44
|
+
expect(count).toEqual(1)
|
|
45
|
+
expect(onlineManager.isOnline()).toBeFalsy()
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
test('setEventListener should call previous remove handler when replacing an event listener', () => {
|
|
49
|
+
const remove1Spy = jest.fn()
|
|
50
|
+
const remove2Spy = jest.fn()
|
|
51
|
+
|
|
52
|
+
onlineManager.setEventListener(() => remove1Spy)
|
|
53
|
+
onlineManager.setEventListener(() => remove2Spy)
|
|
54
|
+
|
|
55
|
+
expect(remove1Spy).toHaveBeenCalledTimes(1)
|
|
56
|
+
expect(remove2Spy).not.toHaveBeenCalled()
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
test('cleanup should still be undefined if window is not defined', async () => {
|
|
60
|
+
const restoreIsServer = setIsServer(true)
|
|
61
|
+
|
|
62
|
+
const unsubscribe = onlineManager.subscribe(() => undefined)
|
|
63
|
+
expect(onlineManager['cleanup']).toBeUndefined()
|
|
64
|
+
|
|
65
|
+
unsubscribe()
|
|
66
|
+
restoreIsServer()
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
test('cleanup should still be undefined if window.addEventListener is not defined', async () => {
|
|
70
|
+
const { addEventListener } = globalThis.window
|
|
71
|
+
|
|
72
|
+
// @ts-expect-error
|
|
73
|
+
globalThis.window.addEventListener = undefined
|
|
74
|
+
|
|
75
|
+
const unsubscribe = onlineManager.subscribe(() => undefined)
|
|
76
|
+
expect(onlineManager['cleanup']).toBeUndefined()
|
|
77
|
+
|
|
78
|
+
unsubscribe()
|
|
79
|
+
globalThis.window.addEventListener = addEventListener
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
test('it should replace default window listener when a new event listener is set', async () => {
|
|
83
|
+
const addEventListenerSpy = jest.spyOn(
|
|
84
|
+
globalThis.window,
|
|
85
|
+
'addEventListener',
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
const removeEventListenerSpy = jest.spyOn(
|
|
89
|
+
globalThis.window,
|
|
90
|
+
'removeEventListener',
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
// Should set the default event listener with window event listeners
|
|
94
|
+
const unsubscribe = onlineManager.subscribe(() => undefined)
|
|
95
|
+
expect(addEventListenerSpy).toHaveBeenCalledTimes(2)
|
|
96
|
+
|
|
97
|
+
// Should replace the window default event listener by a new one
|
|
98
|
+
// and it should call window.removeEventListener twice
|
|
99
|
+
onlineManager.setEventListener(() => {
|
|
100
|
+
return () => void 0
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
expect(removeEventListenerSpy).toHaveBeenCalledTimes(2)
|
|
104
|
+
|
|
105
|
+
unsubscribe()
|
|
106
|
+
addEventListenerSpy.mockRestore()
|
|
107
|
+
removeEventListenerSpy.mockRestore()
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
test('should call removeEventListener when last listener unsubscribes', () => {
|
|
111
|
+
const addEventListenerSpy = jest.spyOn(
|
|
112
|
+
globalThis.window,
|
|
113
|
+
'addEventListener',
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
const removeEventListenerSpy = jest.spyOn(
|
|
117
|
+
globalThis.window,
|
|
118
|
+
'removeEventListener',
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
const unsubscribe1 = onlineManager.subscribe(() => undefined)
|
|
122
|
+
const unsubscribe2 = onlineManager.subscribe(() => undefined)
|
|
123
|
+
expect(addEventListenerSpy).toHaveBeenCalledTimes(2) // online + offline
|
|
124
|
+
|
|
125
|
+
unsubscribe1()
|
|
126
|
+
expect(removeEventListenerSpy).toHaveBeenCalledTimes(0)
|
|
127
|
+
unsubscribe2()
|
|
128
|
+
expect(removeEventListenerSpy).toHaveBeenCalledTimes(2) // online + offline
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
test('should keep setup function even if last listener unsubscribes', () => {
|
|
132
|
+
const setupSpy = jest.fn().mockImplementation(() => () => undefined)
|
|
133
|
+
|
|
134
|
+
onlineManager.setEventListener(setupSpy)
|
|
135
|
+
|
|
136
|
+
const unsubscribe1 = onlineManager.subscribe(() => undefined)
|
|
137
|
+
|
|
138
|
+
expect(setupSpy).toHaveBeenCalledTimes(1)
|
|
139
|
+
|
|
140
|
+
unsubscribe1()
|
|
141
|
+
|
|
142
|
+
const unsubscribe2 = onlineManager.subscribe(() => undefined)
|
|
143
|
+
|
|
144
|
+
expect(setupSpy).toHaveBeenCalledTimes(2)
|
|
145
|
+
|
|
146
|
+
unsubscribe2()
|
|
147
|
+
})
|
|
148
|
+
})
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
import { waitFor } from '@testing-library/react'
|
|
2
|
+
import {
|
|
3
|
+
sleep,
|
|
4
|
+
queryKey,
|
|
5
|
+
createQueryClient,
|
|
6
|
+
mockLogger,
|
|
7
|
+
} from '../../../../tests/utils'
|
|
8
|
+
import {
|
|
9
|
+
QueryClient,
|
|
10
|
+
QueriesObserver,
|
|
11
|
+
QueryObserverResult,
|
|
12
|
+
QueryObserver,
|
|
13
|
+
} from '..'
|
|
14
|
+
import { QueryKey } from '..'
|
|
15
|
+
|
|
16
|
+
describe('queriesObserver', () => {
|
|
17
|
+
let queryClient: QueryClient
|
|
18
|
+
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
queryClient = createQueryClient()
|
|
21
|
+
queryClient.mount()
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
afterEach(() => {
|
|
25
|
+
queryClient.clear()
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
test('should return an array with all query results', async () => {
|
|
29
|
+
const key1 = queryKey()
|
|
30
|
+
const key2 = queryKey()
|
|
31
|
+
const queryFn1 = jest.fn().mockReturnValue(1)
|
|
32
|
+
const queryFn2 = jest.fn().mockReturnValue(2)
|
|
33
|
+
const observer = new QueriesObserver(queryClient, [
|
|
34
|
+
{ queryKey: key1, queryFn: queryFn1 },
|
|
35
|
+
{ queryKey: key2, queryFn: queryFn2 },
|
|
36
|
+
])
|
|
37
|
+
let observerResult
|
|
38
|
+
const unsubscribe = observer.subscribe((result) => {
|
|
39
|
+
observerResult = result
|
|
40
|
+
})
|
|
41
|
+
await sleep(1)
|
|
42
|
+
unsubscribe()
|
|
43
|
+
expect(observerResult).toMatchObject([{ data: 1 }, { data: 2 }])
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
test('should still return value for undefined query key', async () => {
|
|
47
|
+
const key1 = queryKey()
|
|
48
|
+
const queryFn1 = jest.fn().mockReturnValue(1)
|
|
49
|
+
const queryFn2 = jest.fn().mockReturnValue(2)
|
|
50
|
+
const observer = new QueriesObserver(queryClient, [
|
|
51
|
+
{ queryKey: key1, queryFn: queryFn1 },
|
|
52
|
+
{ queryKey: undefined, queryFn: queryFn2 },
|
|
53
|
+
])
|
|
54
|
+
let observerResult
|
|
55
|
+
const unsubscribe = observer.subscribe((result) => {
|
|
56
|
+
observerResult = result
|
|
57
|
+
})
|
|
58
|
+
await sleep(1)
|
|
59
|
+
unsubscribe()
|
|
60
|
+
expect(observerResult).toMatchObject([{ data: 1 }, { data: 2 }])
|
|
61
|
+
|
|
62
|
+
expect(mockLogger.error).toHaveBeenCalledTimes(1)
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
test('should update when a query updates', async () => {
|
|
66
|
+
const key1 = queryKey()
|
|
67
|
+
const key2 = queryKey()
|
|
68
|
+
const queryFn1 = jest.fn().mockReturnValue(1)
|
|
69
|
+
const queryFn2 = jest.fn().mockReturnValue(2)
|
|
70
|
+
const observer = new QueriesObserver(queryClient, [
|
|
71
|
+
{ queryKey: key1, queryFn: queryFn1 },
|
|
72
|
+
{ queryKey: key2, queryFn: queryFn2 },
|
|
73
|
+
])
|
|
74
|
+
const results: QueryObserverResult[][] = []
|
|
75
|
+
results.push(observer.getCurrentResult())
|
|
76
|
+
const unsubscribe = observer.subscribe((result) => {
|
|
77
|
+
results.push(result)
|
|
78
|
+
})
|
|
79
|
+
await sleep(1)
|
|
80
|
+
queryClient.setQueryData(key2, 3)
|
|
81
|
+
await sleep(1)
|
|
82
|
+
unsubscribe()
|
|
83
|
+
expect(results.length).toBe(6)
|
|
84
|
+
expect(results[0]).toMatchObject([
|
|
85
|
+
{ status: 'loading', fetchStatus: 'idle', data: undefined },
|
|
86
|
+
{ status: 'loading', fetchStatus: 'idle', data: undefined },
|
|
87
|
+
])
|
|
88
|
+
expect(results[1]).toMatchObject([
|
|
89
|
+
{ status: 'loading', fetchStatus: 'fetching', data: undefined },
|
|
90
|
+
{ status: 'loading', fetchStatus: 'idle', data: undefined },
|
|
91
|
+
])
|
|
92
|
+
expect(results[2]).toMatchObject([
|
|
93
|
+
{ status: 'loading', fetchStatus: 'fetching', data: undefined },
|
|
94
|
+
{ status: 'loading', fetchStatus: 'fetching', data: undefined },
|
|
95
|
+
])
|
|
96
|
+
expect(results[3]).toMatchObject([
|
|
97
|
+
{ status: 'success', data: 1 },
|
|
98
|
+
{ status: 'loading', fetchStatus: 'fetching', data: undefined },
|
|
99
|
+
])
|
|
100
|
+
expect(results[4]).toMatchObject([
|
|
101
|
+
{ status: 'success', data: 1 },
|
|
102
|
+
{ status: 'success', data: 2 },
|
|
103
|
+
])
|
|
104
|
+
expect(results[5]).toMatchObject([
|
|
105
|
+
{ status: 'success', data: 1 },
|
|
106
|
+
{ status: 'success', data: 3 },
|
|
107
|
+
])
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
test('should update when a query is removed', async () => {
|
|
111
|
+
const key1 = queryKey()
|
|
112
|
+
const key2 = queryKey()
|
|
113
|
+
const queryFn1 = jest.fn().mockReturnValue(1)
|
|
114
|
+
const queryFn2 = jest.fn().mockReturnValue(2)
|
|
115
|
+
const observer = new QueriesObserver(queryClient, [
|
|
116
|
+
{ queryKey: key1, queryFn: queryFn1 },
|
|
117
|
+
{ queryKey: key2, queryFn: queryFn2 },
|
|
118
|
+
])
|
|
119
|
+
const results: QueryObserverResult[][] = []
|
|
120
|
+
results.push(observer.getCurrentResult())
|
|
121
|
+
const unsubscribe = observer.subscribe((result) => {
|
|
122
|
+
results.push(result)
|
|
123
|
+
})
|
|
124
|
+
await sleep(1)
|
|
125
|
+
observer.setQueries([{ queryKey: key2, queryFn: queryFn2 }])
|
|
126
|
+
await sleep(1)
|
|
127
|
+
const queryCache = queryClient.getQueryCache()
|
|
128
|
+
expect(queryCache.find(key1, { type: 'active' })).toBeUndefined()
|
|
129
|
+
expect(queryCache.find(key2, { type: 'active' })).toBeDefined()
|
|
130
|
+
unsubscribe()
|
|
131
|
+
expect(queryCache.find(key1, { type: 'active' })).toBeUndefined()
|
|
132
|
+
expect(queryCache.find(key2, { type: 'active' })).toBeUndefined()
|
|
133
|
+
expect(results.length).toBe(6)
|
|
134
|
+
expect(results[0]).toMatchObject([
|
|
135
|
+
{ status: 'loading', fetchStatus: 'idle', data: undefined },
|
|
136
|
+
{ status: 'loading', fetchStatus: 'idle', data: undefined },
|
|
137
|
+
])
|
|
138
|
+
expect(results[1]).toMatchObject([
|
|
139
|
+
{ status: 'loading', fetchStatus: 'fetching', data: undefined },
|
|
140
|
+
{ status: 'loading', fetchStatus: 'idle', data: undefined },
|
|
141
|
+
])
|
|
142
|
+
expect(results[2]).toMatchObject([
|
|
143
|
+
{ status: 'loading', fetchStatus: 'fetching', data: undefined },
|
|
144
|
+
{ status: 'loading', fetchStatus: 'fetching', data: undefined },
|
|
145
|
+
])
|
|
146
|
+
expect(results[3]).toMatchObject([
|
|
147
|
+
{ status: 'success', data: 1 },
|
|
148
|
+
{ status: 'loading', fetchStatus: 'fetching', data: undefined },
|
|
149
|
+
])
|
|
150
|
+
expect(results[4]).toMatchObject([
|
|
151
|
+
{ status: 'success', data: 1 },
|
|
152
|
+
{ status: 'success', data: 2 },
|
|
153
|
+
])
|
|
154
|
+
expect(results[5]).toMatchObject([{ status: 'success', data: 2 }])
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
test('should update when a query changed position', async () => {
|
|
158
|
+
const key1 = queryKey()
|
|
159
|
+
const key2 = queryKey()
|
|
160
|
+
const queryFn1 = jest.fn().mockReturnValue(1)
|
|
161
|
+
const queryFn2 = jest.fn().mockReturnValue(2)
|
|
162
|
+
const observer = new QueriesObserver(queryClient, [
|
|
163
|
+
{ queryKey: key1, queryFn: queryFn1 },
|
|
164
|
+
{ queryKey: key2, queryFn: queryFn2 },
|
|
165
|
+
])
|
|
166
|
+
const results: QueryObserverResult[][] = []
|
|
167
|
+
results.push(observer.getCurrentResult())
|
|
168
|
+
const unsubscribe = observer.subscribe((result) => {
|
|
169
|
+
results.push(result)
|
|
170
|
+
})
|
|
171
|
+
await sleep(1)
|
|
172
|
+
observer.setQueries([
|
|
173
|
+
{ queryKey: key2, queryFn: queryFn2 },
|
|
174
|
+
{ queryKey: key1, queryFn: queryFn1 },
|
|
175
|
+
])
|
|
176
|
+
await sleep(1)
|
|
177
|
+
unsubscribe()
|
|
178
|
+
expect(results.length).toBe(6)
|
|
179
|
+
expect(results[0]).toMatchObject([
|
|
180
|
+
{ status: 'loading', fetchStatus: 'idle', data: undefined },
|
|
181
|
+
{ status: 'loading', fetchStatus: 'idle', data: undefined },
|
|
182
|
+
])
|
|
183
|
+
expect(results[1]).toMatchObject([
|
|
184
|
+
{ status: 'loading', fetchStatus: 'fetching', data: undefined },
|
|
185
|
+
{ status: 'loading', fetchStatus: 'idle', data: undefined },
|
|
186
|
+
])
|
|
187
|
+
expect(results[2]).toMatchObject([
|
|
188
|
+
{ status: 'loading', fetchStatus: 'fetching', data: undefined },
|
|
189
|
+
{ status: 'loading', fetchStatus: 'fetching', data: undefined },
|
|
190
|
+
])
|
|
191
|
+
expect(results[3]).toMatchObject([
|
|
192
|
+
{ status: 'success', data: 1 },
|
|
193
|
+
{ status: 'loading', fetchStatus: 'fetching', data: undefined },
|
|
194
|
+
])
|
|
195
|
+
expect(results[4]).toMatchObject([
|
|
196
|
+
{ status: 'success', data: 1 },
|
|
197
|
+
{ status: 'success', data: 2 },
|
|
198
|
+
])
|
|
199
|
+
expect(results[5]).toMatchObject([
|
|
200
|
+
{ status: 'success', data: 2 },
|
|
201
|
+
{ status: 'success', data: 1 },
|
|
202
|
+
])
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
test('should not update when nothing has changed', async () => {
|
|
206
|
+
const key1 = queryKey()
|
|
207
|
+
const key2 = queryKey()
|
|
208
|
+
const queryFn1 = jest.fn().mockReturnValue(1)
|
|
209
|
+
const queryFn2 = jest.fn().mockReturnValue(2)
|
|
210
|
+
const observer = new QueriesObserver(queryClient, [
|
|
211
|
+
{ queryKey: key1, queryFn: queryFn1 },
|
|
212
|
+
{ queryKey: key2, queryFn: queryFn2 },
|
|
213
|
+
])
|
|
214
|
+
const results: QueryObserverResult[][] = []
|
|
215
|
+
results.push(observer.getCurrentResult())
|
|
216
|
+
const unsubscribe = observer.subscribe((result) => {
|
|
217
|
+
results.push(result)
|
|
218
|
+
})
|
|
219
|
+
await sleep(1)
|
|
220
|
+
observer.setQueries([
|
|
221
|
+
{ queryKey: key1, queryFn: queryFn1 },
|
|
222
|
+
{ queryKey: key2, queryFn: queryFn2 },
|
|
223
|
+
])
|
|
224
|
+
await sleep(1)
|
|
225
|
+
unsubscribe()
|
|
226
|
+
expect(results.length).toBe(5)
|
|
227
|
+
expect(results[0]).toMatchObject([
|
|
228
|
+
{ status: 'loading', fetchStatus: 'idle', data: undefined },
|
|
229
|
+
{ status: 'loading', fetchStatus: 'idle', data: undefined },
|
|
230
|
+
])
|
|
231
|
+
expect(results[1]).toMatchObject([
|
|
232
|
+
{ status: 'loading', fetchStatus: 'fetching', data: undefined },
|
|
233
|
+
{ status: 'loading', fetchStatus: 'idle', data: undefined },
|
|
234
|
+
])
|
|
235
|
+
expect(results[2]).toMatchObject([
|
|
236
|
+
{ status: 'loading', fetchStatus: 'fetching', data: undefined },
|
|
237
|
+
{ status: 'loading', fetchStatus: 'fetching', data: undefined },
|
|
238
|
+
])
|
|
239
|
+
expect(results[3]).toMatchObject([
|
|
240
|
+
{ status: 'success', data: 1 },
|
|
241
|
+
{ status: 'loading', fetchStatus: 'fetching', data: undefined },
|
|
242
|
+
])
|
|
243
|
+
expect(results[4]).toMatchObject([
|
|
244
|
+
{ status: 'success', data: 1 },
|
|
245
|
+
{ status: 'success', data: 2 },
|
|
246
|
+
])
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
test('should trigger all fetches when subscribed', async () => {
|
|
250
|
+
const key1 = queryKey()
|
|
251
|
+
const key2 = queryKey()
|
|
252
|
+
const queryFn1 = jest.fn().mockReturnValue(1)
|
|
253
|
+
const queryFn2 = jest.fn().mockReturnValue(2)
|
|
254
|
+
const observer = new QueriesObserver(queryClient, [
|
|
255
|
+
{ queryKey: key1, queryFn: queryFn1 },
|
|
256
|
+
{ queryKey: key2, queryFn: queryFn2 },
|
|
257
|
+
])
|
|
258
|
+
const unsubscribe = observer.subscribe(() => undefined)
|
|
259
|
+
await sleep(1)
|
|
260
|
+
unsubscribe()
|
|
261
|
+
expect(queryFn1).toHaveBeenCalledTimes(1)
|
|
262
|
+
expect(queryFn2).toHaveBeenCalledTimes(1)
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
test('should not destroy the observer if there is still a subscription', async () => {
|
|
266
|
+
const key1 = queryKey()
|
|
267
|
+
const observer = new QueriesObserver(queryClient, [
|
|
268
|
+
{
|
|
269
|
+
queryKey: key1,
|
|
270
|
+
queryFn: async () => {
|
|
271
|
+
await sleep(20)
|
|
272
|
+
return 1
|
|
273
|
+
},
|
|
274
|
+
},
|
|
275
|
+
])
|
|
276
|
+
|
|
277
|
+
const subscription1Handler = jest.fn()
|
|
278
|
+
const subscription2Handler = jest.fn()
|
|
279
|
+
|
|
280
|
+
const unsubscribe1 = observer.subscribe(subscription1Handler)
|
|
281
|
+
const unsubscribe2 = observer.subscribe(subscription2Handler)
|
|
282
|
+
|
|
283
|
+
unsubscribe1()
|
|
284
|
+
|
|
285
|
+
await waitFor(() => {
|
|
286
|
+
// 1 call: loading
|
|
287
|
+
expect(subscription1Handler).toBeCalledTimes(1)
|
|
288
|
+
// 1 call: success
|
|
289
|
+
expect(subscription2Handler).toBeCalledTimes(1)
|
|
290
|
+
})
|
|
291
|
+
|
|
292
|
+
// Clean-up
|
|
293
|
+
unsubscribe2()
|
|
294
|
+
})
|
|
295
|
+
|
|
296
|
+
test('onUpdate should not update the result for an unknown observer', async () => {
|
|
297
|
+
const key1 = queryKey()
|
|
298
|
+
const key2 = queryKey()
|
|
299
|
+
|
|
300
|
+
const queriesObserver = new QueriesObserver(queryClient, [
|
|
301
|
+
{
|
|
302
|
+
queryKey: key1,
|
|
303
|
+
queryFn: () => 1,
|
|
304
|
+
},
|
|
305
|
+
])
|
|
306
|
+
|
|
307
|
+
const newQueryObserver = new QueryObserver<
|
|
308
|
+
unknown,
|
|
309
|
+
unknown,
|
|
310
|
+
unknown,
|
|
311
|
+
unknown,
|
|
312
|
+
QueryKey
|
|
313
|
+
>(queryClient, {
|
|
314
|
+
queryKey: key2,
|
|
315
|
+
queryFn: () => 2,
|
|
316
|
+
})
|
|
317
|
+
|
|
318
|
+
// Force onUpdate with an unknown QueryObserver
|
|
319
|
+
// because no existing use case has been found in the lib
|
|
320
|
+
queriesObserver['onUpdate'](
|
|
321
|
+
newQueryObserver,
|
|
322
|
+
// The current queries observer result is re-used here
|
|
323
|
+
// to use a typescript friendly result
|
|
324
|
+
queriesObserver.getCurrentResult()[0]!,
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
// Should not alter the result
|
|
328
|
+
expect(queriesObserver.getCurrentResult()[-1]).toBeUndefined()
|
|
329
|
+
})
|
|
330
|
+
})
|