source: node_modules/@reduxjs/toolkit/src/query/tests/apiProvider.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.8 KB
Line 
1import { configureStore } from '@reduxjs/toolkit'
2import {
3 ApiProvider,
4 buildCreateApi,
5 coreModule,
6 createApi,
7 reactHooksModule,
8} from '@reduxjs/toolkit/query/react'
9import { fireEvent, render, waitFor } from '@testing-library/react'
10import { delay } from 'msw'
11import * as React from 'react'
12import type { ReactReduxContextValue } from 'react-redux'
13import {
14 Provider,
15 createDispatchHook,
16 createSelectorHook,
17 createStoreHook,
18} from 'react-redux'
19
20const api = createApi({
21 baseQuery: async (arg: any) => {
22 await delay(150)
23 return { data: arg?.body ? arg.body : null }
24 },
25 endpoints: (build) => ({
26 getUser: build.query<any, number>({
27 query: (arg) => arg,
28 }),
29 updateUser: build.mutation<any, { name: string }>({
30 query: (update) => ({ body: update }),
31 }),
32 }),
33})
34
35afterEach(() => {
36 vi.resetAllMocks()
37})
38
39describe('ApiProvider', () => {
40 test('ApiProvider allows a user to make queries without a traditional Redux setup', async () => {
41 function User() {
42 const [value, setValue] = React.useState(0)
43
44 const { isFetching } = api.endpoints.getUser.useQuery(1, {
45 skip: value < 1,
46 })
47
48 return (
49 <div>
50 <div data-testid="isFetching">{String(isFetching)}</div>
51 <button onClick={() => setValue((val) => val + 1)}>
52 Increment value
53 </button>
54 </div>
55 )
56 }
57
58 const { getByText, getByTestId } = render(
59 <ApiProvider api={api}>
60 <User />
61 </ApiProvider>,
62 )
63
64 await waitFor(() =>
65 expect(getByTestId('isFetching').textContent).toBe('false'),
66 )
67 fireEvent.click(getByText('Increment value'))
68 await waitFor(() =>
69 expect(getByTestId('isFetching').textContent).toBe('true'),
70 )
71 await waitFor(() =>
72 expect(getByTestId('isFetching').textContent).toBe('false'),
73 )
74 fireEvent.click(getByText('Increment value'))
75 // Being that nothing has changed in the args, this should never fire.
76 expect(getByTestId('isFetching').textContent).toBe('false')
77 })
78 test('ApiProvider throws if nested inside a Redux context', () => {
79 // Intentionally swallow the "unhandled error" message
80 vi.spyOn(console, 'error').mockImplementation(() => {})
81 expect(() =>
82 render(
83 <Provider store={configureStore({ reducer: () => null })}>
84 <ApiProvider api={api}>child</ApiProvider>
85 </Provider>,
86 ),
87 ).toThrowErrorMatchingInlineSnapshot(
88 `[Error: Existing Redux context detected. If you already have a store set up, please use the traditional Redux setup.]`,
89 )
90 })
91 test('ApiProvider allows a custom context', async () => {
92 const customContext = React.createContext<ReactReduxContextValue | null>(
93 null,
94 )
95
96 const createApiWithCustomContext = buildCreateApi(
97 coreModule(),
98 reactHooksModule({
99 hooks: {
100 useStore: createStoreHook(customContext),
101 useSelector: createSelectorHook(customContext),
102 useDispatch: createDispatchHook(customContext),
103 },
104 }),
105 )
106
107 const customApi = createApiWithCustomContext({
108 baseQuery: async (arg: any) => {
109 await delay(150)
110 return { data: arg?.body ? arg.body : null }
111 },
112 endpoints: (build) => ({
113 getUser: build.query<any, number>({
114 query: (arg) => arg,
115 }),
116 updateUser: build.mutation<any, { name: string }>({
117 query: (update) => ({ body: update }),
118 }),
119 }),
120 })
121
122 function User() {
123 const [value, setValue] = React.useState(0)
124
125 const { isFetching } = customApi.endpoints.getUser.useQuery(1, {
126 skip: value < 1,
127 })
128
129 return (
130 <div>
131 <div data-testid="isFetching">{String(isFetching)}</div>
132 <button onClick={() => setValue((val) => val + 1)}>
133 Increment value
134 </button>
135 </div>
136 )
137 }
138
139 const { getByText, getByTestId } = render(
140 <ApiProvider api={customApi} context={customContext}>
141 <User />
142 </ApiProvider>,
143 )
144
145 await waitFor(() =>
146 expect(getByTestId('isFetching').textContent).toBe('false'),
147 )
148 fireEvent.click(getByText('Increment value'))
149 await waitFor(() =>
150 expect(getByTestId('isFetching').textContent).toBe('true'),
151 )
152 await waitFor(() =>
153 expect(getByTestId('isFetching').textContent).toBe('false'),
154 )
155 fireEvent.click(getByText('Increment value'))
156 // Being that nothing has changed in the args, this should never fire.
157 expect(getByTestId('isFetching').textContent).toBe('false')
158
159 // won't throw if nested, because context is different
160 expect(() =>
161 render(
162 <Provider store={configureStore({ reducer: () => null })}>
163 <ApiProvider api={customApi} context={customContext}>
164 child
165 </ApiProvider>
166 </Provider>,
167 ),
168 ).not.toThrow()
169 })
170})
Note: See TracBrowser for help on using the repository browser.