source: node_modules/@reduxjs/toolkit/src/tests/mapBuilders.test-d.ts

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

Added visualizations

  • Property mode set to 100644
File size: 12.6 KB
Line 
1import type { SerializedError } from '@internal/createAsyncThunk'
2import { createAsyncThunk } from '@internal/createAsyncThunk'
3import { executeReducerBuilderCallback } from '@internal/mapBuilders'
4import type { UnknownAction } from '@reduxjs/toolkit'
5import { createAction } from '@reduxjs/toolkit'
6
7describe('type tests', () => {
8 test('builder callback for actionMap', () => {
9 const increment = createAction<number, 'increment'>('increment')
10
11 const decrement = createAction<number, 'decrement'>('decrement')
12
13 executeReducerBuilderCallback<number>((builder) => {
14 builder.addCase(increment, (state, action) => {
15 expectTypeOf(state).toBeNumber()
16
17 expectTypeOf(action).toEqualTypeOf<{
18 type: 'increment'
19 payload: number
20 }>()
21
22 expectTypeOf(state).not.toBeString()
23
24 expectTypeOf(action).not.toMatchTypeOf<{
25 type: 'increment'
26 payload: string
27 }>()
28
29 expectTypeOf(action).not.toMatchTypeOf<{
30 type: 'decrement'
31 payload: number
32 }>()
33 })
34
35 builder.addCase('increment', (state, action) => {
36 expectTypeOf(state).toBeNumber()
37
38 expectTypeOf(action).toEqualTypeOf<{ type: 'increment' }>()
39
40 expectTypeOf(state).not.toBeString()
41
42 expectTypeOf(action).not.toMatchTypeOf<{ type: 'decrement' }>()
43
44 // this cannot be inferred and has to be manually specified
45 expectTypeOf(action).not.toMatchTypeOf<{
46 type: 'increment'
47 payload: number
48 }>()
49 })
50
51 builder.addCase(
52 increment,
53 (state, action: ReturnType<typeof increment>) => state,
54 )
55
56 // @ts-expect-error
57 builder.addCase(
58 increment,
59 (state, action: ReturnType<typeof decrement>) => state,
60 )
61
62 builder.addCase(
63 'increment',
64 (state, action: ReturnType<typeof increment>) => state,
65 )
66
67 // @ts-expect-error
68 builder.addCase(
69 'decrement',
70 (state, action: ReturnType<typeof increment>) => state,
71 )
72
73 // action type is inferred
74 builder.addMatcher(increment.match, (state, action) => {
75 expectTypeOf(action).toEqualTypeOf<ReturnType<typeof increment>>()
76 })
77
78 test('action type is inferred when type predicate lacks `type` property', () => {
79 type PredicateWithoutTypeProperty = {
80 payload: number
81 }
82
83 builder.addMatcher(
84 (action): action is PredicateWithoutTypeProperty => true,
85 (state, action) => {
86 expectTypeOf(action).toMatchTypeOf<PredicateWithoutTypeProperty>()
87
88 expectTypeOf(action).toMatchTypeOf<UnknownAction>()
89 },
90 )
91 })
92
93 // action type defaults to UnknownAction if no type predicate matcher is passed
94 builder.addMatcher(
95 () => true,
96 (state, action) => {
97 expectTypeOf(action).toMatchTypeOf<UnknownAction>()
98 },
99 )
100
101 // with a boolean checker, action can also be typed by type argument
102 builder.addMatcher<{ foo: boolean }>(
103 () => true,
104 (state, action) => {
105 expectTypeOf(action).toMatchTypeOf<{ foo: boolean }>()
106
107 expectTypeOf(action).toMatchTypeOf<UnknownAction>()
108 },
109 )
110
111 // addCase().addMatcher() is possible, action type inferred correctly
112 builder
113 .addCase(
114 'increment',
115 (state, action: ReturnType<typeof increment>) => state,
116 )
117 .addMatcher(decrement.match, (state, action) => {
118 expectTypeOf(action).toEqualTypeOf<ReturnType<typeof decrement>>()
119 })
120
121 // addCase().addDefaultCase() is possible, action type is UnknownAction
122 builder
123 .addCase(
124 'increment',
125 (state, action: ReturnType<typeof increment>) => state,
126 )
127 .addDefaultCase((state, action) => {
128 expectTypeOf(action).toMatchTypeOf<UnknownAction>()
129 })
130
131 test('addAsyncThunk() should prevent further calls to addCase() ', () => {
132 const asyncThunk = createAsyncThunk('test', () => {})
133 const b = builder.addAsyncThunk(asyncThunk, {
134 pending: () => {},
135 rejected: () => {},
136 fulfilled: () => {},
137 settled: () => {},
138 })
139
140 expectTypeOf(b).not.toHaveProperty('addCase')
141
142 expectTypeOf(b.addAsyncThunk).toBeFunction()
143
144 expectTypeOf(b.addMatcher).toBeCallableWith(increment.match, () => {})
145
146 expectTypeOf(b.addDefaultCase).toBeCallableWith(() => {})
147 })
148
149 test('addMatcher() should prevent further calls to addCase() and addAsyncThunk()', () => {
150 const b = builder.addMatcher(increment.match, () => {})
151
152 expectTypeOf(b).not.toHaveProperty('addCase')
153 expectTypeOf(b).not.toHaveProperty('addAsyncThunk')
154
155 expectTypeOf(b.addMatcher).toBeCallableWith(increment.match, () => {})
156
157 expectTypeOf(b.addDefaultCase).toBeCallableWith(() => {})
158 })
159
160 test('addDefaultCase() should prevent further calls to addCase(), addAsyncThunk(), addMatcher() and addDefaultCase', () => {
161 const b = builder.addDefaultCase(() => {})
162
163 expectTypeOf(b).not.toHaveProperty('addCase')
164
165 expectTypeOf(b).not.toHaveProperty('addAsyncThunk')
166
167 expectTypeOf(b).not.toHaveProperty('addMatcher')
168
169 expectTypeOf(b).not.toHaveProperty('addDefaultCase')
170 })
171
172 describe('`createAsyncThunk` actions work with `mapBuilder`', () => {
173 test('case 1: normal `createAsyncThunk`', () => {
174 const thunk = createAsyncThunk('test', () => {
175 return 'ret' as const
176 })
177 builder.addCase(thunk.pending, (_, action) => {
178 expectTypeOf(action).toMatchTypeOf<{
179 payload: undefined
180 meta: {
181 arg: void
182 requestId: string
183 requestStatus: 'pending'
184 }
185 }>()
186 })
187
188 builder.addCase(thunk.rejected, (_, action) => {
189 expectTypeOf(action).toMatchTypeOf<{
190 payload: unknown
191 error: SerializedError
192 meta: {
193 arg: void
194 requestId: string
195 requestStatus: 'rejected'
196 aborted: boolean
197 condition: boolean
198 rejectedWithValue: boolean
199 }
200 }>()
201 })
202 builder.addCase(thunk.fulfilled, (_, action) => {
203 expectTypeOf(action).toMatchTypeOf<{
204 payload: 'ret'
205 meta: {
206 arg: void
207 requestId: string
208 requestStatus: 'fulfilled'
209 }
210 }>()
211 })
212
213 builder.addAsyncThunk(thunk, {
214 pending(_, action) {
215 expectTypeOf(action).toMatchTypeOf<{
216 payload: undefined
217 meta: {
218 arg: void
219 requestId: string
220 requestStatus: 'pending'
221 }
222 }>()
223 },
224 rejected(_, action) {
225 expectTypeOf(action).toMatchTypeOf<{
226 payload: unknown
227 error: SerializedError
228 meta: {
229 arg: void
230 requestId: string
231 requestStatus: 'rejected'
232 aborted: boolean
233 condition: boolean
234 rejectedWithValue: boolean
235 }
236 }>()
237 },
238 fulfilled(_, action) {
239 expectTypeOf(action).toMatchTypeOf<{
240 payload: 'ret'
241 meta: {
242 arg: void
243 requestId: string
244 requestStatus: 'fulfilled'
245 }
246 }>()
247 },
248 settled(_, action) {
249 expectTypeOf(action).toMatchTypeOf<
250 | {
251 payload: 'ret'
252 meta: {
253 arg: void
254 requestId: string
255 requestStatus: 'fulfilled'
256 }
257 }
258 | {
259 payload: unknown
260 error: SerializedError
261 meta: {
262 arg: void
263 requestId: string
264 requestStatus: 'rejected'
265 aborted: boolean
266 condition: boolean
267 rejectedWithValue: boolean
268 }
269 }
270 >()
271 },
272 })
273 })
274
275 test('case 2: `createAsyncThunk` with `meta`', () => {
276 const thunk = createAsyncThunk<
277 'ret',
278 void,
279 {
280 pendingMeta: { startedTimeStamp: number }
281 fulfilledMeta: {
282 fulfilledTimeStamp: number
283 baseQueryMeta: 'meta!'
284 }
285 rejectedMeta: {
286 baseQueryMeta: 'meta!'
287 }
288 }
289 >(
290 'test',
291 (_, api) => {
292 return api.fulfillWithValue('ret' as const, {
293 fulfilledTimeStamp: 5,
294 baseQueryMeta: 'meta!',
295 })
296 },
297 {
298 getPendingMeta() {
299 return { startedTimeStamp: 0 }
300 },
301 },
302 )
303
304 builder.addCase(thunk.pending, (_, action) => {
305 expectTypeOf(action).toMatchTypeOf<{
306 payload: undefined
307 meta: {
308 arg: void
309 requestId: string
310 requestStatus: 'pending'
311 startedTimeStamp: number
312 }
313 }>()
314 })
315
316 builder.addCase(thunk.rejected, (_, action) => {
317 expectTypeOf(action).toMatchTypeOf<{
318 payload: unknown
319 error: SerializedError
320 meta: {
321 arg: void
322 requestId: string
323 requestStatus: 'rejected'
324 aborted: boolean
325 condition: boolean
326 rejectedWithValue: boolean
327 baseQueryMeta?: 'meta!'
328 }
329 }>()
330
331 if (action.meta.rejectedWithValue) {
332 expectTypeOf(action.meta.baseQueryMeta).toEqualTypeOf<'meta!'>()
333 }
334 })
335 builder.addCase(thunk.fulfilled, (_, action) => {
336 expectTypeOf(action).toMatchTypeOf<{
337 payload: 'ret'
338 meta: {
339 arg: void
340 requestId: string
341 requestStatus: 'fulfilled'
342 baseQueryMeta: 'meta!'
343 }
344 }>()
345 })
346
347 builder.addAsyncThunk(thunk, {
348 pending(_, action) {
349 expectTypeOf(action).toMatchTypeOf<{
350 payload: undefined
351 meta: {
352 arg: void
353 requestId: string
354 requestStatus: 'pending'
355 startedTimeStamp: number
356 }
357 }>()
358 },
359 rejected(_, action) {
360 expectTypeOf(action).toMatchTypeOf<{
361 payload: unknown
362 error: SerializedError
363 meta: {
364 arg: void
365 requestId: string
366 requestStatus: 'rejected'
367 aborted: boolean
368 condition: boolean
369 rejectedWithValue: boolean
370 baseQueryMeta?: 'meta!'
371 }
372 }>()
373 },
374 fulfilled(_, action) {
375 expectTypeOf(action).toMatchTypeOf<{
376 payload: 'ret'
377 meta: {
378 arg: void
379 requestId: string
380 requestStatus: 'fulfilled'
381 baseQueryMeta: 'meta!'
382 }
383 }>()
384 },
385 settled(_, action) {
386 expectTypeOf(action).toMatchTypeOf<
387 | {
388 payload: 'ret'
389 meta: {
390 arg: void
391 requestId: string
392 requestStatus: 'fulfilled'
393 baseQueryMeta: 'meta!'
394 }
395 }
396 | {
397 payload: unknown
398 error: SerializedError
399 meta: {
400 arg: void
401 requestId: string
402 requestStatus: 'rejected'
403 aborted: boolean
404 condition: boolean
405 rejectedWithValue: boolean
406 baseQueryMeta?: 'meta!'
407 }
408 }
409 >()
410 },
411 })
412 })
413 })
414 })
415 })
416})
Note: See TracBrowser for help on using the repository browser.