@khanacademy/wonder-blocks-data 3.1.2 → 5.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 (51) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/dist/es/index.js +408 -349
  3. package/dist/index.js +568 -467
  4. package/docs.md +17 -35
  5. package/package.json +1 -1
  6. package/src/__tests__/__snapshots__/generated-snapshot.test.js.snap +7 -46
  7. package/src/__tests__/generated-snapshot.test.js +60 -126
  8. package/src/components/__tests__/data.test.js +373 -313
  9. package/src/components/__tests__/intercept-requests.test.js +58 -0
  10. package/src/components/data.js +139 -21
  11. package/src/components/data.md +38 -69
  12. package/src/components/gql-router.js +1 -1
  13. package/src/components/intercept-context.js +6 -3
  14. package/src/components/intercept-requests.js +69 -0
  15. package/src/components/intercept-requests.md +54 -0
  16. package/src/components/track-data.md +9 -23
  17. package/src/hooks/__tests__/__snapshots__/use-shared-cache.test.js.snap +17 -0
  18. package/src/hooks/__tests__/use-gql.test.js +1 -0
  19. package/src/hooks/__tests__/use-request-interception.test.js +255 -0
  20. package/src/hooks/__tests__/use-server-effect.test.js +217 -0
  21. package/src/hooks/__tests__/use-shared-cache.test.js +307 -0
  22. package/src/hooks/use-gql.js +39 -31
  23. package/src/hooks/use-request-interception.js +54 -0
  24. package/src/hooks/use-server-effect.js +45 -0
  25. package/src/hooks/use-shared-cache.js +106 -0
  26. package/src/index.js +17 -20
  27. package/src/util/__tests__/__snapshots__/scoped-in-memory-cache.test.js.snap +19 -0
  28. package/src/util/__tests__/request-fulfillment.test.js +42 -85
  29. package/src/util/__tests__/request-tracking.test.js +72 -191
  30. package/src/util/__tests__/{result-from-cache-entry.test.js → result-from-cache-response.test.js} +9 -10
  31. package/src/util/__tests__/scoped-in-memory-cache.test.js +396 -0
  32. package/src/util/__tests__/ssr-cache.test.js +639 -0
  33. package/src/util/gql-types.js +5 -10
  34. package/src/util/request-fulfillment.js +36 -44
  35. package/src/util/request-tracking.js +62 -75
  36. package/src/util/{result-from-cache-entry.js → result-from-cache-response.js} +10 -13
  37. package/src/util/scoped-in-memory-cache.js +149 -0
  38. package/src/util/ssr-cache.js +206 -0
  39. package/src/util/types.js +43 -108
  40. package/src/components/__tests__/intercept-data.test.js +0 -87
  41. package/src/components/intercept-data.js +0 -77
  42. package/src/components/intercept-data.md +0 -65
  43. package/src/hooks/__tests__/use-data.test.js +0 -826
  44. package/src/hooks/use-data.js +0 -143
  45. package/src/util/__tests__/memory-cache.test.js +0 -446
  46. package/src/util/__tests__/request-handler.test.js +0 -121
  47. package/src/util/__tests__/response-cache.test.js +0 -879
  48. package/src/util/memory-cache.js +0 -187
  49. package/src/util/request-handler.js +0 -42
  50. package/src/util/request-handler.md +0 -51
  51. package/src/util/response-cache.js +0 -213
