@khanacademy/wonder-blocks-data 2.3.2 → 3.0.1

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 (41) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/es/index.js +210 -439
  3. package/dist/index.js +235 -478
  4. package/docs.md +19 -13
  5. package/package.json +6 -7
  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 -119
  12. package/src/components/data.md +38 -60
  13. package/src/components/intercept-context.js +2 -3
  14. package/src/components/intercept-data.js +2 -34
  15. package/src/components/intercept-data.md +7 -105
  16. package/src/hooks/__tests__/use-data.test.js +826 -0
  17. package/src/hooks/use-data.js +143 -0
  18. package/src/index.js +1 -3
  19. package/src/util/__tests__/memory-cache.test.js +134 -35
  20. package/src/util/__tests__/request-fulfillment.test.js +21 -36
  21. package/src/util/__tests__/request-handler.test.js +30 -30
  22. package/src/util/__tests__/request-tracking.test.js +29 -30
  23. package/src/util/__tests__/response-cache.test.js +521 -561
  24. package/src/util/__tests__/result-from-cache-entry.test.js +68 -0
  25. package/src/util/memory-cache.js +20 -15
  26. package/src/util/request-fulfillment.js +4 -0
  27. package/src/util/request-handler.js +4 -28
  28. package/src/util/request-handler.md +0 -32
  29. package/src/util/request-tracking.js +2 -3
  30. package/src/util/response-cache.js +50 -110
  31. package/src/util/result-from-cache-entry.js +38 -0
  32. package/src/util/types.js +14 -35
  33. package/LICENSE +0 -21
  34. package/src/components/__tests__/intercept-cache.test.js +0 -124
  35. package/src/components/__tests__/internal-data.test.js +0 -1030
  36. package/src/components/intercept-cache.js +0 -79
  37. package/src/components/intercept-cache.md +0 -103
  38. package/src/components/internal-data.js +0 -219
  39. package/src/util/__tests__/no-cache.test.js +0 -112
  40. package/src/util/no-cache.js +0 -66
  41. package/src/util/no-cache.md +0 -66
