@khanacademy/wonder-blocks-data 2.3.4 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/es/index.js +212 -446
  3. package/dist/index.js +230 -478
  4. package/docs.md +19 -13
  5. package/package.json +2 -3
  6. package/src/__tests__/__snapshots__/generated-snapshot.test.js.snap +40 -160
  7. package/src/__tests__/generated-snapshot.test.js +15 -195
  8. package/src/components/__tests__/data.test.js +159 -965
  9. package/src/components/__tests__/intercept-data.test.js +9 -66
  10. package/src/components/__tests__/track-data.test.js +6 -5
  11. package/src/components/data.js +9 -117
  12. package/src/components/data.md +38 -60
  13. package/src/components/intercept-data.js +2 -34
  14. package/src/components/intercept-data.md +7 -105
  15. package/src/hooks/__tests__/use-data.test.js +790 -0
  16. package/src/hooks/use-data.js +138 -0
  17. package/src/index.js +1 -3
  18. package/src/util/__tests__/memory-cache.test.js +134 -35
  19. package/src/util/__tests__/request-fulfillment.test.js +21 -36
  20. package/src/util/__tests__/request-handler.test.js +30 -30
  21. package/src/util/__tests__/request-tracking.test.js +29 -30
  22. package/src/util/__tests__/response-cache.test.js +521 -561
  23. package/src/util/__tests__/result-from-cache-entry.test.js +68 -0
  24. package/src/util/memory-cache.js +18 -14
  25. package/src/util/request-fulfillment.js +4 -0
  26. package/src/util/request-handler.js +2 -27
  27. package/src/util/request-handler.md +0 -32
  28. package/src/util/response-cache.js +50 -110
  29. package/src/util/result-from-cache-entry.js +38 -0
  30. package/src/util/types.js +14 -35
  31. package/LICENSE +0 -21
  32. package/src/components/__tests__/intercept-cache.test.js +0 -124
  33. package/src/components/__tests__/internal-data.test.js +0 -1030
  34. package/src/components/intercept-cache.js +0 -79
  35. package/src/components/intercept-cache.md +0 -103
  36. package/src/components/internal-data.js +0 -219
  37. package/src/util/__tests__/no-cache.test.js +0 -112
  38. package/src/util/no-cache.js +0 -67
  39. package/src/util/no-cache.md +0 -66
