source: node_modules/@reduxjs/toolkit/src/query/tests/cleanup.test.tsx@ 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: 4.9 KB
Line 
1// tests for "cleanup-after-unsubscribe" behavior
2import React from 'react'
3
4import { createListenerMiddleware } from '@reduxjs/toolkit'
5import { createApi, QueryStatus } from '@reduxjs/toolkit/query/react'
6import { act, render, screen, waitFor } from '@testing-library/react'
7import { setupApiStore } from '../../tests/utils/helpers'
8import type { SubscriptionSelectors } from '../core/buildMiddleware/types'
9
10const api = createApi({
11 baseQuery: () => ({ data: 42 }),
12 endpoints: (build) => ({
13 a: build.query<unknown, void>({ query: () => '' }),
14 b: build.query<unknown, void>({ query: () => '' }),
15 }),
16})
17const storeRef = setupApiStore(api)
18
19const getSubStateA = () => storeRef.store.getState().api.queries['a(undefined)']
20const getSubStateB = () => storeRef.store.getState().api.queries['b(undefined)']
21
22function UsingA() {
23 const { data } = api.endpoints.a.useQuery()
24
25 return <>Result: {data as React.ReactNode} </>
26}
27
28function UsingB() {
29 api.endpoints.b.useQuery()
30
31 return <></>
32}
33
34function UsingAB() {
35 api.endpoints.a.useQuery()
36 api.endpoints.b.useQuery()
37
38 return <></>
39}
40
41beforeAll(() => {
42 vi.useFakeTimers({ shouldAdvanceTime: true })
43})
44
45test('data stays in store when component stays rendered', async () => {
46 expect(getSubStateA()).toBeUndefined()
47
48 render(<UsingA />, { wrapper: storeRef.wrapper })
49 await waitFor(() =>
50 expect(getSubStateA()?.status).toBe(QueryStatus.fulfilled),
51 )
52
53 vi.advanceTimersByTime(120_000)
54
55 expect(getSubStateA()?.status).toBe(QueryStatus.fulfilled)
56})
57
58test('data is removed from store after 60 seconds', async () => {
59 expect(getSubStateA()).toBeUndefined()
60
61 const { unmount } = render(<UsingA />, { wrapper: storeRef.wrapper })
62 await waitFor(() =>
63 expect(getSubStateA()?.status).toBe(QueryStatus.fulfilled),
64 )
65
66 unmount()
67
68 vi.advanceTimersByTime(59_000)
69
70 expect(getSubStateA()?.status).toBe(QueryStatus.fulfilled)
71
72 vi.advanceTimersByTime(2000)
73
74 expect(getSubStateA()).toBeUndefined()
75})
76
77test('data stays in store when component stays rendered while data for another component is removed after it unmounted', async () => {
78 expect(getSubStateA()).toBeUndefined()
79 expect(getSubStateB()).toBeUndefined()
80
81 const { rerender } = render(
82 <>
83 <UsingA />
84 <UsingB />
85 </>,
86 { wrapper: storeRef.wrapper },
87 )
88 await waitFor(() => {
89 expect(getSubStateA()?.status).toBe(QueryStatus.fulfilled)
90 expect(getSubStateB()?.status).toBe(QueryStatus.fulfilled)
91 })
92
93 const statusA = getSubStateA()
94
95 await act(async () => {
96 rerender(<UsingA />)
97
98 vi.advanceTimersByTime(10)
99 })
100
101 vi.advanceTimersByTime(120_000)
102
103 expect(getSubStateA()).toEqual(statusA)
104 expect(getSubStateB()).toBeUndefined()
105})
106
107test('data stays in store when one component requiring the data stays in the store', async () => {
108 expect(getSubStateA()).toBeUndefined()
109 expect(getSubStateB()).toBeUndefined()
110
111 const { rerender } = render(
112 <>
113 <UsingA key="a" />
114 <UsingAB key="ab" />
115 </>,
116 { wrapper: storeRef.wrapper },
117 )
118 await waitFor(() => {
119 expect(getSubStateA()?.status).toBe(QueryStatus.fulfilled)
120 expect(getSubStateB()?.status).toBe(QueryStatus.fulfilled)
121 })
122
123 const statusA = getSubStateA()
124 const statusB = getSubStateB()
125
126 await act(async () => {
127 rerender(<UsingAB key="ab" />)
128 vi.advanceTimersByTime(10)
129 vi.runAllTimers()
130 })
131
132 await act(async () => {
133 vi.advanceTimersByTime(120000)
134 vi.runAllTimers()
135 })
136
137 expect(getSubStateA()).toEqual(statusA)
138 expect(getSubStateB()).toEqual(statusB)
139})
140
141test('Minimizes the number of subscription dispatches when multiple components ask for the same data', async () => {
142 const listenerMiddleware = createListenerMiddleware()
143 const storeRef = setupApiStore(api, undefined, {
144 middleware: {
145 concat: [listenerMiddleware.middleware],
146 },
147 withoutTestLifecycles: true,
148 })
149
150 const actionTypes: unknown[] = []
151
152 listenerMiddleware.startListening({
153 predicate: () => true,
154 effect: (action) => {
155 if (
156 action.type.includes('subscriptionsUpdated') ||
157 action.type.includes('internal_')
158 ) {
159 return
160 }
161
162 actionTypes.push(action.type)
163 },
164 })
165
166 const { getSubscriptionCount } = storeRef.store.dispatch(
167 api.internalActions.internal_getRTKQSubscriptions(),
168 ) as unknown as SubscriptionSelectors
169
170 const NUM_LIST_ITEMS = 1000
171
172 function ParentComponent() {
173 const listItems = Array.from({ length: NUM_LIST_ITEMS }).map((_, i) => (
174 <UsingA key={i} />
175 ))
176
177 return <>{listItems}</>
178 }
179
180 render(<ParentComponent />, {
181 wrapper: storeRef.wrapper,
182 })
183
184 await act(async () => {
185 vi.advanceTimersByTime(10)
186 vi.runAllTimers()
187 })
188
189 await waitFor(() => {
190 return screen.getAllByText(/42/).length > 0
191 })
192
193 expect(getSubscriptionCount('a(undefined)')).toBe(NUM_LIST_ITEMS)
194
195 expect(actionTypes).toEqual([
196 'api/config/middlewareRegistered',
197 'api/executeQuery/pending',
198 'api/executeQuery/fulfilled',
199 ])
200}, 25_000)
Note: See TracBrowser for help on using the repository browser.