source: node_modules/@reduxjs/toolkit/src/query/tests/cacheCollection.test.ts@ a762898

Last change on this file since a762898 was a762898, checked in by istevanoska <ilinastevanoska@…>, 5 months ago

Added visualizations

  • Property mode set to 100644
File size: 6.8 KB
Line 
1import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'
2import { configureStore } from '@reduxjs/toolkit'
3import { vi } from 'vitest'
4import type { Middleware, Reducer } from 'redux'
5import {
6 THIRTY_TWO_BIT_MAX_INT,
7 THIRTY_TWO_BIT_MAX_TIMER_SECONDS,
8} from '../core/buildMiddleware/cacheCollection'
9import { countObjectKeys } from '../utils/countObjectKeys'
10
11beforeAll(() => {
12 vi.useFakeTimers()
13})
14
15const onCleanup = vi.fn()
16
17beforeEach(() => {
18 onCleanup.mockClear()
19})
20
21test(`query: await cleanup, defaults`, async () => {
22 const { store, api } = storeForApi(
23 createApi({
24 baseQuery: fetchBaseQuery({ baseUrl: 'https://example.com' }),
25 endpoints: (build) => ({
26 query: build.query<unknown, string>({
27 query: () => '/success',
28 }),
29 }),
30 }),
31 )
32
33 const promise = store.dispatch(api.endpoints.query.initiate('arg'))
34 await promise
35 promise.unsubscribe()
36 vi.advanceTimersByTime(59000)
37 expect(onCleanup).not.toHaveBeenCalled()
38 vi.advanceTimersByTime(2000)
39 expect(onCleanup).toHaveBeenCalled()
40})
41
42test(`query: await cleanup, keepUnusedDataFor set`, async () => {
43 const { store, api } = storeForApi(
44 createApi({
45 baseQuery: fetchBaseQuery({ baseUrl: 'https://example.com' }),
46 endpoints: (build) => ({
47 query: build.query<unknown, string>({
48 query: () => '/success',
49 }),
50 }),
51 keepUnusedDataFor: 29,
52 }),
53 )
54
55 const promise = store.dispatch(api.endpoints.query.initiate('arg'))
56 await promise
57 promise.unsubscribe()
58 vi.advanceTimersByTime(28000)
59 expect(onCleanup).not.toHaveBeenCalled()
60 vi.advanceTimersByTime(2000)
61 expect(onCleanup).toHaveBeenCalled()
62})
63
64test(`query: handles large keepUnuseDataFor values over 32-bit ms`, async () => {
65 const { store, api } = storeForApi(
66 createApi({
67 baseQuery: fetchBaseQuery({ baseUrl: 'https://example.com' }),
68 endpoints: (build) => ({
69 query: build.query<unknown, string>({
70 query: () => '/success',
71 }),
72 }),
73 keepUnusedDataFor: THIRTY_TWO_BIT_MAX_TIMER_SECONDS - 10,
74 }),
75 )
76
77 const promise = store.dispatch(api.endpoints.query.initiate('arg'))
78 await promise
79 promise.unsubscribe()
80
81 // Shouldn't have been called right away
82 vi.advanceTimersByTime(1000)
83 expect(onCleanup).not.toHaveBeenCalled()
84
85 // Shouldn't have been called any time in the next few minutes
86 vi.advanceTimersByTime(1_000_000)
87 expect(onCleanup).not.toHaveBeenCalled()
88
89 // _Should_ be called _wayyyy_ in the future (like 24.8 days from now)
90 vi.advanceTimersByTime(THIRTY_TWO_BIT_MAX_TIMER_SECONDS * 1000),
91 expect(onCleanup).toHaveBeenCalled()
92})
93
94describe(`query: await cleanup, keepUnusedDataFor set`, () => {
95 const { store, api } = storeForApi(
96 createApi({
97 baseQuery: fetchBaseQuery({ baseUrl: 'https://example.com' }),
98 endpoints: (build) => ({
99 query: build.query<unknown, string>({
100 query: () => '/success',
101 }),
102 query2: build.query<unknown, string>({
103 query: () => '/success',
104 keepUnusedDataFor: 35,
105 }),
106 query3: build.query<unknown, string>({
107 query: () => '/success',
108 keepUnusedDataFor: 0,
109 }),
110 query4: build.query<unknown, string>({
111 query: () => '/success',
112 keepUnusedDataFor: Infinity,
113 }),
114 }),
115 keepUnusedDataFor: 29,
116 }),
117 )
118
119 test('global keepUnusedDataFor', async () => {
120 const promise = store.dispatch(api.endpoints.query.initiate('arg'))
121 await promise
122 promise.unsubscribe()
123 vi.advanceTimersByTime(28000)
124 expect(onCleanup).not.toHaveBeenCalled()
125 vi.advanceTimersByTime(2000)
126 expect(onCleanup).toHaveBeenCalled()
127 })
128
129 test('endpoint keepUnusedDataFor', async () => {
130 const promise = store.dispatch(api.endpoints.query2.initiate('arg'))
131 await promise
132 promise.unsubscribe()
133
134 vi.advanceTimersByTime(34000)
135 expect(onCleanup).not.toHaveBeenCalled()
136 vi.advanceTimersByTime(2000)
137 expect(onCleanup).toHaveBeenCalled()
138 })
139
140 test('endpoint keepUnusedDataFor: 0 ', async () => {
141 expect(onCleanup).not.toHaveBeenCalled()
142 const promise = store.dispatch(api.endpoints.query3.initiate('arg'))
143 await promise
144 promise.unsubscribe()
145 expect(onCleanup).not.toHaveBeenCalled()
146 vi.advanceTimersByTime(1)
147 expect(onCleanup).toHaveBeenCalled()
148 })
149
150 test('endpoint keepUnusedDataFor: Infinity', async () => {
151 expect(onCleanup).not.toHaveBeenCalled()
152 store.dispatch(api.endpoints.query4.initiate('arg')).unsubscribe()
153 expect(onCleanup).not.toHaveBeenCalled()
154 vi.advanceTimersByTime(THIRTY_TWO_BIT_MAX_INT)
155 expect(onCleanup).not.toHaveBeenCalled()
156 })
157})
158
159describe('resetApiState cleanup', () => {
160 test('resetApiState aborts multiple running queries and mutations', async () => {
161 const { store, api } = storeForApi(
162 createApi({
163 baseQuery: fetchBaseQuery({ baseUrl: 'https://example.com' }),
164 endpoints: (build) => ({
165 query1: build.query<unknown, string>({
166 query: () => '/success',
167 }),
168 query2: build.query<unknown, string>({
169 query: () => '/success',
170 }),
171 mutation: build.mutation<unknown, string>({
172 query: () => ({
173 url: '/success',
174 method: 'POST',
175 }),
176 }),
177 }),
178 }),
179 )
180
181 // Start multiple queries and a mutation
182 const queryPromise1 = store.dispatch(api.endpoints.query1.initiate('arg1'))
183 const queryPromise2 = store.dispatch(api.endpoints.query2.initiate('arg2'))
184 const mutationPromise = store.dispatch(
185 api.endpoints.mutation.initiate('arg'),
186 )
187
188 // Spy on abort methods
189 queryPromise1.abort = vi.fn(queryPromise1.abort)
190 queryPromise2.abort = vi.fn(queryPromise2.abort)
191 mutationPromise.abort = vi.fn(mutationPromise.abort)
192
193 // Dispatch resetApiState
194 store.dispatch(api.util.resetApiState())
195
196 // Verify all aborts were called
197 expect(queryPromise1.abort).toHaveBeenCalled()
198 expect(queryPromise2.abort).toHaveBeenCalled()
199 expect(mutationPromise.abort).toHaveBeenCalled()
200 })
201})
202
203function storeForApi<
204 A extends {
205 reducerPath: 'api'
206 reducer: Reducer<any, any>
207 middleware: Middleware
208 util: { resetApiState(): any }
209 },
210>(api: A) {
211 const store = configureStore({
212 reducer: { api: api.reducer },
213 middleware: (gdm) =>
214 gdm({ serializableCheck: false, immutableCheck: false }).concat(
215 api.middleware,
216 ),
217 enhancers: (gde) =>
218 gde({
219 autoBatch: false,
220 }),
221 })
222 let hadQueries = false
223 store.subscribe(() => {
224 const queryState = store.getState().api.queries
225 if (hadQueries && countObjectKeys(queryState) === 0) {
226 onCleanup()
227 }
228 hadQueries = hadQueries || countObjectKeys(queryState) > 0
229 })
230 return { api, store }
231}
Note: See TracBrowser for help on using the repository browser.