@@ -0,0 +1,790 @@
1
+ // @flow
2
+ import {
3
+ renderHook as clientRenderHook,
4
+ act,
5
+ } from "@testing-library/react-hooks";
6
+ import {renderHook as serverRenderHook} from "@testing-library/react-hooks/server";
7
+
8
+ import {Server} from "@khanacademy/wonder-blocks-core";
9
+
10
+ import * as React from "react";
11
+ import TrackData from "../../components/track-data.js";
12
+ import InterceptData from "../../components/intercept-data.js";
13
+ import {RequestFulfillment} from "../../util/request-fulfillment.js";
14
+ import {ResponseCache} from "../../util/response-cache.js";
15
+ import {RequestTracker} from "../../util/request-tracking.js";
16
+
17
+ import {useData} from "../use-data.js";
18
+
19
+ import type {IRequestHandler} from "../../util/types.js";
20
+
21
+ describe("#useData", () => {
22
+ beforeEach(() => {
23
+ const responseCache = new ResponseCache();
24
+ jest.spyOn(ResponseCache, "Default", "get").mockReturnValue(
25
+ responseCache,
26
+ );
27
+ jest.spyOn(RequestFulfillment, "Default", "get").mockReturnValue(
28
+ new RequestFulfillment(responseCache),
29
+ );
30
+ jest.spyOn(RequestTracker, "Default", "get").mockReturnValue(
31
+ new RequestTracker(responseCache),
32
+ );
33
+ });
34
+
35
+ afterEach(() => {
36
+ jest.resetAllMocks();
37
+ });
38
+
39
+ describe("when server-side", () => {
40
+ beforeEach(() => {
41
+ jest.spyOn(Server, "isServerSide").mockReturnValue(true);
42
+ });
43
+
44
+ it("should return loading if no cached result", () => {
45
+ // Arrange
46
+ const fakeHandler: IRequestHandler<string, string> = {
47
+ fulfillRequest: jest.fn(),
48
+ getKey: (o) => o,
49
+ type: "MY_HANDLER",
50
+ hydrate: true,
51
+ };
52
+
53
+ // Act
54
+ const {
55
+ result: {current: result},
56
+ } = serverRenderHook(() => useData(fakeHandler, "options"));
57
+
58
+ // Assert
59
+ expect(result).toEqual({
60
+ status: "loading",
61
+ });
62
+ });
63
+
64
+ it("should not directly request fulfillment", () => {
65
+ // Arrange
66
+ const fakeHandler: IRequestHandler<string, string> = {
67
+ fulfillRequest: jest.fn(),
68
+ getKey: (o) => o,
69
+ type: "MY_HANDLER",
70
+ hydrate: true,
71
+ };
72
+ const fulfillRequestSpy = jest.spyOn(
73
+ RequestFulfillment.Default,
74
+ "fulfill",
75
+ );
76
+
77
+ // Act
78
+ serverRenderHook(() => useData(fakeHandler, "options"));
79
+
80
+ // Assert
81
+ expect(fulfillRequestSpy).not.toHaveBeenCalled();
82
+ });
83
+
84
+ it("should track the request", () => {
85
+ // Arrange
86
+ const fakeHandler: IRequestHandler<string, string> = {
87
+ fulfillRequest: jest.fn(),
88
+ getKey: (o) => o,
89
+ type: "MY_HANDLER",
90
+ hydrate: true,
91
+ };
92
+ const trackDataRequestSpy = jest.spyOn(
93
+ RequestTracker.Default,
94
+ "trackDataRequest",
95
+ );
96
+
97
+ // Act
98
+ serverRenderHook(() => useData(fakeHandler, "options"), {
99
+ wrapper: TrackData,
100
+ });
101
+
102
+ // Assert
103
+ expect(trackDataRequestSpy).toHaveBeenCalledWith(
104
+ fakeHandler,
105
+ "options",
106
+ );
107
+ });
108
+
109
+ it("should return success state if cached result has data", () => {
110
+ // Arrange
111
+ const fakeHandler: IRequestHandler<string, string> = {
112
+ fulfillRequest: jest.fn(),
113
+ getKey: (o) => o,
114
+ type: "MY_HANDLER",
115
+ hydrate: true,
116
+ };
117
+ jest.spyOn(ResponseCache.Default, "getEntry").mockReturnValueOnce({
118
+ data: "DATA",
119
+ error: null,
120
+ });
121
+
122
+ // Act
123
+ const {
124
+ result: {current: result},
125
+ } = serverRenderHook(() => useData(fakeHandler, "options"));
126
+
127
+ // Assert
128
+ expect(result).toEqual({
129
+ status: "success",
130
+ data: "DATA",
131
+ });
132
+ });
133
+
134
+ it("should return error state if cached result has error", () => {
135
+ // Arrange
136
+ const fakeHandler: IRequestHandler<string, string> = {
137
+ fulfillRequest: jest.fn(),
138
+ getKey: (o) => o,
139
+ type: "MY_HANDLER",
140
+ hydrate: true,
141
+ };
142
+ jest.spyOn(ResponseCache.Default, "getEntry").mockReturnValueOnce({
143
+ data: null,
144
+ error: "ERROR",
145
+ });
146
+
147
+ // Act
148
+ const {
149
+ result: {current: result},
150
+ } = serverRenderHook(() => useData(fakeHandler, "options"));
151
+
152
+ // Assert
153
+ expect(result).toEqual({
154
+ status: "error",
155
+ error: "ERROR",
156
+ });
157
+ });
158
+ });
159
+
160
+ describe("when client-side", () => {
161
+ beforeEach(() => {
162
+ jest.spyOn(Server, "isServerSide").mockReturnValue(false);
163
+ });
164
+
165
+ it("should initially return loading state when no cached result", () => {
166
+ // Arrange
167
+ const fakeHandler: IRequestHandler<string, string> = {
168
+ fulfillRequest: jest.fn().mockReturnValue(
169
+ new Promise(() => {
170
+ /*prevent act() warning*/
171
+ }),
172
+ ),
173
+ getKey: (o) => o,
174
+ type: "MY_HANDLER",
175
+ hydrate: true,
176
+ };
177
+
178
+ // Act
179
+ const {
180
+ result: {current: result},
181
+ } = clientRenderHook(() => useData(fakeHandler, "options"));
182
+
183
+ // Assert
184
+ expect(result).toEqual({
185
+ status: "loading",
186
+ });
187
+ });
188
+
189
+ it("should initially return success state when cached result has data", () => {
190
+ // Arrange
191
+ const fakeHandler: IRequestHandler<string, string> = {
192
+ fulfillRequest: jest.fn().mockReturnValue(
193
+ new Promise(() => {
194
+ /*prevent act() warning*/
195
+ }),
196
+ ),
197
+ getKey: (o) => o,
198
+ type: "MY_HANDLER",
199
+ hydrate: true,
200
+ };
201
+ jest.spyOn(ResponseCache.Default, "getEntry").mockReturnValueOnce({
202
+ data: "DATA",
203
+ error: null,
204
+ });
205
+
206
+ // Act
207
+ const {
208
+ result: {current: result},
209
+ } = clientRenderHook(() => useData(fakeHandler, "options"));
210
+
211
+ // Assert
212
+ expect(result).toEqual({
213
+ status: "success",
214
+ data: "DATA",
215
+ });
216
+ });
217
+
218
+ it("should initially return error state when cached result has error", () => {
219
+ // Arrange
220
+ const fakeHandler: IRequestHandler<string, string> = {
221
+ fulfillRequest: jest.fn().mockReturnValue(
222
+ new Promise(() => {
223
+ /*prevent act() warning*/
224
+ }),
225
+ ),
226
+ getKey: (o) => o,
227
+ type: "MY_HANDLER",
228
+ hydrate: true,
229
+ };
230
+ jest.spyOn(ResponseCache.Default, "getEntry").mockReturnValueOnce({
231
+ data: null,
232
+ error: "ERROR",
233
+ });
234
+
235
+ // Act
236
+ const {
237
+ result: {current: result},
238
+ } = clientRenderHook(() => useData(fakeHandler, "options"));
239
+
240
+ // Assert
241
+ expect(result).toEqual({
242
+ status: "error",
243
+ error: "ERROR",
244
+ });
245
+ });
246
+
247
+ it("should not track the request", () => {
248
+ // Arrange
249
+ const fakeHandler: IRequestHandler<string, string> = {
250
+ fulfillRequest: jest.fn().mockReturnValue(
251
+ new Promise(() => {
252
+ /*prevent act() warning*/
253
+ }),
254
+ ),
255
+ getKey: (o) => o,
256
+ type: "MY_HANDLER",
257
+ hydrate: true,
258
+ };
259
+ const trackDataRequestSpy = jest.spyOn(
260
+ RequestTracker.Default,
261
+ "trackDataRequest",
262
+ );
263
+
264
+ // Act
265
+ clientRenderHook(() => useData(fakeHandler, "options"), {
266
+ wrapper: TrackData,
267
+ });
268
+
269
+ // Assert
270
+ expect(trackDataRequestSpy).not.toHaveBeenCalled();
271
+ });
272
+
273
+ it("should request fulfillment", () => {
274
+ // Arrange
275
+ const fakeHandler: IRequestHandler<string, string> = {
276
+ fulfillRequest: jest.fn().mockReturnValue(
277
+ new Promise(() => {
278
+ /*prevent act() warning*/
279
+ }),
280
+ ),
281
+ getKey: (o) => o,
282
+ type: "MY_HANDLER",
283
+ hydrate: true,
284
+ };
285
+ const fulfillRequestSpy = jest.spyOn(
286
+ RequestFulfillment.Default,
287
+ "fulfill",
288
+ );
289
+
290
+ // Act
291
+ clientRenderHook(() => useData(fakeHandler, "options"));
292
+
293
+ // Assert
294
+ expect(fulfillRequestSpy).toHaveBeenCalledWith(
295
+ fakeHandler,
296
+ "options",
297
+ );
298
+ });
299
+
300
+ it("should return success state if fulfillment resolves with data", async () => {
301
+ // Arrange
302
+ const request = Promise.resolve("DATA");
303
+ const fakeHandler: IRequestHandler<string, string> = {
304
+ fulfillRequest: jest.fn().mockReturnValue(request),
305
+ getKey: (o) => o,
306
+ type: "MY_HANDLER",
307
+ hydrate: true,
308
+ };
309
+
310
+ // Act
311
+ const render = clientRenderHook(() =>
312
+ useData(fakeHandler, "options"),
313
+ );
314
+ await act((): Promise<mixed> => request);
315
+ const result = render.result.current;
316
+
317
+ // Assert
318
+ expect(result).toEqual({
319
+ status: "success",
320
+ data: "DATA",
321
+ });
322
+ });
323
+
324
+ it("should return error state if fulfillment resolves with error", async () => {
325
+ // Arrange
326
+ const request = Promise.reject(new Error("ERROR"));
327
+ const fakeHandler: IRequestHandler<string, string> = {
328
+ fulfillRequest: jest.fn().mockReturnValue(request),
329
+ getKey: (o) => o,
330
+ type: "MY_HANDLER",
331
+ hydrate: true,
332
+ };
333
+
334
+ // Act
335
+ const render = clientRenderHook(() =>
336
+ useData(fakeHandler, "options"),
337
+ );
338
+ await act(async (): Promise<mixed> => {
339
+ try {
340
+ await request;
341
+ } catch (e) {
342
+ /* we know, we know */
343
+ }
344
+ });
345
+ const result = render.result.current;
346
+
347
+ // Assert
348
+ expect(result).toEqual({
349
+ status: "error",
350
+ error: "ERROR",
351
+ });
352
+ });
353
+
354
+ it("should return error state if fulfillment rejects", async () => {
355
+ // Arrange
356
+ const fakeHandler: IRequestHandler<string, string> = {
357
+ fulfillRequest: jest.fn().mockReturnValue(
358
+ new Promise(() => {
359
+ /*prevent act() warning*/
360
+ }),
361
+ ),
362
+ getKey: (o) => o,
363
+ type: "MY_HANDLER",
364
+ hydrate: true,
365
+ };
366
+ const request = Promise.reject(new Error("ERROR"));
367
+ jest.spyOn(
368
+ RequestFulfillment.Default,
369
+ "fulfill",
370
+ ).mockReturnValueOnce(request);
371
+
372
+ // Act
373
+ const render = clientRenderHook(() =>
374
+ useData(fakeHandler, "options"),
375
+ );
376
+ await act(async (): Promise<mixed> => {
377
+ try {
378
+ await request;
379
+ } catch (e) {
380
+ /* we know, we know */
381
+ }
382
+ });
383
+ const result = render.result.current;
384
+
385
+ // Assert
386
+ expect(result).toEqual({
387
+ status: "error",
388
+ error: "ERROR",
389
+ });
390
+ });
391
+
392
+ it("should ignore resolution of pending request fulfillment when handler changes", async () => {
393
+ // Arrange
394
+ const oldRequest = Promise.resolve("OLD DATA");
395
+ const oldHandler: IRequestHandler<string, string> = {
396
+ fulfillRequest: jest.fn().mockReturnValue(oldRequest),
397
+ getKey: (o) => o,
398
+ type: "MY_HANDLER",
399
+ hydrate: true,
400
+ };
401
+ const newHandler: IRequestHandler<string, string> = {
402
+ fulfillRequest: jest.fn().mockReturnValue(
403
+ new Promise(() => {
404
+ /*let's have the new request remain pending*/
405
+ }),
406
+ ),
407
+ getKey: (o) => o,
408
+ type: "MY_NEW_HANDLER",
409
+ hydrate: true,
410
+ };
411
+
412
+ // Act
413
+ const render = clientRenderHook(
414
+ ({handler}) => useData(handler, "options"),
415
+ {
416
+ initialProps: {handler: oldHandler},
417
+ },
418
+ );
419
+ render.rerender({handler: newHandler});
420
+ await act((): Promise<mixed> => oldRequest);
421
+ const result = render.result.all;
422
+
423
+ // Assert
424
+ expect(result).not.toIncludeAnyMembers([
425
+ {
426
+ status: "success",
427
+ data: "OLD DATA",
428
+ },
429
+ ]);
430
+ });
431
+
432
+ it("should ignore resolution of pending request fulfillment when key from options changes", async () => {
433
+ // Arrange
434
+ const oldRequest = Promise.reject("OLD ERROR");
435
+ const oldHandler: IRequestHandler<string, string> = {
436
+ fulfillRequest: jest
437
+ .fn()
438
+ .mockReturnValueOnce(oldRequest)
439
+ .mockReturnValue(
440
+ new Promise(() => {
441
+ /*let's have the new request remain pending*/
442
+ }),
443
+ ),
444
+ getKey: (o) => o,
445
+ type: "MY_HANDLER",
446
+ hydrate: true,
447
+ };
448
+
449
+ // Act
450
+ const render = clientRenderHook(
451
+ ({options}) => useData(oldHandler, options),
452
+ {
453
+ initialProps: {options: "OLD OPTIONS"},
454
+ },
455
+ );
456
+ render.rerender({options: "NEW OPTIONS"});
457
+ await act((): Promise<mixed> => oldRequest.catch(() => {}));
458
+ const result = render.result.all;
459
+
460
+ // Assert
461
+ expect(result).not.toIncludeAnyMembers([
462
+ {
463
+ status: "error",
464
+ error: "OLD ERROR",
465
+ },
466
+ ]);
467
+ });
468
+
469
+ it("should ignore rejection of pending request fulfillment when handler changes", async () => {
470
+ // Arrange
471
+ const oldRequest = Promise.reject("OLD ERROR");
472
+ const oldHandler: IRequestHandler<string, string> = {
473
+ fulfillRequest: jest.fn().mockReturnValue(
474
+ new Promise(() => {
475
+ /*this doesn't get called for this test case*/
476
+ }),
477
+ ),
478
+ getKey: (o) => o,
479
+ type: "MY_HANDLER",
480
+ hydrate: true,
481
+ };
482
+ const newHandler: IRequestHandler<string, string> = {
483
+ fulfillRequest: jest.fn().mockReturnValue(
484
+ new Promise(() => {
485
+ /*let's have the new request remain pending*/
486
+ }),
487
+ ),
488
+ getKey: (o) => o,
489
+ type: "MY_NEW_HANDLER",
490
+ hydrate: true,
491
+ };
492
+ jest.spyOn(RequestFulfillment.Default, "fulfill")
493
+ .mockReturnValueOnce(oldRequest)
494
+ .mockReturnValue(
495
+ new Promise(() => {
496
+ /*leave the second request pending*/
497
+ }),
498
+ );
499
+
500
+ // Act
501
+ const render = clientRenderHook(
502
+ ({handler}) => useData(handler, "options"),
503
+ {
504
+ initialProps: {handler: oldHandler},
505
+ },
506
+ );
507
+ render.rerender({handler: newHandler});
508
+ await act((): Promise<mixed> => oldRequest.catch(() => {}));
509
+ const result = render.result.all;
510
+
511
+ // Assert
512
+ expect(result).not.toIncludeAnyMembers([
513
+ {
514
+ status: "error",
515
+ error: "OLD ERROR",
516
+ },
517
+ ]);
518
+ });
519
+
520
+ it("should ignore rejection of pending request fulfillment when key from options changes", async () => {
521
+ // Arrange
522
+ const oldRequest = Promise.resolve("OLD DATA");
523
+ const oldHandler: IRequestHandler<string, string> = {
524
+ fulfillRequest: jest.fn().mockReturnValue(
525
+ new Promise(() => {
526
+ /*this won't ever get called in this version*/
527
+ }),
528
+ ),
529
+ getKey: (o) => o,
530
+ type: "MY_HANDLER",
531
+ hydrate: true,
532
+ };
533
+ jest.spyOn(RequestFulfillment.Default, "fulfill")
534
+ .mockReturnValueOnce(oldRequest)
535
+ .mockReturnValue(
536
+ new Promise(() => {
537
+ /*leave the second request pending*/
538
+ }),
539
+ );
540
+
541
+ // Act
542
+ const render = clientRenderHook(
543
+ ({options}) => useData(oldHandler, options),
544
+ {
545
+ initialProps: {options: "OLD OPTIONS"},
546
+ },
547
+ );
548
+ render.rerender({options: "NEW OPTIONS"});
549
+ await act((): Promise<mixed> => oldRequest);
550
+ const result = render.result.all;
551
+
552
+ // Assert
553
+ expect(result).not.toIncludeAnyMembers([
554
+ {
555
+ status: "success",
556
+ data: "OLD DATA",
557
+ },
558
+ ]);
559
+ });
560
+
561
+ it("should return to loading status when handler changes", async () => {
562
+ // Arrange
563
+ const oldRequest = Promise.resolve("OLD DATA");
564
+ const oldHandler: IRequestHandler<string, string> = {
565
+ fulfillRequest: jest.fn().mockReturnValue(oldRequest),
566
+ getKey: (o) => o,
567
+ type: "MY_HANDLER",
568
+ hydrate: true,
569
+ };
570
+ const newHandler: IRequestHandler<string, string> = {
571
+ fulfillRequest: jest.fn().mockReturnValue(
572
+ new Promise(() => {
573
+ /*let's have the new request remain pending*/
574
+ }),
575
+ ),
576
+ getKey: (o) => o,
577
+ type: "MY_NEW_HANDLER",
578
+ hydrate: true,
579
+ };
580
+
581
+ // Act
582
+ const render = clientRenderHook(
583
+ ({handler}) => useData(handler, "options"),
584
+ {
585
+ initialProps: {handler: oldHandler},
586
+ },
587
+ );
588
+ await act((): Promise<mixed> => oldRequest);
589
+ render.rerender({handler: newHandler});
590
+ const result = render.result.current;
591
+
592
+ // Assert
593
+ expect(result).toEqual({
594
+ status: "loading",
595
+ });
596
+ });
597
+
598
+ it("should return to loading status when key from options changes", async () => {
599
+ // Arrange
600
+ const oldRequest = Promise.resolve("OLD DATA");
601
+ const oldHandler: IRequestHandler<string, string> = {
602
+ fulfillRequest: jest
603
+ .fn()
604
+ .mockReturnValueOnce(oldRequest)
605
+ .mockReturnValue(
606
+ new Promise(() => {
607
+ /*let's have the new request remain pending*/
608
+ }),
609
+ ),
610
+ getKey: (o) => o,
611
+ type: "MY_HANDLER",
612
+ hydrate: true,
613
+ };
614
+
615
+ // Act
616
+ const render = clientRenderHook(
617
+ ({options}) => useData(oldHandler, options),
618
+ {
619
+ initialProps: {options: "OLD OPTIONS"},
620
+ },
621
+ );
622
+ await act((): Promise<mixed> => oldRequest);
623
+ render.rerender({options: "NEW OPTIONS"});
624
+ const result = render.result.current;
625
+
626
+ // Assert
627
+ expect(result).toEqual({
628
+ status: "loading",
629
+ });
630
+ });
631
+
632
+ it("should not return to loading state if options change but represent the same key", async () => {
633
+ // Arrange
634
+ const oldRequest = Promise.resolve("DATA");
635
+ const oldHandler: IRequestHandler<
636
+ {|key: string, someThing: string|},
637
+ string,
638
+ > = {
639
+ fulfillRequest: jest
640
+ .fn()
641
+ .mockReturnValueOnce(oldRequest)
642
+ .mockReturnValue(
643
+ new Promise(() => {
644
+ /*let's have the new request remain pending*/
645
+ }),
646
+ ),
647
+ getKey: (o) => o.key,
648
+ type: "MY_HANDLER",
649
+ hydrate: true,
650
+ };
651
+
652
+ // Act
653
+ const render = clientRenderHook(
654
+ ({options}) => useData(oldHandler, options),
655
+ {
656
+ initialProps: {
657
+ options: {key: "SAME KEY", someThing: "else"},
658
+ },
659
+ },
660
+ );
661
+ await act((): Promise<mixed> => oldRequest);
662
+ render.rerender({options: {key: "SAME KEY", someThing: "new"}});
663
+ const result = render.result.current;
664
+
665
+ // Assert
666
+ expect(result).toEqual({
667
+ status: "success",
668
+ data: "DATA",
669
+ });
670
+ });
671
+ });
672
+
673
+ describe("with interceptor", () => {
674
+ it("should return the result of the interceptor request resolution", async () => {
675
+ // Arrange
676
+ const intercepted = Promise.resolve("INTERCEPTED");
677
+ const notIntercepted = Promise.resolve("NOT INTERCEPTED");
678
+ const fakeHandler: IRequestHandler<string, string> = {
679
+ fulfillRequest: jest.fn().mockReturnValue(notIntercepted),
680
+ getKey: (o) => o,
681
+ type: "MY_HANDLER",
682
+ hydrate: true,
683
+ };
684
+ const wrapper = ({children}) => (
685
+ <InterceptData
686
+ fulfillRequest={() => intercepted}
687
+ handler={fakeHandler}
688
+ >
689
+ {children}
690
+ </InterceptData>
691
+ );
692
+
693
+ // Act
694
+ const render = clientRenderHook(
695
+ () => useData(fakeHandler, "options"),
696
+ {
697
+ wrapper,
698
+ },
699
+ );
700
+ await act((): Promise<mixed> =>
701
+ Promise.all([notIntercepted, intercepted]),
702
+ );
703
+ const result = render.result.current;
704
+
705
+ // Assert
706
+ expect(result).toEqual({
707
+ status: "success",
708
+ data: "INTERCEPTED",
709
+ });
710
+ });
711
+
712
+ it("should return the result of the interceptor request rejection", async () => {
713
+ // Arrange
714
+ const intercepted = Promise.reject("INTERCEPTED");
715
+ const notIntercepted = Promise.resolve("NOT INTERCEPTED");
716
+ const fakeHandler: IRequestHandler<string, string> = {
717
+ fulfillRequest: jest.fn().mockReturnValue(notIntercepted),
718
+ getKey: (o) => o,
719
+ type: "MY_HANDLER",
720
+ hydrate: true,
721
+ };
722
+ const wrapper = ({children}) => (
723
+ <InterceptData
724
+ fulfillRequest={() => intercepted}
725
+ handler={fakeHandler}
726
+ >
727
+ {children}
728
+ </InterceptData>
729
+ );
730
+
731
+ // Act
732
+ const render = clientRenderHook(
733
+ () => useData(fakeHandler, "options"),
734
+ {
735
+ wrapper,
736
+ },
737
+ );
738
+ await notIntercepted;
739
+ await act(async (): Promise<mixed> => {
740
+ try {
741
+ await intercepted;
742
+ } catch (e) {
743
+ /* ignore, it's ok */
744
+ }
745
+ });
746
+ const result = render.result.current;
747
+
748
+ // Assert
749
+ expect(result).toEqual({
750
+ status: "error",
751
+ error: "INTERCEPTED",
752
+ });
753
+ });
754
+
755
+ it("should return the result of the handler if the interceptor returns null", async () => {
756
+ // Arrange
757
+ const notIntercepted = Promise.resolve("NOT INTERCEPTED");
758
+ const fakeHandler: IRequestHandler<string, string> = {
759
+ fulfillRequest: jest.fn().mockReturnValue(notIntercepted),
760
+ getKey: (o) => o,
761
+ type: "MY_HANDLER",
762
+ hydrate: true,
763
+ };
764
+ const wrapper = ({children}) => (
765
+ <InterceptData
766
+ fulfillRequest={() => null}
767
+ handler={fakeHandler}
768
+ >
769
+ {children}
770
+ </InterceptData>
771
+ );
772
+
773
+ // Act
774
+ const render = clientRenderHook(
775
+ () => useData(fakeHandler, "options"),
776
+ {
777
+ wrapper,
778
+ },
779
+ );
780
+ await act((): Promise<mixed> => notIntercepted);
781
+ const result = render.result.current;
782
+
783
+ // Assert
784
+ expect(result).toEqual({
785
+ status: "success",
786
+ data: "NOT INTERCEPTED",
787
+ });
788
+ });
789
+ });
790
+ });