1
|
|
|
import indexCrudReducer, { |
2
|
|
|
ActionTypes, |
3
|
|
|
CreateFulfillAction, |
4
|
|
|
CreateRejectAction, |
5
|
|
|
CreateStartAction, |
6
|
|
|
DeleteFulfillAction, |
7
|
|
|
DeleteRejectAction, |
8
|
|
|
DeleteStartAction, |
9
|
|
|
IndexFulfillAction, |
10
|
|
|
IndexRejectAction, |
11
|
|
|
IndexStartAction, |
12
|
|
|
initializeState, |
13
|
|
|
ResourceState, |
14
|
|
|
UpdateFulfillAction, |
15
|
|
|
UpdateRejectAction, |
16
|
|
|
UpdateStartAction, |
17
|
|
|
} from "./indexCrudReducer"; |
18
|
|
|
|
19
|
|
|
interface TestResource { |
20
|
|
|
id: number; |
21
|
|
|
name: string; |
22
|
|
|
} |
23
|
|
|
function addKey<T extends { id: number }>(item: T): { item: T; key: string } { |
24
|
|
|
return { |
25
|
|
|
item, |
26
|
|
|
key: String(item.id), |
27
|
|
|
}; |
28
|
|
|
} |
29
|
|
|
|
30
|
|
|
describe("indexCrudReducer tests", (): void => { |
31
|
|
|
describe("Test INDEX actions", () => { |
32
|
|
|
it("updates indexMeta in response to INDEX START", () => { |
33
|
|
|
const initialState = initializeState([]); |
34
|
|
|
const expectState = { |
35
|
|
|
...initialState, |
36
|
|
|
indexMeta: { |
37
|
|
|
status: "pending", |
38
|
|
|
pendingCount: 1, |
39
|
|
|
error: undefined, |
40
|
|
|
}, |
41
|
|
|
}; |
42
|
|
|
const action: IndexStartAction = { type: ActionTypes.IndexStart }; |
43
|
|
|
expect(indexCrudReducer(initialState, action)).toEqual(expectState); |
44
|
|
|
}); |
45
|
|
|
it("increments index pendingCount twice for two INDEX START actions", () => { |
46
|
|
|
const initialState = initializeState([]); |
47
|
|
|
const expectState = { |
48
|
|
|
...initialState, |
49
|
|
|
indexMeta: { |
50
|
|
|
status: "pending", |
51
|
|
|
pendingCount: 2, |
52
|
|
|
error: undefined, |
53
|
|
|
}, |
54
|
|
|
}; |
55
|
|
|
const action: IndexStartAction = { type: ActionTypes.IndexStart }; |
56
|
|
|
const state1 = indexCrudReducer(initialState, action); |
57
|
|
|
const state2 = indexCrudReducer(state1, action); |
58
|
|
|
expect(state2).toEqual(expectState); |
59
|
|
|
}); |
60
|
|
|
it("decrements pending count, updates status, and saves values when INDEX FULFILLED", () => { |
61
|
|
|
const initialState: ResourceState<TestResource> = { |
62
|
|
|
...initializeState([]), |
63
|
|
|
indexMeta: { |
64
|
|
|
status: "pending", |
65
|
|
|
pendingCount: 1, |
66
|
|
|
error: undefined, |
67
|
|
|
}, |
68
|
|
|
}; |
69
|
|
|
const action: IndexFulfillAction<TestResource> = { |
70
|
|
|
type: ActionTypes.IndexFulfill, |
71
|
|
|
payload: [ |
72
|
|
|
{ id: 1, name: "one" }, |
73
|
|
|
{ id: 2, name: "two" }, |
74
|
|
|
].map(addKey), |
75
|
|
|
}; |
76
|
|
|
const expectState = { |
77
|
|
|
...initialState, |
78
|
|
|
indexMeta: { |
79
|
|
|
status: "fulfilled", |
80
|
|
|
pendingCount: 0, |
81
|
|
|
error: undefined, |
82
|
|
|
}, |
83
|
|
|
values: { |
84
|
|
|
1: { |
85
|
|
|
value: { id: 1, name: "one" }, |
86
|
|
|
status: "fulfilled", |
87
|
|
|
pendingCount: 0, |
88
|
|
|
error: undefined, |
89
|
|
|
}, |
90
|
|
|
2: { |
91
|
|
|
value: { id: 2, name: "two" }, |
92
|
|
|
status: "fulfilled", |
93
|
|
|
pendingCount: 0, |
94
|
|
|
error: undefined, |
95
|
|
|
}, |
96
|
|
|
}, |
97
|
|
|
}; |
98
|
|
|
expect(indexCrudReducer(initialState, action)).toEqual(expectState); |
99
|
|
|
}); |
100
|
|
|
it("INDEX FULFILLED overwrites item values and errors, but not pendingCount (and status if pending), if they already exist", () => { |
101
|
|
|
const initialState: ResourceState<TestResource> = { |
102
|
|
|
...initializeState([]), |
103
|
|
|
indexMeta: { |
104
|
|
|
status: "pending", |
105
|
|
|
pendingCount: 1, |
106
|
|
|
error: new Error(), |
107
|
|
|
}, |
108
|
|
|
values: { |
109
|
|
|
1: { |
110
|
|
|
value: { id: 1, name: "one" }, |
111
|
|
|
status: "pending", |
112
|
|
|
pendingCount: 2, |
113
|
|
|
error: undefined, |
114
|
|
|
}, |
115
|
|
|
2: { |
116
|
|
|
value: { id: 2, name: "two" }, |
117
|
|
|
status: "pending", |
118
|
|
|
pendingCount: 1, |
119
|
|
|
error: new Error("Something went wrong with a pretend request."), |
120
|
|
|
}, |
121
|
|
|
3: { |
122
|
|
|
value: { id: 3, name: "three" }, |
123
|
|
|
status: "rejected", |
124
|
|
|
pendingCount: 0, |
125
|
|
|
error: new Error("Something went wrong with a pretend request."), |
126
|
|
|
}, |
127
|
|
|
4: { |
128
|
|
|
value: { id: 4, name: "four" }, |
129
|
|
|
status: "initial", |
130
|
|
|
pendingCount: 0, |
131
|
|
|
error: undefined, |
132
|
|
|
}, |
133
|
|
|
}, |
134
|
|
|
}; |
135
|
|
|
const action: IndexFulfillAction<TestResource> = { |
136
|
|
|
type: ActionTypes.IndexFulfill, |
137
|
|
|
payload: [ |
138
|
|
|
{ id: 1, name: "new one" }, |
139
|
|
|
{ id: 2, name: "new two" }, |
140
|
|
|
{ id: 3, name: "new three" }, |
141
|
|
|
{ id: 4, name: "new four" }, |
142
|
|
|
].map(addKey), |
143
|
|
|
}; |
144
|
|
|
const expectState = { |
145
|
|
|
...initialState, |
146
|
|
|
indexMeta: { |
147
|
|
|
status: "fulfilled", |
148
|
|
|
pendingCount: 0, |
149
|
|
|
error: undefined, |
150
|
|
|
}, |
151
|
|
|
values: { |
152
|
|
|
1: { |
153
|
|
|
...initialState.values[1], |
154
|
|
|
value: { id: 1, name: "new one" }, |
155
|
|
|
}, |
156
|
|
|
2: { |
157
|
|
|
...initialState.values[2], |
158
|
|
|
value: { id: 2, name: "new two" }, |
159
|
|
|
error: undefined, // Overwrites error, but not pendingCount and pending status |
160
|
|
|
}, |
161
|
|
|
3: { |
162
|
|
|
value: { id: 3, name: "new three" }, |
163
|
|
|
status: "fulfilled", // Rejected status replaced with fulfilled |
164
|
|
|
pendingCount: 0, |
165
|
|
|
error: undefined, |
166
|
|
|
}, |
167
|
|
|
4: { |
168
|
|
|
value: { id: 4, name: "new four" }, |
169
|
|
|
status: "fulfilled", // Initial status replaced with fulfilled |
170
|
|
|
pendingCount: 0, |
171
|
|
|
error: undefined, |
172
|
|
|
}, |
173
|
|
|
}, |
174
|
|
|
}; |
175
|
|
|
expect(indexCrudReducer(initialState, action)).toEqual(expectState); |
176
|
|
|
}); |
177
|
|
|
it("INDEX FULFILLED deletes values not included in payload", () => { |
178
|
|
|
const initialState: ResourceState<TestResource> = { |
179
|
|
|
...initializeState( |
180
|
|
|
[ |
181
|
|
|
{ id: 1, name: "one" }, |
182
|
|
|
{ id: 2, name: "two" }, |
183
|
|
|
].map(addKey), |
184
|
|
|
), |
185
|
|
|
indexMeta: { |
186
|
|
|
status: "pending", |
187
|
|
|
pendingCount: 1, |
188
|
|
|
error: undefined, |
189
|
|
|
}, |
190
|
|
|
}; |
191
|
|
|
const action: IndexFulfillAction<TestResource> = { |
192
|
|
|
type: ActionTypes.IndexFulfill, |
193
|
|
|
payload: [{ id: 2, name: "new two" }].map(addKey), |
194
|
|
|
}; |
195
|
|
|
const expectState = { |
196
|
|
|
...initialState, |
197
|
|
|
indexMeta: { |
198
|
|
|
status: "fulfilled", |
199
|
|
|
pendingCount: 0, |
200
|
|
|
error: undefined, |
201
|
|
|
}, |
202
|
|
|
values: { |
203
|
|
|
2: { |
204
|
|
|
...initialState.values[2], |
205
|
|
|
value: { id: 2, name: "new two" }, |
206
|
|
|
status: "fulfilled", |
207
|
|
|
}, |
208
|
|
|
}, |
209
|
|
|
}; |
210
|
|
|
expect(indexCrudReducer(initialState, action)).toEqual(expectState); |
211
|
|
|
}); |
212
|
|
|
it("INDEX REJECTED decrements pendingCount, sets status to rejected, and doesn't modify values", () => { |
213
|
|
|
const initialState: ResourceState<TestResource> = { |
214
|
|
|
...initializeState([{ id: 1, name: "one" }].map(addKey)), |
215
|
|
|
indexMeta: { |
216
|
|
|
status: "pending", |
217
|
|
|
pendingCount: 1, |
218
|
|
|
error: undefined, |
219
|
|
|
}, |
220
|
|
|
}; |
221
|
|
|
const action: IndexRejectAction = { |
222
|
|
|
type: ActionTypes.IndexReject, |
223
|
|
|
payload: new Error("Something went wrong with a fake request"), |
224
|
|
|
}; |
225
|
|
|
const expectState = { |
226
|
|
|
...initialState, |
227
|
|
|
indexMeta: { |
228
|
|
|
status: "rejected", |
229
|
|
|
pendingCount: 0, |
230
|
|
|
error: action.payload, |
231
|
|
|
}, |
232
|
|
|
// Values are unchanged |
233
|
|
|
}; |
234
|
|
|
expect(indexCrudReducer(initialState, action)).toEqual(expectState); |
235
|
|
|
}); |
236
|
|
|
it("status remains 'pending' after INDEX FULFILLED and INDEX REJECTED if pendingCount was higher than 1", () => { |
237
|
|
|
const initialState: ResourceState<TestResource> = { |
238
|
|
|
...initializeState([]), |
239
|
|
|
indexMeta: { |
240
|
|
|
status: "pending", |
241
|
|
|
pendingCount: 2, |
242
|
|
|
error: undefined, |
243
|
|
|
}, |
244
|
|
|
}; |
245
|
|
|
const fulfilledAction: IndexFulfillAction<TestResource> = { |
246
|
|
|
type: ActionTypes.IndexFulfill, |
247
|
|
|
payload: [], |
248
|
|
|
}; |
249
|
|
|
const fulfilledState = { |
250
|
|
|
...initialState, |
251
|
|
|
indexMeta: { |
252
|
|
|
status: "pending", |
253
|
|
|
pendingCount: 1, |
254
|
|
|
error: undefined, |
255
|
|
|
}, |
256
|
|
|
}; |
257
|
|
|
expect(indexCrudReducer(initialState, fulfilledAction)).toEqual( |
258
|
|
|
fulfilledState, |
259
|
|
|
); |
260
|
|
|
const rejectedAction: IndexRejectAction = { |
261
|
|
|
type: ActionTypes.IndexReject, |
262
|
|
|
payload: new Error(), |
263
|
|
|
}; |
264
|
|
|
const rejectedState = { |
265
|
|
|
...initialState, |
266
|
|
|
indexMeta: { |
267
|
|
|
status: "pending", |
268
|
|
|
pendingCount: 1, |
269
|
|
|
error: rejectedAction.payload, |
270
|
|
|
}, |
271
|
|
|
}; |
272
|
|
|
expect(indexCrudReducer(initialState, rejectedAction)).toEqual( |
273
|
|
|
rejectedState, |
274
|
|
|
); |
275
|
|
|
}); |
276
|
|
|
}); |
277
|
|
|
describe("Test CREATE actions", (): void => { |
278
|
|
|
it("CREATE START updates createMeta", () => { |
279
|
|
|
const initialState: ResourceState<TestResource> = initializeState([]); |
280
|
|
|
const expectState = { |
281
|
|
|
...initialState, |
282
|
|
|
createMeta: { |
283
|
|
|
status: "pending", |
284
|
|
|
pendingCount: 1, |
285
|
|
|
error: undefined, |
286
|
|
|
}, |
287
|
|
|
}; |
288
|
|
|
const action: CreateStartAction<TestResource> = { |
289
|
|
|
type: ActionTypes.CreateStart, |
290
|
|
|
meta: { item: { id: 1, name: "one" } }, |
291
|
|
|
}; |
292
|
|
|
expect(indexCrudReducer(initialState, action)).toEqual(expectState); |
293
|
|
|
}); |
294
|
|
|
it("two CREATE START actions increment pendingCount twice", () => { |
295
|
|
|
const initialState: ResourceState<TestResource> = initializeState([]); |
296
|
|
|
const expectState = { |
297
|
|
|
...initialState, |
298
|
|
|
createMeta: { |
299
|
|
|
status: "pending", |
300
|
|
|
pendingCount: 2, |
301
|
|
|
error: undefined, |
302
|
|
|
}, |
303
|
|
|
}; |
304
|
|
|
const action: CreateStartAction<TestResource> = { |
305
|
|
|
type: ActionTypes.CreateStart, |
306
|
|
|
meta: { item: { id: 1, name: "one" } }, |
307
|
|
|
}; |
308
|
|
|
const state1 = indexCrudReducer(initialState, action); |
309
|
|
|
const state2 = indexCrudReducer(state1, action); |
310
|
|
|
expect(state2).toEqual(expectState); |
311
|
|
|
}); |
312
|
|
|
it("CREATE FULFILLED decrements pending count, updates status, and saves new value", () => { |
313
|
|
|
const initialState: ResourceState<TestResource> = { |
314
|
|
|
...initializeState([]), |
315
|
|
|
createMeta: { |
316
|
|
|
status: "pending", |
317
|
|
|
pendingCount: 1, |
318
|
|
|
error: undefined, |
319
|
|
|
}, |
320
|
|
|
}; |
321
|
|
|
const action: CreateFulfillAction<TestResource> = { |
322
|
|
|
type: ActionTypes.CreateFulfill, |
323
|
|
|
payload: addKey({ id: 1, name: "one" }), |
324
|
|
|
meta: { item: { id: 0, name: "one" } }, |
325
|
|
|
}; |
326
|
|
|
const expectState = { |
327
|
|
|
...initialState, |
328
|
|
|
createMeta: { |
329
|
|
|
status: "fulfilled", |
330
|
|
|
pendingCount: 0, |
331
|
|
|
error: undefined, |
332
|
|
|
}, |
333
|
|
|
values: { |
334
|
|
|
1: { |
335
|
|
|
value: { id: 1, name: "one" }, |
336
|
|
|
status: "fulfilled", |
337
|
|
|
pendingCount: 0, |
338
|
|
|
error: undefined, |
339
|
|
|
}, |
340
|
|
|
}, |
341
|
|
|
}; |
342
|
|
|
expect(indexCrudReducer(initialState, action)).toEqual(expectState); |
343
|
|
|
}); |
344
|
|
|
it("CREATE FULFILLED overwrites an error", () => { |
345
|
|
|
const initialState: ResourceState<TestResource> = { |
346
|
|
|
...initializeState([]), |
347
|
|
|
createMeta: { |
348
|
|
|
status: "pending", |
349
|
|
|
pendingCount: 1, |
350
|
|
|
error: new Error(), |
351
|
|
|
}, |
352
|
|
|
}; |
353
|
|
|
const action: CreateFulfillAction<TestResource> = { |
354
|
|
|
type: ActionTypes.CreateFulfill, |
355
|
|
|
payload: addKey({ id: 1, name: "one" }), |
356
|
|
|
meta: { item: { id: 0, name: "one" } }, |
357
|
|
|
}; |
358
|
|
|
expect( |
359
|
|
|
indexCrudReducer(initialState, action).createMeta.error, |
360
|
|
|
).toBeUndefined(); |
361
|
|
|
}); |
362
|
|
|
it("CREATE REJECTED decrements pendingCount, sets status to rejected, and doesn't modify values", () => { |
363
|
|
|
const initialState: ResourceState<TestResource> = { |
364
|
|
|
...initializeState([{ id: 1, name: "one" }].map(addKey)), |
365
|
|
|
createMeta: { |
366
|
|
|
status: "pending", |
367
|
|
|
pendingCount: 1, |
368
|
|
|
error: undefined, |
369
|
|
|
}, |
370
|
|
|
}; |
371
|
|
|
const action: CreateRejectAction<TestResource> = { |
372
|
|
|
type: ActionTypes.CreateReject, |
373
|
|
|
payload: new Error("Something went wrong with a fake request"), |
374
|
|
|
meta: { item: { id: 0, name: "two" } }, |
375
|
|
|
}; |
376
|
|
|
const expectState = { |
377
|
|
|
...initialState, |
378
|
|
|
createMeta: { |
379
|
|
|
status: "rejected", |
380
|
|
|
pendingCount: 0, |
381
|
|
|
error: action.payload, |
382
|
|
|
}, |
383
|
|
|
// Values are unchanged |
384
|
|
|
}; |
385
|
|
|
expect(indexCrudReducer(initialState, action)).toEqual(expectState); |
386
|
|
|
}); |
387
|
|
|
it("status remains 'pending' after CREATE FULFILLED and CREATE REJECTED if pendingCount was higher than 1", () => { |
388
|
|
|
const initialState: ResourceState<TestResource> = { |
389
|
|
|
...initializeState([]), |
390
|
|
|
createMeta: { |
391
|
|
|
status: "pending", |
392
|
|
|
pendingCount: 2, |
393
|
|
|
error: undefined, |
394
|
|
|
}, |
395
|
|
|
}; |
396
|
|
|
const fulfilledAction: CreateFulfillAction<TestResource> = { |
397
|
|
|
type: ActionTypes.CreateFulfill, |
398
|
|
|
payload: addKey({ id: 1, name: "one" }), |
399
|
|
|
meta: { item: { id: 0, name: "one" } }, |
400
|
|
|
}; |
401
|
|
|
const fulfilledState = indexCrudReducer(initialState, fulfilledAction); |
402
|
|
|
expect(fulfilledState.createMeta).toEqual({ |
403
|
|
|
status: "pending", |
404
|
|
|
pendingCount: 1, |
405
|
|
|
error: undefined, |
406
|
|
|
}); |
407
|
|
|
const rejectedAction: CreateRejectAction<TestResource> = { |
408
|
|
|
type: ActionTypes.CreateReject, |
409
|
|
|
payload: new Error(), |
410
|
|
|
meta: { item: { id: 0, name: "one" } }, |
411
|
|
|
}; |
412
|
|
|
const rejectedState = indexCrudReducer(initialState, rejectedAction); |
413
|
|
|
expect(rejectedState.createMeta).toEqual({ |
414
|
|
|
status: "pending", |
415
|
|
|
pendingCount: 1, |
416
|
|
|
error: rejectedAction.payload, |
417
|
|
|
}); |
418
|
|
|
}); |
419
|
|
|
}); |
420
|
|
|
describe("Test UPDATE actions", (): void => { |
421
|
|
|
it("UPDATE START updates item-specific metadata", () => { |
422
|
|
|
const initialState: ResourceState<TestResource> = initializeState( |
423
|
|
|
[{ id: 1, name: "one" }].map(addKey), |
424
|
|
|
); |
425
|
|
|
const expectState = { |
426
|
|
|
...initialState, |
427
|
|
|
values: { |
428
|
|
|
1: { |
429
|
|
|
value: initialState.values[1].value, |
430
|
|
|
status: "pending", |
431
|
|
|
pendingCount: 1, |
432
|
|
|
error: undefined, |
433
|
|
|
}, |
434
|
|
|
}, |
435
|
|
|
}; |
436
|
|
|
const action: UpdateStartAction<TestResource> = { |
437
|
|
|
type: ActionTypes.UpdateStart, |
438
|
|
|
meta: addKey({ id: 1, name: "one" }), |
439
|
|
|
}; |
440
|
|
|
expect(indexCrudReducer(initialState, action)).toEqual(expectState); |
441
|
|
|
}); |
442
|
|
|
it("two UPDATE START actions increment pendingCount twice", () => { |
443
|
|
|
const initialState: ResourceState<TestResource> = initializeState( |
444
|
|
|
[{ id: 1, name: "one" }].map(addKey), |
445
|
|
|
); |
446
|
|
|
const expectState = { |
447
|
|
|
...initialState, |
448
|
|
|
values: { |
449
|
|
|
1: { |
450
|
|
|
value: initialState.values[1].value, |
451
|
|
|
status: "pending", |
452
|
|
|
pendingCount: 2, // pendingCount is 2 this time |
453
|
|
|
error: undefined, |
454
|
|
|
}, |
455
|
|
|
}, |
456
|
|
|
}; |
457
|
|
|
const action: UpdateStartAction<TestResource> = { |
458
|
|
|
type: ActionTypes.UpdateStart, |
459
|
|
|
meta: addKey({ id: 1, name: "one" }), |
460
|
|
|
}; |
461
|
|
|
const state1 = indexCrudReducer(initialState, action); |
462
|
|
|
const state2 = indexCrudReducer(state1, action); |
463
|
|
|
expect(state2).toEqual(expectState); |
464
|
|
|
}); |
465
|
|
|
it("UPDATE START overwrites an error status", () => { |
466
|
|
|
const initialState: ResourceState<TestResource> = { |
467
|
|
|
...initializeState([]), |
468
|
|
|
values: { |
469
|
|
|
1: { |
470
|
|
|
value: { id: 1, name: "one" }, |
471
|
|
|
status: "rejected", |
472
|
|
|
pendingCount: 0, |
473
|
|
|
error: new Error(), |
474
|
|
|
}, |
475
|
|
|
}, |
476
|
|
|
}; |
477
|
|
|
const expectState = { |
478
|
|
|
...initialState, |
479
|
|
|
values: { |
480
|
|
|
1: { |
481
|
|
|
value: initialState.values[1].value, |
482
|
|
|
status: "pending", |
483
|
|
|
pendingCount: 1, |
484
|
|
|
error: undefined, // Note the error has been erased after new request starts. |
485
|
|
|
}, |
486
|
|
|
}, |
487
|
|
|
}; |
488
|
|
|
const action: UpdateStartAction<TestResource> = { |
489
|
|
|
type: ActionTypes.UpdateStart, |
490
|
|
|
meta: addKey({ id: 1, name: "one" }), |
491
|
|
|
}; |
492
|
|
|
expect(indexCrudReducer(initialState, action)).toEqual(expectState); |
493
|
|
|
}); |
494
|
|
|
it("UPDATE FULFILLED decrements pending count, updates status, and updates value", () => { |
495
|
|
|
const initialState: ResourceState<TestResource> = { |
496
|
|
|
...initializeState([]), |
497
|
|
|
values: { |
498
|
|
|
1: { |
499
|
|
|
value: { id: 1, name: "one" }, |
500
|
|
|
status: "pending", |
501
|
|
|
pendingCount: 1, |
502
|
|
|
error: undefined, |
503
|
|
|
}, |
504
|
|
|
}, |
505
|
|
|
}; |
506
|
|
|
const action: UpdateFulfillAction<TestResource> = { |
507
|
|
|
type: ActionTypes.UpdateFulfill, |
508
|
|
|
payload: { id: 1, name: "NEW one" }, |
509
|
|
|
meta: addKey({ id: 1, name: "NEW one" }), |
510
|
|
|
}; |
511
|
|
|
const expectState = { |
512
|
|
|
...initialState, |
513
|
|
|
values: { |
514
|
|
|
1: { |
515
|
|
|
value: action.payload, |
516
|
|
|
status: "fulfilled", |
517
|
|
|
pendingCount: 0, |
518
|
|
|
error: undefined, |
519
|
|
|
}, |
520
|
|
|
}, |
521
|
|
|
}; |
522
|
|
|
expect(indexCrudReducer(initialState, action)).toEqual(expectState); |
523
|
|
|
}); |
524
|
|
|
it("UPDATE FULFILLED does not change data for other values", () => { |
525
|
|
|
const initialState = initializeState( |
526
|
|
|
[ |
527
|
|
|
{ id: 1, name: "one" }, |
528
|
|
|
{ id: 2, name: "two" }, |
529
|
|
|
].map(addKey), |
530
|
|
|
); |
531
|
|
|
const action: UpdateFulfillAction<TestResource> = { |
532
|
|
|
type: ActionTypes.UpdateFulfill, |
533
|
|
|
payload: { id: 1, name: "NEW one" }, |
534
|
|
|
meta: addKey({ id: 1, name: "NEW one" }), |
535
|
|
|
}; |
536
|
|
|
const fulfilledState = indexCrudReducer(initialState, action); |
537
|
|
|
expect(fulfilledState.values[2]).toEqual(initialState.values[2]); |
538
|
|
|
expect(fulfilledState.values[1]).not.toEqual(initialState.values[1]); |
539
|
|
|
}); |
540
|
|
|
it("UPDATE FULFILLED saves payload, not metadata, as new value in store", () => { |
541
|
|
|
const initialState = initializeState( |
542
|
|
|
[{ id: 1, name: "one" }].map(addKey), |
543
|
|
|
); |
544
|
|
|
const action: UpdateFulfillAction<TestResource> = { |
545
|
|
|
type: ActionTypes.UpdateFulfill, |
546
|
|
|
payload: { id: 1, name: "NEW one" }, |
547
|
|
|
meta: addKey({ id: 1, name: "Doesn't matter what metadata value is" }), |
548
|
|
|
}; |
549
|
|
|
const fulfilledState = indexCrudReducer(initialState, action); |
550
|
|
|
expect(fulfilledState.values[1].value).toEqual(action.payload); |
551
|
|
|
expect(fulfilledState.values[1].value).not.toEqual(action.meta.item); |
552
|
|
|
}); |
553
|
|
|
it("UPDATE FULFILLED overwrites an error", () => { |
554
|
|
|
const initialState: ResourceState<TestResource> = { |
555
|
|
|
...initializeState([]), |
556
|
|
|
values: { |
557
|
|
|
1: { |
558
|
|
|
value: { id: 1, name: "one" }, |
559
|
|
|
status: "pending", |
560
|
|
|
pendingCount: 1, |
561
|
|
|
error: new Error(), |
562
|
|
|
}, |
563
|
|
|
}, |
564
|
|
|
}; |
565
|
|
|
const action: UpdateFulfillAction<TestResource> = { |
566
|
|
|
type: ActionTypes.UpdateFulfill, |
567
|
|
|
payload: { id: 1, name: "NEW one" }, |
568
|
|
|
meta: addKey({ id: 1, name: "NEW one" }), |
569
|
|
|
}; |
570
|
|
|
const fulfilledState = indexCrudReducer(initialState, action); |
571
|
|
|
expect(fulfilledState.values[1].error).toBeUndefined(); |
572
|
|
|
}); |
573
|
|
|
it("UPDATE REJECTED decrements pendingCount, sets status to rejected, and doesn't modify last value", () => { |
574
|
|
|
const initialState: ResourceState<TestResource> = { |
575
|
|
|
...initializeState([]), |
576
|
|
|
values: { |
577
|
|
|
1: { |
578
|
|
|
value: { id: 1, name: "one" }, |
579
|
|
|
status: "pending", |
580
|
|
|
pendingCount: 1, |
581
|
|
|
error: undefined, |
582
|
|
|
}, |
583
|
|
|
}, |
584
|
|
|
}; |
585
|
|
|
const action: UpdateRejectAction<TestResource> = { |
586
|
|
|
type: ActionTypes.UpdateReject, |
587
|
|
|
payload: new Error("Something went wrong with a fake request"), |
588
|
|
|
meta: addKey({ id: 1, name: "NEW one" }), |
589
|
|
|
}; |
590
|
|
|
const expectState = { |
591
|
|
|
...initialState, |
592
|
|
|
values: { |
593
|
|
|
1: { |
594
|
|
|
value: initialState.values[1].value, |
595
|
|
|
status: "rejected", |
596
|
|
|
pendingCount: 0, |
597
|
|
|
error: action.payload, |
598
|
|
|
}, |
599
|
|
|
}, |
600
|
|
|
}; |
601
|
|
|
expect(indexCrudReducer(initialState, action)).toEqual(expectState); |
602
|
|
|
}); |
603
|
|
|
it("status remains 'pending' after UPDATE FULFILLED and UPDATE REJECTED if pendingCount was higher than 1", () => { |
604
|
|
|
const initialState: ResourceState<TestResource> = { |
605
|
|
|
...initializeState([]), |
606
|
|
|
values: { |
607
|
|
|
1: { |
608
|
|
|
value: { id: 1, name: "one" }, |
609
|
|
|
status: "pending", |
610
|
|
|
pendingCount: 2, |
611
|
|
|
error: undefined, |
612
|
|
|
}, |
613
|
|
|
}, |
614
|
|
|
}; |
615
|
|
|
const fulfilledAction: UpdateFulfillAction<TestResource> = { |
616
|
|
|
type: ActionTypes.UpdateFulfill, |
617
|
|
|
payload: { id: 1, name: "NEW one" }, |
618
|
|
|
meta: addKey({ id: 1, name: "NEW one" }), |
619
|
|
|
}; |
620
|
|
|
const fulfilledState = indexCrudReducer(initialState, fulfilledAction); |
621
|
|
|
expect(fulfilledState.values[1]).toEqual({ |
622
|
|
|
value: fulfilledAction.payload, |
623
|
|
|
status: "pending", |
624
|
|
|
pendingCount: 1, |
625
|
|
|
error: undefined, |
626
|
|
|
}); |
627
|
|
|
const rejectedAction: UpdateRejectAction<TestResource> = { |
628
|
|
|
type: ActionTypes.UpdateReject, |
629
|
|
|
payload: new Error(), |
630
|
|
|
meta: addKey({ id: 1, name: "one" }), |
631
|
|
|
}; |
632
|
|
|
const rejectedState = indexCrudReducer(initialState, rejectedAction); |
633
|
|
|
expect(rejectedState.values[1]).toEqual({ |
634
|
|
|
value: initialState.values[1].value, |
635
|
|
|
status: "pending", |
636
|
|
|
pendingCount: 1, |
637
|
|
|
error: rejectedAction.payload, |
638
|
|
|
}); |
639
|
|
|
}); |
640
|
|
|
}); |
641
|
|
|
describe("Test DELETE actions", () => { |
642
|
|
|
it("DELETE START updates item-specific metadata", () => { |
643
|
|
|
const initialState: ResourceState<TestResource> = initializeState( |
644
|
|
|
[{ id: 1, name: "one" }].map(addKey), |
645
|
|
|
); |
646
|
|
|
const expectState = { |
647
|
|
|
...initialState, |
648
|
|
|
values: { |
649
|
|
|
1: { |
650
|
|
|
value: initialState.values[1].value, |
651
|
|
|
status: "pending", |
652
|
|
|
pendingCount: 1, |
653
|
|
|
error: undefined, |
654
|
|
|
}, |
655
|
|
|
}, |
656
|
|
|
}; |
657
|
|
|
const action: DeleteStartAction = { |
658
|
|
|
type: ActionTypes.DeleteStart, |
659
|
|
|
meta: { key: 1 }, |
660
|
|
|
}; |
661
|
|
|
expect(indexCrudReducer(initialState, action)).toEqual(expectState); |
662
|
|
|
}); |
663
|
|
|
it("two DELETE START actions increment pendingCount twice", () => { |
664
|
|
|
const initialState: ResourceState<TestResource> = initializeState( |
665
|
|
|
[{ id: 1, name: "one" }].map(addKey), |
666
|
|
|
); |
667
|
|
|
const expectState = { |
668
|
|
|
...initialState, |
669
|
|
|
values: { |
670
|
|
|
1: { |
671
|
|
|
value: initialState.values[1].value, |
672
|
|
|
status: "pending", |
673
|
|
|
pendingCount: 2, // pendingCount is 2 this time |
674
|
|
|
error: undefined, |
675
|
|
|
}, |
676
|
|
|
}, |
677
|
|
|
}; |
678
|
|
|
const action: DeleteStartAction = { |
679
|
|
|
type: ActionTypes.DeleteStart, |
680
|
|
|
meta: { key: 1 }, |
681
|
|
|
}; |
682
|
|
|
const state1 = indexCrudReducer(initialState, action); |
683
|
|
|
const state2 = indexCrudReducer(state1, action); |
684
|
|
|
expect(state2).toEqual(expectState); |
685
|
|
|
}); |
686
|
|
|
it("DELETE START overwrites an error status", () => { |
687
|
|
|
const initialState: ResourceState<TestResource> = { |
688
|
|
|
...initializeState([]), |
689
|
|
|
values: { |
690
|
|
|
1: { |
691
|
|
|
value: { id: 1, name: "one" }, |
692
|
|
|
status: "rejected", |
693
|
|
|
pendingCount: 0, |
694
|
|
|
error: new Error(), |
695
|
|
|
}, |
696
|
|
|
}, |
697
|
|
|
}; |
698
|
|
|
const expectState = { |
699
|
|
|
...initialState, |
700
|
|
|
values: { |
701
|
|
|
1: { |
702
|
|
|
value: initialState.values[1].value, |
703
|
|
|
status: "pending", |
704
|
|
|
pendingCount: 1, |
705
|
|
|
error: undefined, // Note the error has been erased after new request starts. |
706
|
|
|
}, |
707
|
|
|
}, |
708
|
|
|
}; |
709
|
|
|
const action: DeleteStartAction = { |
710
|
|
|
type: ActionTypes.DeleteStart, |
711
|
|
|
meta: { key: 1 }, |
712
|
|
|
}; |
713
|
|
|
expect(indexCrudReducer(initialState, action)).toEqual(expectState); |
714
|
|
|
}); |
715
|
|
|
it("DELETE FULFILLED removes value entry entirely", () => { |
716
|
|
|
const initialState: ResourceState<TestResource> = { |
717
|
|
|
...initializeState([]), |
718
|
|
|
values: { |
719
|
|
|
1: { |
720
|
|
|
value: { id: 1, name: "one" }, |
721
|
|
|
status: "pending", |
722
|
|
|
pendingCount: 1, |
723
|
|
|
error: undefined, |
724
|
|
|
}, |
725
|
|
|
}, |
726
|
|
|
}; |
727
|
|
|
const action: DeleteFulfillAction = { |
728
|
|
|
type: ActionTypes.DeleteFulfill, |
729
|
|
|
meta: { key: 1 }, |
730
|
|
|
}; |
731
|
|
|
const expectState = { |
732
|
|
|
...initialState, |
733
|
|
|
values: {}, |
734
|
|
|
}; |
735
|
|
|
expect(indexCrudReducer(initialState, action)).toEqual(expectState); |
736
|
|
|
}); |
737
|
|
|
it("DELETE FULFILLED doesn't affect other values", () => { |
738
|
|
|
const initialState = initializeState( |
739
|
|
|
[ |
740
|
|
|
{ id: 1, name: "one" }, |
741
|
|
|
{ id: 2, name: "two" }, |
742
|
|
|
].map(addKey), |
743
|
|
|
); |
744
|
|
|
const action: DeleteFulfillAction = { |
745
|
|
|
type: ActionTypes.DeleteFulfill, |
746
|
|
|
meta: { key: 1 }, |
747
|
|
|
}; |
748
|
|
|
const fulfilledState = indexCrudReducer(initialState, action); |
749
|
|
|
expect(fulfilledState.values[2]).toEqual(initialState.values[2]); |
750
|
|
|
expect(fulfilledState.values[1]).toBeUndefined(); |
751
|
|
|
}); |
752
|
|
|
it("DELETE FULFILLED doesn't change state (or throw error) if value doesn't exist", () => { |
753
|
|
|
const initialState = initializeState( |
754
|
|
|
[{ id: 1, name: "one" }].map(addKey), |
755
|
|
|
); |
756
|
|
|
const action: DeleteFulfillAction = { |
757
|
|
|
type: ActionTypes.DeleteFulfill, |
758
|
|
|
meta: { key: "5" }, |
759
|
|
|
}; |
760
|
|
|
expect(indexCrudReducer(initialState, action)).toEqual(initialState); |
761
|
|
|
}); |
762
|
|
|
it("DELETE REJECTED decrements pendingCount, sets status to rejected, and doesn't modify item value", () => { |
763
|
|
|
const initialState: ResourceState<TestResource> = { |
764
|
|
|
...initializeState([]), |
765
|
|
|
values: { |
766
|
|
|
1: { |
767
|
|
|
value: { id: 1, name: "one" }, |
768
|
|
|
status: "pending", |
769
|
|
|
pendingCount: 1, |
770
|
|
|
error: undefined, |
771
|
|
|
}, |
772
|
|
|
}, |
773
|
|
|
}; |
774
|
|
|
const action: DeleteRejectAction = { |
775
|
|
|
type: ActionTypes.DeleteReject, |
776
|
|
|
payload: new Error("Something went wrong"), |
777
|
|
|
meta: { key: 1 }, |
778
|
|
|
}; |
779
|
|
|
const expectState = { |
780
|
|
|
...initialState, |
781
|
|
|
values: { |
782
|
|
|
1: { |
783
|
|
|
value: { id: 1, name: "one" }, |
784
|
|
|
status: "rejected", |
785
|
|
|
pendingCount: 0, |
786
|
|
|
error: action.payload, |
787
|
|
|
}, |
788
|
|
|
}, |
789
|
|
|
}; |
790
|
|
|
expect(indexCrudReducer(initialState, action)).toEqual(expectState); |
791
|
|
|
}); |
792
|
|
|
it("status remains 'pending' after DELETE REJECTED if if pendingCount was higher than 1", () => { |
793
|
|
|
const initialState: ResourceState<TestResource> = { |
794
|
|
|
...initializeState([]), |
795
|
|
|
values: { |
796
|
|
|
1: { |
797
|
|
|
value: { id: 1, name: "one" }, |
798
|
|
|
status: "pending", |
799
|
|
|
pendingCount: 3, |
800
|
|
|
error: undefined, |
801
|
|
|
}, |
802
|
|
|
}, |
803
|
|
|
}; |
804
|
|
|
const action: DeleteRejectAction = { |
805
|
|
|
type: ActionTypes.DeleteReject, |
806
|
|
|
payload: new Error("Something went wrong"), |
807
|
|
|
meta: { key: 1 }, |
808
|
|
|
}; |
809
|
|
|
const expectState = { |
810
|
|
|
...initialState, |
811
|
|
|
values: { |
812
|
|
|
1: { |
813
|
|
|
value: { id: 1, name: "one" }, |
814
|
|
|
status: "pending", |
815
|
|
|
pendingCount: 2, |
816
|
|
|
error: action.payload, |
817
|
|
|
}, |
818
|
|
|
}, |
819
|
|
|
}; |
820
|
|
|
expect(indexCrudReducer(initialState, action)).toEqual(expectState); |
821
|
|
|
}); |
822
|
|
|
}); |
823
|
|
|
}); |
824
|
|
|
|