@@ -0,0 +1,639 @@
1
+ // @flow
2
+ import {Server} from "@khanacademy/wonder-blocks-core";
3
+ import {SsrCache} from "../ssr-cache.js";
4
+ import {ScopedInMemoryCache} from "../scoped-in-memory-cache.js";
5
+
6
+ describe("../ssr-cache.js", () => {
7
+ afterEach(() => {
8
+ /**
9
+ * This is needed or the JSON.stringify mocks need to be
10
+ * mockImplementationOnce. This is because if the snapshots need
11
+ * to update, they write the inline snapshot and that appears to invoke
12
+ * prettier which in turn, calls JSON.stringify. And if that mock
13
+ * throws, then boom. No snapshot update and a big old confusing test
14
+ * failure.
15
+ */
16
+ jest.restoreAllMocks();
17
+ });
18
+
19
+ describe("@Default", () => {
20
+ it("should return an instance of SsrCache", () => {
21
+ // Arrange
22
+
23
+ // Act
24
+ const result = SsrCache.Default;
25
+
26
+ // Assert
27
+ expect(result).toBeInstanceOf(SsrCache);
28
+ });
29
+
30
+ it("should return the same instance on each call", () => {
31
+ // Arrange
32
+
33
+ // Act
34
+ const result1 = SsrCache.Default;
35
+ const result2 = SsrCache.Default;
36
+
37
+ // Assert
38
+ expect(result1).toBe(result2);
39
+ });
40
+ });
41
+
42
+ describe("#initialize", () => {
43
+ it("should initialize the cache with the given data", () => {
44
+ // Arrange
45
+ const cache = new SsrCache();
46
+
47
+ // Act
48
+ cache.initialize({
49
+ MY_KEY: {data: "THE_DATA"},
50
+ });
51
+ const result = cache.getEntry("MY_KEY");
52
+
53
+ // Assert
54
+ expect(result).toStrictEqual({data: "THE_DATA"});
55
+ });
56
+
57
+ it("should throw if the cache is already intialized", () => {
58
+ // Arrange
59
+ const internalCache = new ScopedInMemoryCache({
60
+ MY_KEY: {data: "THE_DATA"},
61
+ });
62
+ const cache = new SsrCache(internalCache);
63
+
64
+ // Act
65
+ const underTest = () =>
66
+ cache.initialize({
67
+ MY_OTHER_KEY: {data: "MORE_DATA"},
68
+ });
69
+
70
+ // Assert
71
+ expect(underTest).toThrowErrorMatchingInlineSnapshot(
72
+ `"Cannot initialize data response cache more than once"`,
73
+ );
74
+ });
75
+
76
+ it("should deep clone the cached data", () => {
77
+ // Arrange
78
+ const cache = new SsrCache();
79
+ const sourceData = {
80
+ MY_KEY: {data: "THE_DATA"},
81
+ };
82
+
83
+ // Act
84
+ cache.initialize(sourceData);
85
+ // Try to mutate the cache.
86
+ sourceData["MY_KEY"] = {data: "SOME_NEW_DATA"};
87
+ const result = cache.getEntry("MY_KEY");
88
+
89
+ // Assert
90
+ expect(result).toStrictEqual({data: "THE_DATA"});
91
+ });
92
+ });
93
+
94
+ describe("#cacheData", () => {
95
+ describe("when client-side", () => {
96
+ it("should not store the entry in the hydration cache", () => {
97
+ // Arrange
98
+ const hydrationCache = new ScopedInMemoryCache();
99
+ const cache = new SsrCache(hydrationCache);
100
+ const hydrationStoreSpy = jest.spyOn(hydrationCache, "set");
101
+
102
+ // Act
103
+ cache.cacheData("MY_KEY", "data", true);
104
+
105
+ // Assert
106
+ expect(hydrationStoreSpy).not.toHaveBeenCalled();
107
+ });
108
+
109
+ it("should not store the entry in the ssrOnly cache", () => {
110
+ // Arrange
111
+ const hydrationCache = new ScopedInMemoryCache();
112
+ const ssrOnlyCache = new ScopedInMemoryCache();
113
+ const cache = new SsrCache(hydrationCache, ssrOnlyCache);
114
+ const ssrOnlyStoreSpy = jest.spyOn(ssrOnlyCache, "set");
115
+
116
+ // Act
117
+ cache.cacheData("MY_KEY", "data", false);
118
+
119
+ // Assert
120
+ expect(ssrOnlyStoreSpy).not.toHaveBeenCalled();
121
+ });
122
+ });
123
+
124
+ describe("when server-side", () => {
125
+ beforeEach(() => {
126
+ jest.spyOn(Server, "isServerSide").mockReturnValue(true);
127
+ });
128
+
129
+ describe("when hydrate is true", () => {
130
+ it("should store the entry in the hydration cache", () => {
131
+ // Arrange
132
+ const hydrationCache = new ScopedInMemoryCache();
133
+ const cache = new SsrCache(hydrationCache);
134
+ const hydrationStoreSpy = jest.spyOn(hydrationCache, "set");
135
+
136
+ // Act
137
+ cache.cacheData("MY_KEY", "data", true);
138
+
139
+ // Assert
140
+ expect(hydrationStoreSpy).toHaveBeenCalledWith(
141
+ "default",
142
+ "MY_KEY",
143
+ {
144
+ data: "data",
145
+ },
146
+ );
147
+ });
148
+
149
+ it("should not store the entry in the ssrOnly cache", () => {
150
+ // Arrange
151
+ const hydrationCache = new ScopedInMemoryCache();
152
+ const ssrOnlyCache = new ScopedInMemoryCache();
153
+ const cache = new SsrCache(hydrationCache, ssrOnlyCache);
154
+ const ssrOnlyStoreSpy = jest.spyOn(ssrOnlyCache, "set");
155
+
156
+ // Act
157
+ cache.cacheData("MY_KEY", "data", true);
158
+
159
+ // Assert
160
+ expect(ssrOnlyStoreSpy).not.toHaveBeenCalled();
161
+ });
162
+ });
163
+
164
+ describe("when hydrate is false", () => {
165
+ it("should store the entry in the ssr-only cache", () => {
166
+ // Arrange
167
+ const hydrationCache = new ScopedInMemoryCache();
168
+ const ssrOnlyCache = new ScopedInMemoryCache();
169
+ const cache = new SsrCache(hydrationCache, ssrOnlyCache);
170
+ const ssrOnlyStoreSpy = jest.spyOn(ssrOnlyCache, "set");
171
+
172
+ // Act
173
+ cache.cacheData("MY_KEY", "data", false);
174
+
175
+ // Assert
176
+ expect(ssrOnlyStoreSpy).toHaveBeenCalledWith(
177
+ "default",
178
+ "MY_KEY",
179
+ {
180
+ data: "data",
181
+ },
182
+ );
183
+ });
184
+
185
+ it("should not store the entry in the hydration cache", () => {
186
+ // Arrange
187
+ const hydrationCache = new ScopedInMemoryCache();
188
+ const ssrOnlyCache = new ScopedInMemoryCache();
189
+ const cache = new SsrCache(hydrationCache, ssrOnlyCache);
190
+ const hydrationStoreSpy = jest.spyOn(hydrationCache, "set");
191
+
192
+ // Act
193
+ cache.cacheData("MY_KEY", "data", false);
194
+
195
+ // Assert
196
+ expect(hydrationStoreSpy).not.toHaveBeenCalled();
197
+ });
198
+ });
199
+ });
200
+ });
201
+
202
+ describe("#cacheError", () => {
203
+ describe("when client-side", () => {
204
+ it("should not store the entry in the hydration cache", () => {
205
+ // Arrange
206
+ const hydrationCache = new ScopedInMemoryCache();
207
+ const cache = new SsrCache(hydrationCache);
208
+ const hydrationStoreSpy = jest.spyOn(hydrationCache, "set");
209
+
210
+ // Act
211
+ cache.cacheError("MY_KEY", new Error("Ooops!"), true);
212
+
213
+ // Assert
214
+ expect(hydrationStoreSpy).not.toHaveBeenCalled();
215
+ });
216
+
217
+ it("should not store the entry in the ssrOnly cache", () => {
218
+ // Arrange
219
+ const hydrationCache = new ScopedInMemoryCache();
220
+ const ssrOnlyCache = new ScopedInMemoryCache();
221
+ const cache = new SsrCache(hydrationCache, ssrOnlyCache);
222
+ const ssrOnlyStoreSpy = jest.spyOn(ssrOnlyCache, "set");
223
+
224
+ // Act
225
+ cache.cacheError("MY_KEY", "Ooops!", false);
226
+
227
+ // Assert
228
+ expect(ssrOnlyStoreSpy).not.toHaveBeenCalled();
229
+ });
230
+ });
231
+
232
+ describe("when server-side", () => {
233
+ beforeEach(() => {
234
+ jest.spyOn(Server, "isServerSide").mockReturnValue(true);
235
+ });
236
+
237
+ describe("when hydrate is true", () => {
238
+ it("should store the entry in the hydration cache", () => {
239
+ // Arrange
240
+ const hydrationCache = new ScopedInMemoryCache();
241
+ const cache = new SsrCache(hydrationCache);
242
+ const hydrationStoreSpy = jest.spyOn(hydrationCache, "set");
243
+
244
+ // Act
245
+ cache.cacheError("MY_KEY", new Error("Ooops!"), true);
246
+
247
+ // Assert
248
+ expect(hydrationStoreSpy).toHaveBeenCalledWith(
249
+ "default",
250
+ "MY_KEY",
251
+ {
252
+ error: "Ooops!",
253
+ },
254
+ );
255
+ });
256
+
257
+ it("should not store the entry in the ssrOnly cache", () => {
258
+ // Arrange
259
+ const hydrationCache = new ScopedInMemoryCache();
260
+ const ssrOnlyCache = new ScopedInMemoryCache();
261
+ const cache = new SsrCache(hydrationCache, ssrOnlyCache);
262
+ const ssrOnlyStoreSpy = jest.spyOn(ssrOnlyCache, "set");
263
+
264
+ // Act
265
+ cache.cacheError("MY_KEY", new Error("Ooops!"), true);
266
+
267
+ // Assert
268
+ expect(ssrOnlyStoreSpy).not.toHaveBeenCalled();
269
+ });
270
+ });
271
+
272
+ describe("when hydrate is false", () => {
273
+ it("should store the entry in the ssr-only cache", () => {
274
+ // Arrange
275
+ const hydrationCache = new ScopedInMemoryCache();
276
+ const ssrOnlyCache = new ScopedInMemoryCache();
277
+ const cache = new SsrCache(hydrationCache, ssrOnlyCache);
278
+ const ssrOnlyStoreSpy = jest.spyOn(ssrOnlyCache, "set");
279
+
280
+ // Act
281
+ cache.cacheError("MY_KEY", new Error("Ooops!"), false);
282
+
283
+ // Assert
284
+ expect(ssrOnlyStoreSpy).toHaveBeenCalledWith(
285
+ "default",
286
+ "MY_KEY",
287
+ {
288
+ error: "Ooops!",
289
+ },
290
+ );
291
+ });
292
+
293
+ it("should not store the entry in the hydration cache", () => {
294
+ // Arrange
295
+ const hydrationCache = new ScopedInMemoryCache();
296
+ const ssrOnlyCache = new ScopedInMemoryCache();
297
+ const cache = new SsrCache(hydrationCache, ssrOnlyCache);
298
+ const hydrationStoreSpy = jest.spyOn(hydrationCache, "set");
299
+
300
+ // Act
301
+ cache.cacheError("MY_KEY", new Error("Ooops!"), false);
302
+
303
+ // Assert
304
+ expect(hydrationStoreSpy).not.toHaveBeenCalled();
305
+ });
306
+ });
307
+ });
308
+ });
309
+
310
+ describe("#getEntry", () => {
311
+ describe("when client-side", () => {
312
+ beforeEach(() => {
313
+ jest.spyOn(Server, "isServerSide").mockReturnValue(false);
314
+ });
315
+
316
+ it("should return null if not in the hydration cache", () => {
317
+ // Arrange
318
+ const hydrationCache = new ScopedInMemoryCache();
319
+ jest.spyOn(hydrationCache, "get").mockReturnValue(null);
320
+ const cache = new SsrCache(hydrationCache);
321
+
322
+ // Act
323
+ const result = cache.getEntry("MY_KEY");
324
+
325
+ // Assert
326
+ expect(result).toBeNull();
327
+ });
328
+
329
+ it("should return the cached entry if in the hydration cache", () => {
330
+ // Arrange
331
+ const hydrationCache = new ScopedInMemoryCache();
332
+ jest.spyOn(hydrationCache, "get").mockReturnValue({
333
+ data: "data!",
334
+ });
335
+ const cache = new SsrCache(hydrationCache);
336
+
337
+ // Act
338
+ const result = cache.getEntry("MY_KEY");
339
+
340
+ // Assert
341
+ expect(result).toStrictEqual({data: "data!"});
342
+ });
343
+ });
344
+
345
+ describe("when server-side", () => {
346
+ beforeEach(() => {
347
+ jest.spyOn(Server, "isServerSide").mockReturnValue(true);
348
+ });
349
+
350
+ it("should return null in any cache", () => {
351
+ // Arrange
352
+ const hydrationCache = new ScopedInMemoryCache();
353
+ const ssrOnlyCache = new ScopedInMemoryCache();
354
+ const cache = new SsrCache(hydrationCache, ssrOnlyCache);
355
+
356
+ // Act
357
+ const result = cache.getEntry("MY_KEY");
358
+
359
+ // Assert
360
+ expect(result).toBeNull();
361
+ });
362
+
363
+ it("should return the cached entry if in the hydration cache", () => {
364
+ // Arrange
365
+ const hydrationCache = new ScopedInMemoryCache();
366
+ jest.spyOn(hydrationCache, "get").mockReturnValue({
367
+ data: "data!",
368
+ });
369
+ const cache = new SsrCache(hydrationCache);
370
+
371
+ // Act
372
+ const result = cache.getEntry("MY_KEY");
373
+
374
+ // Assert
375
+ expect(result).toStrictEqual({data: "data!"});
376
+ });
377
+
378
+ it("should return the cached entry if in the ssr-only cache", () => {
379
+ // Arrange
380
+ const hydrationCache = new ScopedInMemoryCache();
381
+ const ssrOnlyCache = new ScopedInMemoryCache();
382
+ jest.spyOn(ssrOnlyCache, "get").mockReturnValue({
383
+ data: "data!",
384
+ });
385
+ const cache = new SsrCache(hydrationCache, ssrOnlyCache);
386
+
387
+ // Act
388
+ const result = cache.getEntry("MY_KEY");
389
+
390
+ // Assert
391
+ expect(result).toStrictEqual({data: "data!"});
392
+ });
393
+ });
394
+ });
395
+
396
+ describe("#remove", () => {
397
+ it("should return false if nothing was removed", () => {
398
+ // Arrange
399
+ const hydrationCache = new ScopedInMemoryCache();
400
+ const ssrOnlycache = new ScopedInMemoryCache();
401
+ jest.spyOn(hydrationCache, "purge").mockReturnValue(false);
402
+ jest.spyOn(ssrOnlycache, "purge").mockReturnValue(false);
403
+ const cache = new SsrCache(hydrationCache, ssrOnlycache);
404
+
405
+ // Act
406
+ const result = cache.remove("A");
407
+
408
+ // Assert
409
+ expect(result).toBeFalsy();
410
+ });
411
+
412
+ it("should return true if something was removed from hydration cache", () => {
413
+ // Arrange
414
+ const hydrationCache = new ScopedInMemoryCache();
415
+ jest.spyOn(hydrationCache, "purge").mockReturnValue(true);
416
+ const cache = new SsrCache(hydrationCache);
417
+
418
+ // Act
419
+ const result = cache.remove("A");
420
+
421
+ // Assert
422
+ expect(result).toBeTruthy();
423
+ });
424
+
425
+ describe("when server-side", () => {
426
+ beforeEach(() => {
427
+ jest.spyOn(Server, "isServerSide").mockReturnValue(true);
428
+ });
429
+
430
+ it("should return true if something was removed from ssr-only cache", () => {
431
+ // Arrange
432
+ const hydrationCache = new ScopedInMemoryCache();
433
+ const ssrOnlyCache = new ScopedInMemoryCache();
434
+ jest.spyOn(ssrOnlyCache, "purge").mockReturnValue(true);
435
+ const cache = new SsrCache(hydrationCache, ssrOnlyCache);
436
+
437
+ // Act
438
+ const result = cache.remove("A");
439
+
440
+ // Assert
441
+ expect(result).toBeTruthy();
442
+ });
443
+ });
444
+ });
445
+
446
+ describe("#cloneHydratableData", () => {
447
+ it("should clone the hydration cache", () => {
448
+ // Arrange
449
+ const hydrationCache = new ScopedInMemoryCache();
450
+ const cloneSpy = jest
451
+ .spyOn(hydrationCache, "clone")
452
+ .mockReturnValue({
453
+ default: "CLONE!",
454
+ });
455
+ const cache = new SsrCache(hydrationCache);
456
+ // Let's add to the initialized state to check that everything
457
+ // is cloning as we expect.
458
+ cache.cacheData("KEY1", "DATA", true);
459
+ cache.cacheError("KEY2", new Error("OH NO!"), true);
460
+
461
+ // Act
462
+ const result = cache.cloneHydratableData();
463
+
464
+ // Assert
465
+ expect(cloneSpy).toHaveBeenCalled();
466
+ expect(result).toBe("CLONE!");
467
+ });
468
+ });
469
+
470
+ describe("#removeAll", () => {
471
+ describe("when client-side", () => {
472
+ beforeEach(() => {
473
+ jest.spyOn(Server, "isServerSide").mockReturnValue(false);
474
+ });
475
+
476
+ it("should remove all entries from the hydration cache when client-side without predicate", () => {
477
+ // Arrange
478
+ const hydrationCache = new ScopedInMemoryCache();
479
+ const purgeAllSpy = jest.spyOn(hydrationCache, "purgeAll");
480
+ const cache = new SsrCache(hydrationCache);
481
+
482
+ // Act
483
+ cache.removeAll();
484
+
485
+ // Assert
486
+ expect(purgeAllSpy).toHaveBeenCalledWith(undefined);
487
+ });
488
+
489
+ it("should pass a predicate to hydration cache purge if a predicate is passed", () => {
490
+ // Arrange
491
+ const hydrationCache = new ScopedInMemoryCache();
492
+ const purgeAllSpy = jest.spyOn(hydrationCache, "purgeAll");
493
+ const cache = new SsrCache(
494
+ hydrationCache,
495
+ new ScopedInMemoryCache(),
496
+ );
497
+
498
+ // Act
499
+ cache.removeAll(() => true);
500
+
501
+ // Assert
502
+ expect(purgeAllSpy).toHaveBeenCalledWith(expect.any(Function));
503
+ });
504
+
505
+ it("should pass a predicate to the hydration cache that calls the predicate it was given", () => {
506
+ // Arrange
507
+ const hydrationCache = new ScopedInMemoryCache({
508
+ default: {
509
+ KEY1: {
510
+ data: "DATA",
511
+ },
512
+ },
513
+ });
514
+ const cache = new SsrCache(
515
+ hydrationCache,
516
+ new ScopedInMemoryCache(),
517
+ );
518
+ const predicate = jest.fn().mockReturnValue(false);
519
+
520
+ // Act
521
+ cache.removeAll(predicate);
522
+
523
+ // Assert
524
+ expect(predicate).toHaveBeenCalledWith("KEY1", {data: "DATA"});
525
+ });
526
+ });
527
+
528
+ describe("when server-side", () => {
529
+ beforeEach(() => {
530
+ jest.spyOn(Server, "isServerSide").mockReturnValue(true);
531
+ });
532
+
533
+ it("should remove all entries from hydration cache when server-side without predicate", () => {
534
+ // Arrange
535
+ const hydrationCache = new ScopedInMemoryCache();
536
+ const hydrationPurgeAllSpy = jest.spyOn(
537
+ hydrationCache,
538
+ "purgeAll",
539
+ );
540
+ const cache = new SsrCache(
541
+ hydrationCache,
542
+ new ScopedInMemoryCache(),
543
+ );
544
+
545
+ // Act
546
+ cache.removeAll();
547
+
548
+ // Assert
549
+ expect(hydrationPurgeAllSpy).toHaveBeenCalledWith(undefined);
550
+ });
551
+
552
+ it("should remove all entries from ssr cache when server-side without predicate", () => {
553
+ // Arrange
554
+ const ssrOnlyCache = new ScopedInMemoryCache();
555
+ const ssrPurgeAllSpy = jest.spyOn(ssrOnlyCache, "purgeAll");
556
+ const cache = new SsrCache(
557
+ new ScopedInMemoryCache(),
558
+ ssrOnlyCache,
559
+ );
560
+
561
+ // Act
562
+ cache.removeAll();
563
+
564
+ // Assert
565
+ expect(ssrPurgeAllSpy).toHaveBeenCalledWith(undefined);
566
+ });
567
+
568
+ it("should pass a predicate to hydration cache purge if a predicate is passed", () => {
569
+ // Arrange
570
+ const hydrationCache = new ScopedInMemoryCache();
571
+ const purgeAllSpy = jest.spyOn(hydrationCache, "purgeAll");
572
+ const cache = new SsrCache(
573
+ hydrationCache,
574
+ new ScopedInMemoryCache(),
575
+ );
576
+
577
+ // Act
578
+ cache.removeAll(() => true);
579
+
580
+ // Assert
581
+ expect(purgeAllSpy).toHaveBeenCalledWith(expect.any(Function));
582
+ });
583
+
584
+ it("should pass a predicate to srr cache purge if a predicate is passed", () => {
585
+ // Arrange
586
+ const ssrOnlyCache = new ScopedInMemoryCache();
587
+ const purgeAllSpy = jest.spyOn(ssrOnlyCache, "purgeAll");
588
+ const cache = new SsrCache(
589
+ new ScopedInMemoryCache(),
590
+ ssrOnlyCache,
591
+ );
592
+
593
+ // Act
594
+ cache.removeAll(() => true);
595
+
596
+ // Assert
597
+ expect(purgeAllSpy).toHaveBeenCalledWith(expect.any(Function));
598
+ });
599
+
600
+ it("should pass a predicate to the hydration cache that calls the predicate it was given", () => {
601
+ // Arrange
602
+ const hydrationCache = new ScopedInMemoryCache();
603
+ const cache = new SsrCache(
604
+ hydrationCache,
605
+ new ScopedInMemoryCache(),
606
+ );
607
+ cache.cacheData("KEY1", "DATA", true);
608
+ const predicate = jest.fn().mockReturnValue(false);
609
+
610
+ // Act
611
+ cache.removeAll(predicate);
612
+
613
+ // Assert
614
+ expect(predicate).toHaveBeenCalledWith("KEY1", {data: "DATA"});
615
+ });
616
+
617
+ it("should pass a predicate to the ssr cache that calls the predicate it was given", () => {
618
+ // Arrange
619
+ const ssrOnlyCache = new ScopedInMemoryCache();
620
+ const cache = new SsrCache(
621
+ new ScopedInMemoryCache(),
622
+ ssrOnlyCache,
623
+ );
624
+ cache.cacheData(
625
+ "KEY1",
626
+ "DATA",
627
+ false /*false so that the data goes into the ssr only cache*/,
628
+ );
629
+ const predicate = jest.fn().mockReturnValue(false);
630
+
631
+ // Act
632
+ cache.removeAll(predicate);
633
+
634
+ // Assert
635
+ expect(predicate).toHaveBeenCalledWith("KEY1", {data: "DATA"});
636
+ });
637
+ });
638
+ });
639
+ });
@@ -8,7 +8,6 @@ export type GqlOperationType = "mutation" | "query";
8
8
  * A GraphQL operation.