@@ -1,1030 +0,0 @@
1
- /* eslint-disable max-lines */
2
- // @flow
3
- import * as React from "react";
4
- import {mount, shallow} from "enzyme";
5
-
6
- // eslint-disable-next-line import/extensions
7
- import * as ReactDOMServer from "react-dom/server";
8
- import {Server, View} from "@khanacademy/wonder-blocks-core";
9
-
10
- import TrackData from "../track-data.js";
11
- import {RequestFulfillment} from "../../util/request-fulfillment.js";
12
- import {ResponseCache} from "../../util/response-cache.js";
13
- import {RequestTracker} from "../../util/request-tracking.js";
14
- import InternalData from "../internal-data.js";
15
-
16
- import type {IRequestHandler} from "../../util/types.js";
17
-
18
- describe("InternalData", () => {
19
- beforeEach(() => {
20
- const responseCache = new ResponseCache();
21
- jest.spyOn(ResponseCache, "Default", "get").mockReturnValue(
22
- responseCache,
23
- );
24
- jest.spyOn(RequestFulfillment, "Default", "get").mockReturnValue(
25
- new RequestFulfillment(responseCache),
26
- );
27
- jest.spyOn(RequestTracker, "Default", "get").mockReturnValue(
28
- new RequestTracker(responseCache),
29
- );
30
- });
31
-
32
- afterEach(() => {
33
- jest.resetAllMocks();
34
- });
35
-
36
- describe("CSR: isServerSide false", () => {
37
- beforeEach(() => {
38
- jest.spyOn(Server, "isServerSide").mockReturnValue(false);
39
- });
40
-
41
- describe("without cached data", () => {
42
- it("should initialize state as loading", () => {
43
- // Arrange
44
- const fakeHandler: IRequestHandler<string, string> = {
45
- fulfillRequest: () => Promise.resolve("data"),
46
- getKey: (o) => o,
47
- shouldRefreshCache: () => false,
48
- type: "MY_HANDLER",
49
- cache: null,
50
- hydrate: true,
51
- };
52
- const fakeChildrenFn = jest.fn(() => null);
53
-
54
- // Act
55
- const wrapper = shallow(
56
- <InternalData
57
- handler={fakeHandler}
58
- options={"options"}
59
- getEntry={jest.fn(() => null)}
60
- >
61
- {fakeChildrenFn}
62
- </InternalData>,
63
- );
64
-
65
- // Assert
66
- expect(wrapper).toHaveState("loading", true);
67
- expect(wrapper).toHaveState("data", null);
68
- expect(wrapper).toHaveState("error", null);
69
- });
70
-
71
- it("should make request for data on construction", () => {
72
- // Arrange
73
- const fulfillRequestSpy = jest.fn().mockResolvedValue("data");
74
- const fakeHandler: IRequestHandler<string, string> = {
75
- fulfillRequest: fulfillRequestSpy,
76
- getKey: (o) => o,
77
- shouldRefreshCache: () => false,
78
- type: "MY_HANDLER",
79
- cache: null,
80
- hydrate: true,
81
- };
82
- const fakeChildrenFn = jest.fn(() => null);
83
-
84
- // Act
85
- mount(
86
- <InternalData
87
- handler={fakeHandler}
88
- options={"options"}
89
- getEntry={jest.fn(() => null)}
90
- >
91
- {fakeChildrenFn}
92
- </InternalData>,
93
- );
94
-
95
- // Assert
96
- expect(fulfillRequestSpy).toHaveBeenCalledWith("options");
97
- expect(fulfillRequestSpy).toHaveBeenCalledTimes(1);
98
- });
99
-
100
- it("should initially render children with loading", () => {
101
- // Arrange
102
- const fakeHandler: IRequestHandler<string, string> = {
103
- fulfillRequest: () => Promise.resolve("data"),
104
- getKey: (o) => o,
105
- shouldRefreshCache: () => false,
106
- type: "MY_HANDLER",
107
- cache: null,
108
- hydrate: true,
109
- };
110
- const fakeChildrenFn = jest.fn(() => null);
111
-
112
- // Act
113
- mount(
114
- <InternalData
115
- handler={fakeHandler}
116
- options={"options"}
117
- getEntry={jest.fn(() => null)}
118
- >
119
- {fakeChildrenFn}
120
- </InternalData>,
121
- );
122
-
123
- // Assert
124
- expect(fakeChildrenFn).toHaveBeenCalledWith(
125
- expect.objectContaining({
126
- loading: true,
127
- }),
128
- );
129
- });
130
-
131
- it("should share single request across all uses", () => {
132
- // Arrange
133
- const fulfillRequestSpy = jest.fn(
134
- () => new Promise((resolve, reject) => {}),
135
- );
136
- const fakeHandler: IRequestHandler<string, string> = {
137
- fulfillRequest: fulfillRequestSpy,
138
- getKey: (o) => o,
139
- shouldRefreshCache: () => false,
140
- type: "MY_HANDLER",
141
- cache: null,
142
- hydrate: true,
143
- };
144
- const fakeChildrenFn = jest.fn(() => null);
145
-
146
- // Act
147
- mount(
148
- <View>
149
- <InternalData
150
- handler={fakeHandler}
151
- options={"options"}
152
- getEntry={jest.fn(() => null)}
153
- >
154
- {fakeChildrenFn}
155
- </InternalData>
156
- <InternalData
157
- handler={fakeHandler}
158
- options={"options"}
159
- getEntry={jest.fn(() => null)}
160
- >
161
- {fakeChildrenFn}
162
- </InternalData>
163
- </View>,
164
- );
165
-
166
- // Assert
167
- expect(fulfillRequestSpy).toHaveBeenCalledWith("options");
168
- expect(fulfillRequestSpy).toHaveBeenCalledTimes(1);
169
- });
170
-
171
- it("should render with an error if the request resolves to an error", async () => {
172
- // Arrange
173
- const fulfillSpy = jest.spyOn(
174
- RequestFulfillment.Default,
175
- "fulfill",
176
- );
177
-
178
- const fakeHandler: IRequestHandler<string, string> = {
179
- fulfillRequest: () => Promise.reject(new Error("OH NOES!")),
180
- getKey: (o) => o,
181
- shouldRefreshCache: () => false,
182
- type: "MY_HANDLER",
183
- cache: null,
184
- hydrate: true,
185
- };
186
- const fakeChildrenFn = jest.fn(() => null);
187
-
188
- // Act
189
- mount(
190
- <InternalData
191
- handler={fakeHandler}
192
- options={"options"}
193
- getEntry={jest.fn(() => null)}
194
- >
195
- {fakeChildrenFn}
196
- </InternalData>,
197
- );
198
- /**
199
- * We wait for the fulfillment to resolve.
200
- */
201
- await fulfillSpy.mock.results[0].value;
202
-
203
- // Assert
204
- expect(fakeChildrenFn).toHaveBeenCalledTimes(2);
205
- expect(fakeChildrenFn).toHaveBeenLastCalledWith({
206
- loading: false,
207
- error: "OH NOES!",
208
- });
209
- });
210
-
211
- it("should render with data if the request resolves with data", async () => {
212
- // Arrange
213
- const fulfillSpy = jest.spyOn(
214
- RequestFulfillment.Default,
215
- "fulfill",
216
- );
217
-
218
- const fakeHandler: IRequestHandler<string, string> = {
219
- fulfillRequest: () => Promise.resolve("YAY! DATA!"),
220
- getKey: (o) => o,
221
- shouldRefreshCache: () => false,
222
- type: "MY_HANDLER",
223
- cache: null,
224
- hydrate: true,
225
- };
226
- const fakeChildrenFn = jest.fn(() => null);
227
-
228
- // Act
229
- mount(
230
- <InternalData
231
- handler={fakeHandler}
232
- options={"options"}
233
- getEntry={jest.fn(() => null)}
234
- >
235
- {fakeChildrenFn}
236
- </InternalData>,
237
- );
238
- /**
239
- * We wait for the fulfillment to resolve.
240
- */
241
- await fulfillSpy.mock.results[0].value;
242
-
243
- // Assert
244
- expect(fakeChildrenFn).toHaveBeenCalledTimes(2);
245
- expect(fakeChildrenFn).toHaveBeenLastCalledWith({
246
- loading: false,
247
- data: "YAY! DATA!",
248
- });
249
- });
250
-
251
- it("should render with an error if the request rejects with string", async () => {
252
- // Arrange
253
- const fulfillSpy = jest
254
- .spyOn(RequestFulfillment.Default, "fulfill")
255
- .mockReturnValue(Promise.reject("CATASTROPHE!"));
256
-
257
- const fakeHandler: IRequestHandler<string, string> = {
258
- fulfillRequest: () => Promise.resolve("YAY!"),
259
- getKey: (o) => o,
260
- shouldRefreshCache: () => false,
261
- type: "MY_HANDLER",
262
- cache: null,
263
- hydrate: true,
264
- };
265
- const fakeChildrenFn = jest.fn(() => null);
266
-
267
- // Act
268
- mount(
269
- <InternalData
270
- handler={fakeHandler}
271
- options={"options"}
272
- getEntry={jest.fn(() => null)}
273
- >
274
- {fakeChildrenFn}
275
- </InternalData>,
276
- );
277
- /**
278
- * We wait for the fulfillment to reject.
279
- */
280
- await fulfillSpy.mock.results[0].value.catch(() => {});
281
-
282
- // Assert
283
- expect(fakeChildrenFn).toHaveBeenCalledTimes(2);
284
- expect(fakeChildrenFn).toHaveBeenLastCalledWith({
285
- loading: false,
286
- error: "CATASTROPHE!",
287
- });
288
- });
289
-
290
- it("should render with an error if the request rejects with Error", async () => {
291
- // Arrange
292
- const fulfillSpy = jest
293
- .spyOn(RequestFulfillment.Default, "fulfill")
294
- .mockReturnValue(Promise.reject(new Error("CATASTROPHE!")));
295
-
296
- const fakeHandler: IRequestHandler<string, string> = {
297
- fulfillRequest: () => Promise.resolve("YAY!"),
298
- getKey: (o) => o,
299
- shouldRefreshCache: () => false,
300
- type: "MY_HANDLER",
301
- cache: null,
302
- hydrate: true,
303
- };
304
- const fakeChildrenFn = jest.fn(() => null);
305
-
306
- // Act
307
- mount(
308
- <InternalData
309
- handler={fakeHandler}
310
- options={"options"}
311
- getEntry={jest.fn(() => null)}
312
- >
313
- {fakeChildrenFn}
314
- </InternalData>,
315
- );
316
- /**
317
- * We wait for the fulfillment to reject.
318
- */
319
- await fulfillSpy.mock.results[0].value.catch(() => {});
320
-
321
- // Assert
322
- expect(fakeChildrenFn).toHaveBeenCalledTimes(2);
323
- expect(fakeChildrenFn).toHaveBeenLastCalledWith({
324
- loading: false,
325
- error: "CATASTROPHE!",
326
- });
327
- });
328
-
329
- it("should not setState if no longer mounted when request resolves", async () => {
330
- // Arrange
331
- const fulfillSpy = jest.spyOn(
332
- RequestFulfillment.Default,
333
- "fulfill",
334
- );
335
-
336
- const fakeHandler: IRequestHandler<string, string> = {
337
- fulfillRequest: () => Promise.resolve("YAY! DATA!"),
338
- getKey: (o) => o,
339
- shouldRefreshCache: () => false,
340
- type: "MY_HANDLER",
341
- cache: null,
342
- hydrate: true,
343
- };
344
- const fakeChildrenFn = jest.fn(() => null);
345
-
346
- // Act
347
- const wrapper = mount(
348
- <InternalData
349
- handler={fakeHandler}
350
- options={"options"}
351
- getEntry={jest.fn(() => null)}
352
- >
353
- {fakeChildrenFn}
354
- </InternalData>,
355
- );
356
- wrapper.unmount();
357
- /**
358
- * We wait for the fulfillment to resolve.
359
- */
360
- await fulfillSpy.mock.results[0].value;
361
-
362
- // Assert
363
- expect(fakeChildrenFn).toHaveBeenCalledTimes(1);
364
- expect(fakeChildrenFn).not.toHaveBeenLastCalledWith({
365
- loading: false,
366
- data: "YAY! DATA!",
367
- });
368
- });
369
-
370
- it("should not setState if no longer mounted when request rejects", async () => {
371
- // Arrange
372
- const fulfillSpy = jest
373
- .spyOn(RequestFulfillment.Default, "fulfill")
374
- .mockReturnValue(Promise.reject("CATASTROPHE!"));
375
-
376
- const fakeHandler: IRequestHandler<string, string> = {
377
- fulfillRequest: () => Promise.resolve("YAY!"),
378
- getKey: (o) => o,
379
- shouldRefreshCache: () => false,
380
- type: "MY_HANDLER",
381
- cache: null,
382
- hydrate: true,
383
- };
384
- const fakeChildrenFn = jest.fn(() => null);
385
-
386
- // Act
387
- const wrapper = mount(
388
- <InternalData
389
- handler={fakeHandler}
390
- options={"options"}
391
- getEntry={jest.fn(() => null)}
392
- >
393
- {fakeChildrenFn}
394
- </InternalData>,
395
- );
396
- wrapper.unmount();
397
- /**
398
- * We wait for the fulfillment to reject.
399
- */
400
- await fulfillSpy.mock.results[0].value.catch(() => {});
401
-
402
- // Assert
403
- expect(fakeChildrenFn).toHaveBeenCalledTimes(1);
404
- expect(fakeChildrenFn).not.toHaveBeenLastCalledWith({
405
- loading: false,
406
- error: "CATASTROPHE!",
407
- });
408
- });
409
-
410
- it("should start loading if the handler changes and request not cached", async () => {
411
- // Arrange
412
- const fulfillSpy = jest.spyOn(
413
- RequestFulfillment.Default,
414
- "fulfill",
415
- );
416
-
417
- const fakeHandler: IRequestHandler<string, string> = {
418
- fulfillRequest: () => Promise.reject(new Error("OH NOES!")),
419
- getKey: (o) => o,
420
- shouldRefreshCache: () => false,
421
- type: "TYPE1",
422
- cache: null,
423
- hydrate: true,
424
- };
425
- const fakeHandler2: IRequestHandler<string, string> = {
426
- fulfillRequest: () => new Promise(() => {}),
427
- getKey: (o) => o,
428
- shouldRefreshCache: () => false,
429
- type: "TYPE2",
430
- cache: null,
431
- hydrate: true,
432
- };
433
- const fakeChildrenFn = jest.fn(() => null);
434
- const wrapper = mount(
435
- <InternalData
436
- handler={fakeHandler}
437
- options={"options"}
438
- getEntry={jest.fn(() => null)}
439
- >
440
- {fakeChildrenFn}
441
- </InternalData>,
442
- );
443
- // Make sure we render as laoded.
444
- await fulfillSpy.mock.results[0].value;
445
- // Clear out calls so everything is from the props change.
446
- fulfillSpy.mockClear();
447
- fakeChildrenFn.mockClear();
448
-
449
- // Act
450
- wrapper.setProps({
451
- handler: fakeHandler2,
452
- });
453
-
454
- // Assert
455
- expect(fakeChildrenFn).toHaveBeenCalledTimes(1);
456
- expect(fakeChildrenFn).toHaveBeenLastCalledWith({
457
- loading: true,
458
- });
459
- });
460
-
461
- it("should start loading if the options key changes and is not cached", async () => {
462
- // Arrange
463
- const fulfillSpy = jest.spyOn(
464
- RequestFulfillment.Default,
465
- "fulfill",
466
- );
467
-
468
- const fakeHandler: IRequestHandler<string, string> = {
469
- fulfillRequest: () => Promise.resolve("HELLO!"),
470
- getKey: (o) => o,
471
- shouldRefreshCache: () => false,
472
- type: "MY_HANDLER",
473
- cache: null,
474
- hydrate: true,
475
- };
476
- const fakeChildrenFn = jest.fn(() => null);
477
- const wrapper = mount(
478
- <InternalData
479
- handler={fakeHandler}
480
- options={"options"}
481
- getEntry={jest.fn(() => null)}
482
- >
483
- {fakeChildrenFn}
484
- </InternalData>,
485
- );
486
- await fulfillSpy.mock.results[0].value;
487
- fulfillSpy.mockClear();
488
- fakeChildrenFn.mockClear();
489
-
490
- // Act
491
- wrapper.setProps({
492
- options: "new-options",
493
- });
494
-
495
- // Assert
496
- expect(fakeChildrenFn).toHaveBeenCalledTimes(1);
497
- expect(fakeChildrenFn).toHaveBeenLastCalledWith({
498
- loading: true,
499
- });
500
- });
501
- });
502
-
503
- describe("with cache data", () => {
504
- it("should initialize state with data from cache", () => {
505
- // Arrange
506
- const fakeHandler: IRequestHandler<string, string> = {
507
- fulfillRequest: () => Promise.resolve("data"),
508
- getKey: (o) => o,
509
- shouldRefreshCache: () => false,
510
- type: "MY_HANDLER",
511
- cache: null,
512
- hydrate: true,
513
- };
514
- const fakeChildrenFn = jest.fn(() => null);
515
- const getEntryFn = jest.fn(() => ({
516
- data: "YAY! DATA!",
517
- }));
518
-
519
- // Act
520
- const wrapper = shallow(
521
- <InternalData
522
- handler={fakeHandler}
523
- options={"options"}
524
- getEntry={getEntryFn}
525
- >
526
- {fakeChildrenFn}
527
- </InternalData>,
528
- );
529
-
530
- // Assert
531
- expect(wrapper).toHaveState("loading", false);
532
- expect(wrapper).toHaveState("data", "YAY! DATA!");
533
- expect(wrapper).toHaveState("error", undefined);
534
- });
535
-
536
- it("should not request data if shouldRefreshCache returns false", () => {
537
- // Arrange
538
- const fulfillRequestSpy = jest.fn();
539
- const fakeHandler: IRequestHandler<string, string> = {
540
- fulfillRequest: fulfillRequestSpy,
541
- getKey: (o) => o,
542
- shouldRefreshCache: () => false,
543
- type: "MY_HANDLER",
544
- cache: null,
545
- hydrate: true,
546
- };
547
- const fakeChildrenFn = jest.fn(() => null);
548
- const getEntryFn = jest.fn(() => ({
549
- data: "YAY! DATA!",
550
- }));
551
-
552
- // Act
553
- shallow(
554
- <InternalData
555
- handler={fakeHandler}
556
- options={"options"}
557
- getEntry={getEntryFn}
558
- >
559
- {fakeChildrenFn}
560
- </InternalData>,
561
- );
562
-
563
- // Assert
564
- expect(fulfillRequestSpy).not.toHaveBeenCalled();
565
- });
566
-
567
- it("should request data if shouldRefreshCache returns true", () => {
568
- // Arrange
569
- const fulfillRequestSpy = jest.fn().mockResolvedValue("data");
570
- const fakeHandler: IRequestHandler<string, string> = {
571
- fulfillRequest: fulfillRequestSpy,
572
- getKey: (o) => o,
573
- shouldRefreshCache: () => true,
574
- type: "MY_HANDLER",
575
- cache: null,
576
- hydrate: true,
577
- };
578
- const fakeChildrenFn = jest.fn(() => null);
579
- const getEntryFn = jest.fn(() => ({
580
- data: "YAY! DATA!",
581
- }));
582
-
583
- // Act
584
- mount(
585
- <InternalData
586
- handler={fakeHandler}
587
- options={"options"}
588
- getEntry={getEntryFn}
589
- >
590
- {fakeChildrenFn}
591
- </InternalData>,
592
- );
593
-
594
- // Assert
595
- expect(fulfillRequestSpy).toHaveBeenCalledWith("options");
596
- expect(fulfillRequestSpy).toHaveBeenCalledTimes(1);
597
- });
598
-
599
- it("should render first time with the cached data", () => {
600
- // Arrange
601
- const fakeHandler: IRequestHandler<string, string> = {
602
- fulfillRequest: () => Promise.resolve("data"),
603
- getKey: (o) => o,
604
- shouldRefreshCache: () => false,
605
- type: "MY_HANDLER",
606
- cache: null,
607
- hydrate: true,
608
- };
609
- const fakeChildrenFn = jest.fn(() => null);
610
- const getEntryFn = jest.fn(() => ({
611
- data: "YAY! DATA!",
612
- }));
613
-
614
- // Act
615
- mount(
616
- <InternalData
617
- handler={fakeHandler}
618
- options={"options"}
619
- getEntry={getEntryFn}
620
- >
621
- {fakeChildrenFn}
622
- </InternalData>,
623
- );
624
-
625
- // Assert
626
- expect(fakeChildrenFn).toHaveBeenCalledWith({
627
- loading: false,
628
- data: "YAY! DATA!",
629
- });
630
- });
631
-
632
- it("should render first time with the cached error", () => {
633
- // Arrange
634
- const fakeHandler: IRequestHandler<string, string> = {
635
- fulfillRequest: () => Promise.resolve("data"),
636
- getKey: (o) => o,
637
- shouldRefreshCache: () => false,
638
- type: "MY_HANDLER",
639
- cache: null,
640
- hydrate: true,
641
- };
642
- const fakeChildrenFn = jest.fn(() => null);
643
- const getEntryFn = jest.fn(() => ({
644
- error: "OH NO!",
645
- }));
646
-
647
- // Act
648
- mount(
649
- <InternalData
650
- handler={fakeHandler}
651
- options={"options"}
652
- getEntry={getEntryFn}
653
- >
654
- {fakeChildrenFn}
655
- </InternalData>,
656
- );
657
-
658
- // Assert
659
- expect(fakeChildrenFn).toHaveBeenCalledWith({
660
- loading: false,
661
- error: "OH NO!",
662
- });
663
- });
664
-
665
- it("should throw if cached data is invalid", () => {
666
- // Arrange
667
- const fakeHandler: IRequestHandler<string, string> = {
668
- fulfillRequest: () => Promise.resolve("data"),
669
- getKey: (o) => o,
670
- shouldRefreshCache: () => false,
671
- type: "MY_HANDLER",
672
- cache: null,
673
- hydrate: true,
674
- };
675
- const fakeChildrenFn = jest.fn(() => null);
676
- const getEntryFn = jest.fn(() => ({}: any));
677
-
678
- // Act
679
- const underTest = () =>
680
- mount(
681
- <InternalData
682
- handler={fakeHandler}
683
- options={"options"}
684
- getEntry={getEntryFn}
685
- >
686
- {fakeChildrenFn}
687
- </InternalData>,
688
- );
689
-
690
- // Assert
691
- expect(underTest).toThrowErrorMatchingInlineSnapshot(
692
- `"Loaded result has invalid state where data and error are missing"`,
693
- );
694
- });
695
-
696
- it("should render with new data if the handler changes and request cached", () => {
697
- // Arrange
698
- const fakeHandler: IRequestHandler<string, string> = {
699
- fulfillRequest: () => Promise.reject(new Error("OH NOES!")),
700
- getKey: (o) => o,
701
- shouldRefreshCache: () => false,
702
- type: "TYPE1",
703
- cache: null,
704
- hydrate: true,
705
- };
706
- const fakeHandler2: IRequestHandler<string, string> = {
707
- fulfillRequest: () => new Promise(() => {}),
708
- getKey: (o) => o,
709
- shouldRefreshCache: () => false,
710
- type: "TYPE2",
711
- cache: null,
712
- hydrate: true,
713
- };
714
- const fakeChildrenFn = jest.fn(() => null);
715
- const getEntryFn = jest.fn(() => ({
716
- data: "YAY! DATA!",
717
- }));
718
- const wrapper = mount(
719
- <InternalData
720
- handler={fakeHandler}
721
- options={"options"}
722
- getEntry={getEntryFn}
723
- >
724
- {fakeChildrenFn}
725
- </InternalData>,
726
- );
727
- // Clear out calls so everything is from the props change.
728
- fakeChildrenFn.mockClear();
729
- getEntryFn.mockReturnValue({
730
- data: "NEW DATA!",
731
- });
732
-
733
- // Act
734
- wrapper.setProps({
735
- handler: fakeHandler2,
736
- });
737
-
738
- // Assert
739
- expect(fakeChildrenFn).toHaveBeenCalledTimes(1);
740
- expect(fakeChildrenFn).toHaveBeenLastCalledWith({
741
- loading: false,
742
- data: "NEW DATA!",
743
- });
744
- });
745
-
746
- it("should render with new data if the options key changes and is cached", () => {
747
- // Arrange
748
- const fakeHandler: IRequestHandler<string, string> = {
749
- fulfillRequest: () => Promise.resolve("Not called!"),
750
- getKey: (o) => o,
751
- shouldRefreshCache: () => false,
752
- type: "MY_HANDLER",
753
- cache: null,
754
- hydrate: true,
755
- };
756
- const fakeChildrenFn = jest.fn(() => null);
757
- const getEntryFn = jest.fn(() => ({
758
- data: "YAY! DATA!",
759
- }));
760
- const wrapper = mount(
761
- <InternalData
762
- handler={fakeHandler}
763
- options={"options"}
764
- getEntry={getEntryFn}
765
- >
766
- {fakeChildrenFn}
767
- </InternalData>,
768
- );
769
- fakeChildrenFn.mockClear();
770
- getEntryFn.mockReturnValue({
771
- data: "NEW DATA!",
772
- });
773
-
774
- // Act
775
- wrapper.setProps({
776
- options: "new-options",
777
- });
778
-
779
- // Assert
780
- expect(fakeChildrenFn).toHaveBeenCalledTimes(1);
781
- expect(fakeChildrenFn).toHaveBeenLastCalledWith({
782
- loading: false,
783
- data: "NEW DATA!",
784
- });
785
- });
786
- });
787
- });
788
-
789
- describe("SSR: isServerSide true", () => {
790
- beforeEach(() => {
791
- jest.spyOn(Server, "isServerSide").mockReturnValue(true);
792
- });
793
-
794
- describe("without cached data", () => {
795
- it("should not request data", () => {
796
- // Arrange
797
- const fulfillRequestSpy = jest.fn().mockResolvedValue("data");
798
- const fakeHandler: IRequestHandler<string, string> = {
799
- fulfillRequest: fulfillRequestSpy,
800
- getKey: (o) => o,
801
- shouldRefreshCache: () => false,
802
- type: "MY_HANDLER",
803
- cache: null,
804
- hydrate: true,
805
- };
806
- const fakeChildrenFn = jest.fn(() => null);
807
-
808
- // Act
809
- ReactDOMServer.renderToString(
810
- <InternalData
811
- handler={fakeHandler}
812
- options={"options"}
813
- getEntry={jest.fn(() => null)}
814
- >
815
- {fakeChildrenFn}
816
- </InternalData>,
817
- );
818
-
819
- // Assert
820
- expect(fulfillRequestSpy).not.toHaveBeenCalled();
821
- });
822
-
823
- it("should render children with loading", () => {
824
- // Arrange
825
- const fakeHandler: IRequestHandler<string, string> = {
826
- fulfillRequest: () => Promise.resolve("data"),
827
- getKey: (o) => o,
828
- shouldRefreshCache: () => false,
829
- type: "MY_HANDLER",
830
- cache: null,
831
- hydrate: true,
832
- };
833
- const fakeChildrenFn = jest.fn(() => null);
834
-
835
- // Act
836
- ReactDOMServer.renderToString(
837
- <InternalData
838
- handler={fakeHandler}
839
- options={"options"}
840
- getEntry={jest.fn(() => null)}
841
- >
842
- {fakeChildrenFn}
843
- </InternalData>,
844
- );
845
-
846
- // Assert
847
- expect(fakeChildrenFn).toHaveBeenCalledWith(
848
- expect.objectContaining({
849
- loading: true,
850
- }),
851
- );
852
- });
853
-
854
- it("should invoke the tracking call", () => {
855
- // Arrange
856
- const trackSpy = jest.spyOn(
857
- RequestTracker.Default,
858
- "trackDataRequest",
859
- );
860
- const fakeHandler: IRequestHandler<string, string> = {
861
- fulfillRequest: () => Promise.resolve("data"),
862
- getKey: (o) => o,
863
- shouldRefreshCache: () => false,
864
- type: "MY_HANDLER",
865
- cache: null,
866
- hydrate: true,
867
- };
868
- const fakeChildrenFn = jest.fn(() => null);
869
-
870
- // Act
871
- ReactDOMServer.renderToString(
872
- <TrackData>
873
- <InternalData
874
- handler={fakeHandler}
875
- options={"options"}
876
- getEntry={jest.fn(() => null)}
877
- >
878
- {fakeChildrenFn}
879
- </InternalData>
880
- </TrackData>,
881
- );
882
-
883
- // Assert
884
- expect(trackSpy).toHaveBeenCalledWith(fakeHandler, "options");
885
- });
886
- });
887
-
888
- describe("with cached data", () => {
889
- it("should not request data", () => {
890
- // Arrange
891
- const fulfillRequestSpy = jest.fn().mockResolvedValue("data");
892
- const fakeHandler: IRequestHandler<string, string> = {
893
- fulfillRequest: fulfillRequestSpy,
894
- getKey: (o) => o,
895
- shouldRefreshCache: () => false,
896
- type: "MY_HANDLER",
897
- cache: null,
898
- hydrate: true,
899
- };
900
- const fakeChildrenFn = jest.fn(() => null);
901
- const getEntryFn = jest.fn(() => ({
902
- data: "YAY! DATA!",
903
- }));
904
-
905
- // Act
906
- ReactDOMServer.renderToString(
907
- <InternalData
908
- handler={fakeHandler}
909
- options={"options"}
910
- getEntry={getEntryFn}
911
- >
912
- {fakeChildrenFn}
913
- </InternalData>,
914
- );
915
-
916
- // Assert
917
- expect(fulfillRequestSpy).not.toHaveBeenCalled();
918
- });
919
-
920
- it("should render children with data", () => {
921
- // Arrange
922
- const fakeHandler: IRequestHandler<string, string> = {
923
- fulfillRequest: () => Promise.resolve("data"),
924
- getKey: (o) => o,
925
- shouldRefreshCache: () => false,
926
- type: "MY_HANDLER",
927
- cache: null,
928
- hydrate: true,
929
- };
930
- const fakeChildrenFn = jest.fn(() => null);
931
- const getEntryFn = jest.fn(() => ({
932
- data: "YAY! DATA!",
933
- }));
934
-
935
- // Act
936
- ReactDOMServer.renderToString(
937
- <InternalData
938
- handler={fakeHandler}
939
- options={"options"}
940
- getEntry={getEntryFn}
941
- >
942
- {fakeChildrenFn}
943
- </InternalData>,
944
- );
945
-
946
- // Assert
947
- expect(fakeChildrenFn).toHaveBeenCalledWith(
948
- expect.objectContaining({
949
- loading: false,
950
- data: "YAY! DATA!",
951
- }),
952
- );
953
- });
954
-
955
- it("should render children with error", () => {
956
- // Arrange
957
- const fakeHandler: IRequestHandler<string, string> = {
958
- fulfillRequest: () => Promise.resolve("data"),
959
- getKey: (o) => o,
960
- shouldRefreshCache: () => false,
961
- type: "MY_HANDLER",
962
- cache: null,
963
- hydrate: true,
964
- };
965
- const fakeChildrenFn = jest.fn(() => null);
966
- const getEntryFn = jest.fn(() => ({
967
- error: "OH NO! IT GO BOOM",
968
- }));
969
-
970
- // Act
971
- ReactDOMServer.renderToString(
972
- <InternalData
973
- handler={fakeHandler}
974
- options={"options"}
975
- getEntry={getEntryFn}
976
- >
977
- {fakeChildrenFn}
978
- </InternalData>,
979
- );
980
-
981
- // Assert
982
- expect(fakeChildrenFn).toHaveBeenCalledWith(
983
- expect.objectContaining({
984
- loading: false,
985
- error: "OH NO! IT GO BOOM",
986
- }),
987
- );
988
- });
989
-
990
- it("should not invoke the tracking call", () => {
991
- // Arrange
992
- const trackSpy = jest.spyOn(
993
- RequestTracker.Default,
994
- "trackDataRequest",
995
- );
996
- const fakeHandler: IRequestHandler<string, string> = {
997
- fulfillRequest: () => Promise.resolve("data"),
998
- getKey: (o) => o,
999
- shouldRefreshCache: () => false,
1000
- type: "MY_HANDLER",
1001
- cache: null,
1002
- hydrate: true,
1003
- };
1004
- const fakeChildrenFn = jest.fn(() => null);
1005
- const getEntryFn = jest.fn(() => ({
1006
- data: "YAY! DATA!",
1007
- }));
1008
-
1009
- // Act
1010
- ReactDOMServer.renderToString(
1011
- <TrackData>
1012
- <InternalData
1013
- handler={fakeHandler}
1014
- options={"options"}
1015
- getEntry={getEntryFn}
1016
- >
1017
- {fakeChildrenFn}
1018
- </InternalData>
1019
- </TrackData>,
1020
- );
1021
-
1022
- // Assert
1023
- expect(trackSpy).not.toHaveBeenCalledWith(
1024
- fakeHandler,
1025
- "options",
1026
- );
1027
- });
1028
- });
1029
- });
1030
- });