9
9
  */
10
10
  export type GqlOperation<
11
- TType: GqlOperationType,
12
11
  // TData is not used to define a field on this type, but it is used
13
12
  // to ensure that calls using this operation will properly return the
14
13
  // correct data type.
@@ -20,13 +19,14 @@ export type GqlOperation<
20
19
  // eslint-disable-next-line no-unused-vars
21
20
  TVariables: {...} = Empty,
22
21
  > = {
23
- type: TType,
22
+ type: GqlOperationType,
24
23
  id: string,
25
24
  // We allow other things here to be passed along to the fetch function.
26
25
  // For example, we might want to pass the full query/mutation definition
27
26
  // as a string here to allow that to be sent to an Apollo server that
28
27
  // expects it. This is a courtesy to calling code; these additional
29
28
  // values are ignored by WB Data, and passed through as-is.
29
+ [key: string]: mixed,
30
30
  ...
31
31
  };
32
32
 
@@ -37,13 +37,8 @@ export type GqlContext = {|
37
37
  /**
38
38
  * Functions that make fetches of GQL operations.
39
39
  */
40
- export type GqlFetchFn<
41
- TType,
42
- TData,
43
- TVariables: {...},
44
- TContext: GqlContext,
45
- > = (
46
- operation: GqlOperation<TType, TData, TVariables>,
40
+ export type GqlFetchFn<TData, TVariables: {...}, TContext: GqlContext> = (
41
+ operation: GqlOperation<TData, TVariables>,
47
42
  variables: ?TVariables,
48
43
  context: TContext,
49
44
  ) => Promise<Response>;
@@ -52,7 +47,7 @@ export type GqlFetchFn<
52
47
  * The configuration stored in the GqlRouterContext context.
53
48
  */
54
49
  export type GqlRouterConfiguration<TContext: GqlContext> = {|
55
- fetch: GqlFetchFn<any, any, any, any>,
50
+ fetch: GqlFetchFn<any, any, any>,
56
51
  defaultContext: TContext,
57
52
  |};
58
53