@khanacademy/wonder-blocks-data 13.0.10 → 13.0.12
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.
- package/CHANGELOG.md +15 -0
- package/package.json +3 -3
- package/src/components/__tests__/data.test.tsx +0 -832
- package/src/components/__tests__/gql-router.test.tsx +0 -63
- package/src/components/__tests__/intercept-requests.test.tsx +0 -57
- package/src/components/__tests__/track-data.test.tsx +0 -56
- package/src/components/data.ts +0 -73
- package/src/components/gql-router.tsx +0 -63
- package/src/components/intercept-context.ts +0 -19
- package/src/components/intercept-requests.tsx +0 -67
- package/src/components/track-data.tsx +0 -28
- package/src/hooks/__tests__/__snapshots__/use-shared-cache.test.ts.snap +0 -17
- package/src/hooks/__tests__/use-cached-effect.test.tsx +0 -789
- package/src/hooks/__tests__/use-gql-router-context.test.tsx +0 -132
- package/src/hooks/__tests__/use-gql.test.tsx +0 -204
- package/src/hooks/__tests__/use-hydratable-effect.test.ts +0 -708
- package/src/hooks/__tests__/use-request-interception.test.tsx +0 -254
- package/src/hooks/__tests__/use-server-effect.test.ts +0 -293
- package/src/hooks/__tests__/use-shared-cache.test.ts +0 -263
- package/src/hooks/use-cached-effect.ts +0 -297
- package/src/hooks/use-gql-router-context.ts +0 -49
- package/src/hooks/use-gql.ts +0 -58
- package/src/hooks/use-hydratable-effect.ts +0 -201
- package/src/hooks/use-request-interception.ts +0 -53
- package/src/hooks/use-server-effect.ts +0 -75
- package/src/hooks/use-shared-cache.ts +0 -107
- package/src/index.ts +0 -46
- package/src/util/__tests__/__snapshots__/scoped-in-memory-cache.test.ts.snap +0 -19
- package/src/util/__tests__/__snapshots__/serializable-in-memory-cache.test.ts.snap +0 -19
- package/src/util/__tests__/get-gql-data-from-response.test.ts +0 -186
- package/src/util/__tests__/get-gql-request-id.test.ts +0 -132
- package/src/util/__tests__/graphql-document-node-parser.test.ts +0 -535
- package/src/util/__tests__/hydration-cache-api.test.ts +0 -34
- package/src/util/__tests__/merge-gql-context.test.ts +0 -73
- package/src/util/__tests__/purge-caches.test.ts +0 -28
- package/src/util/__tests__/request-api.test.ts +0 -176
- package/src/util/__tests__/request-fulfillment.test.ts +0 -146
- package/src/util/__tests__/request-tracking.test.tsx +0 -321
- package/src/util/__tests__/result-from-cache-response.test.ts +0 -79
- package/src/util/__tests__/scoped-in-memory-cache.test.ts +0 -316
- package/src/util/__tests__/serializable-in-memory-cache.test.ts +0 -397
- package/src/util/__tests__/ssr-cache.test.ts +0 -636
- package/src/util/__tests__/to-gql-operation.test.ts +0 -41
- package/src/util/data-error.ts +0 -63
- package/src/util/get-gql-data-from-response.ts +0 -65
- package/src/util/get-gql-request-id.ts +0 -106
- package/src/util/gql-error.ts +0 -43
- package/src/util/gql-router-context.ts +0 -9
- package/src/util/gql-types.ts +0 -64
- package/src/util/graphql-document-node-parser.ts +0 -132
- package/src/util/graphql-types.ts +0 -28
- package/src/util/hydration-cache-api.ts +0 -30
- package/src/util/merge-gql-context.ts +0 -35
- package/src/util/purge-caches.ts +0 -14
- package/src/util/request-api.ts +0 -65
- package/src/util/request-fulfillment.ts +0 -121
- package/src/util/request-tracking.ts +0 -211
- package/src/util/result-from-cache-response.ts +0 -30
- package/src/util/scoped-in-memory-cache.ts +0 -121
- package/src/util/serializable-in-memory-cache.ts +0 -44
- package/src/util/ssr-cache.ts +0 -193
- package/src/util/status.ts +0 -35
- package/src/util/to-gql-operation.ts +0 -43
- package/src/util/types.ts +0 -145
- package/tsconfig-build.json +0 -12
- package/tsconfig-build.tsbuildinfo +0 -1
|
@@ -1,832 +0,0 @@
|
|
|
1
|
-
/* eslint-disable max-lines */
|
|
2
|
-
import * as React from "react";
|
|
3
|
-
import {render, act} from "@testing-library/react";
|
|
4
|
-
|
|
5
|
-
// eslint-disable-next-line import/extensions
|
|
6
|
-
import * as ReactDOMServer from "react-dom/server";
|
|
7
|
-
import {Server, View} from "@khanacademy/wonder-blocks-core";
|
|
8
|
-
|
|
9
|
-
import {SharedCache} from "../../hooks/use-shared-cache";
|
|
10
|
-
import TrackData from "../track-data";
|
|
11
|
-
import {RequestFulfillment} from "../../util/request-fulfillment";
|
|
12
|
-
import {SsrCache} from "../../util/ssr-cache";
|
|
13
|
-
import {RequestTracker} from "../../util/request-tracking";
|
|
14
|
-
import InterceptRequests from "../intercept-requests";
|
|
15
|
-
import Data from "../data";
|
|
16
|
-
import {WhenClientSide} from "../../hooks/use-hydratable-effect";
|
|
17
|
-
|
|
18
|
-
describe("Data", () => {
|
|
19
|
-
beforeEach(() => {
|
|
20
|
-
SharedCache.purgeAll();
|
|
21
|
-
|
|
22
|
-
const responseCache = new SsrCache();
|
|
23
|
-
jest.spyOn(SsrCache, "Default", "get").mockReturnValue(responseCache);
|
|
24
|
-
jest.spyOn(RequestFulfillment, "Default", "get").mockReturnValue(
|
|
25
|
-
new RequestFulfillment(),
|
|
26
|
-
);
|
|
27
|
-
jest.spyOn(RequestTracker, "Default", "get").mockReturnValue(
|
|
28
|
-
new RequestTracker(responseCache),
|
|
29
|
-
);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
describe("CSR: isServerSide false", () => {
|
|
33
|
-
beforeEach(() => {
|
|
34
|
-
jest.spyOn(Server, "isServerSide").mockReturnValue(false);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
describe("without hydrated data", () => {
|
|
38
|
-
beforeEach(() => {
|
|
39
|
-
/**
|
|
40
|
-
* Each of these test cases will not have cached data to be
|
|
41
|
-
* retrieved in the beginning.
|
|
42
|
-
*/
|
|
43
|
-
jest.spyOn(SsrCache.Default, "getEntry").mockReturnValueOnce(
|
|
44
|
-
null,
|
|
45
|
-
);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it("should make request for data on construction", async () => {
|
|
49
|
-
// Arrange
|
|
50
|
-
const response: any = Promise.resolve("data");
|
|
51
|
-
const fakeHandler = jest.fn().mockReturnValue(response);
|
|
52
|
-
const fakeChildrenFn = jest.fn(() => null);
|
|
53
|
-
|
|
54
|
-
// Act
|
|
55
|
-
render(
|
|
56
|
-
<Data handler={fakeHandler} requestId="ID">
|
|
57
|
-
{fakeChildrenFn}
|
|
58
|
-
</Data>,
|
|
59
|
-
);
|
|
60
|
-
await act(() => response);
|
|
61
|
-
|
|
62
|
-
// Assert
|
|
63
|
-
expect(fakeHandler).toHaveBeenCalledTimes(1);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it("should initially render children with loading", async () => {
|
|
67
|
-
// Arrange
|
|
68
|
-
const response: any = Promise.resolve("data");
|
|
69
|
-
const fakeHandler = jest.fn().mockReturnValue(response);
|
|
70
|
-
const fakeChildrenFn = jest.fn(() => null);
|
|
71
|
-
|
|
72
|
-
// Act
|
|
73
|
-
render(
|
|
74
|
-
<Data handler={fakeHandler} requestId="ID">
|
|
75
|
-
{fakeChildrenFn}
|
|
76
|
-
</Data>,
|
|
77
|
-
);
|
|
78
|
-
await act(() => response);
|
|
79
|
-
|
|
80
|
-
// Assert
|
|
81
|
-
expect(fakeChildrenFn).toHaveBeenCalledWith({
|
|
82
|
-
status: "loading",
|
|
83
|
-
});
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it("should share single request across all uses", () => {
|
|
87
|
-
// Arrange
|
|
88
|
-
const fakeHandler = jest.fn(
|
|
89
|
-
() => new Promise((resolve: any, reject: any) => {}),
|
|
90
|
-
) as jest.Mock<Promise<any>>;
|
|
91
|
-
const fakeChildrenFn = jest.fn(() => null);
|
|
92
|
-
|
|
93
|
-
// Act
|
|
94
|
-
render(
|
|
95
|
-
<View>
|
|
96
|
-
<Data handler={fakeHandler} requestId="ID">
|
|
97
|
-
{fakeChildrenFn}
|
|
98
|
-
</Data>
|
|
99
|
-
<Data handler={fakeHandler} requestId="ID">
|
|
100
|
-
{fakeChildrenFn}
|
|
101
|
-
</Data>
|
|
102
|
-
</View>,
|
|
103
|
-
);
|
|
104
|
-
|
|
105
|
-
// Assert
|
|
106
|
-
expect(fakeHandler).toHaveBeenCalledTimes(1);
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
it("should render with an error if the handler request rejects to an error", async () => {
|
|
110
|
-
// Arrange
|
|
111
|
-
const fulfillSpy = jest.spyOn(
|
|
112
|
-
RequestFulfillment.Default,
|
|
113
|
-
"fulfill",
|
|
114
|
-
);
|
|
115
|
-
const fakeHandler = () => Promise.reject(new Error("OH NOES!"));
|
|
116
|
-
const fakeChildrenFn = jest.fn(() => null);
|
|
117
|
-
|
|
118
|
-
// Act
|
|
119
|
-
render(
|
|
120
|
-
<Data handler={fakeHandler} requestId="ID">
|
|
121
|
-
{fakeChildrenFn}
|
|
122
|
-
</Data>,
|
|
123
|
-
);
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* We wait for the fulfillment to resolve.
|
|
127
|
-
*/
|
|
128
|
-
await act(() =>
|
|
129
|
-
fulfillSpy.mock.results[0].value.catch(() => {
|
|
130
|
-
/* do nothing */
|
|
131
|
-
}),
|
|
132
|
-
);
|
|
133
|
-
|
|
134
|
-
// Assert
|
|
135
|
-
expect(fakeChildrenFn).toHaveBeenNthCalledWith(2, {
|
|
136
|
-
status: "error",
|
|
137
|
-
error: expect.any(Error),
|
|
138
|
-
});
|
|
139
|
-
expect(fakeChildrenFn).toHaveBeenCalledTimes(2);
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
it("should render with data if the handler resolves with data", async () => {
|
|
143
|
-
// Arrange
|
|
144
|
-
const fulfillSpy = jest.spyOn(
|
|
145
|
-
RequestFulfillment.Default,
|
|
146
|
-
"fulfill",
|
|
147
|
-
);
|
|
148
|
-
|
|
149
|
-
const fakeHandler = () => Promise.resolve("YAY! DATA!");
|
|
150
|
-
const fakeChildrenFn = jest.fn(() => null);
|
|
151
|
-
|
|
152
|
-
// Act
|
|
153
|
-
render(
|
|
154
|
-
<Data handler={fakeHandler} requestId="ID">
|
|
155
|
-
{fakeChildrenFn}
|
|
156
|
-
</Data>,
|
|
157
|
-
);
|
|
158
|
-
/**
|
|
159
|
-
* We wait for the fulfillment to resolve.
|
|
160
|
-
*/
|
|
161
|
-
await act(() => fulfillSpy.mock.results[0].value);
|
|
162
|
-
|
|
163
|
-
// Assert
|
|
164
|
-
expect(fakeChildrenFn).toHaveBeenCalledTimes(2);
|
|
165
|
-
expect(fakeChildrenFn).toHaveBeenLastCalledWith({
|
|
166
|
-
status: "success",
|
|
167
|
-
data: "YAY! DATA!",
|
|
168
|
-
});
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
it("should render with aborted if the request rejects with an abort error", async () => {
|
|
172
|
-
// Arrange
|
|
173
|
-
const fulfillSpy = jest.spyOn(
|
|
174
|
-
RequestFulfillment.Default,
|
|
175
|
-
"fulfill",
|
|
176
|
-
);
|
|
177
|
-
|
|
178
|
-
const abortError = new Error("bang bang, abort!");
|
|
179
|
-
abortError.name = "AbortError";
|
|
180
|
-
const fakeHandler = () => Promise.reject(abortError);
|
|
181
|
-
const fakeChildrenFn = jest.fn(() => null);
|
|
182
|
-
|
|
183
|
-
// Act
|
|
184
|
-
render(
|
|
185
|
-
<Data handler={fakeHandler} requestId="ID">
|
|
186
|
-
{fakeChildrenFn}
|
|
187
|
-
</Data>,
|
|
188
|
-
);
|
|
189
|
-
/**
|
|
190
|
-
* We wait for the fulfillment to resolve.
|
|
191
|
-
*/
|
|
192
|
-
await act(() => fulfillSpy.mock.results[0].value);
|
|
193
|
-
|
|
194
|
-
// Assert
|
|
195
|
-
expect(fakeChildrenFn).toHaveBeenCalledTimes(2);
|
|
196
|
-
expect(fakeChildrenFn).toHaveBeenLastCalledWith({
|
|
197
|
-
status: "aborted",
|
|
198
|
-
});
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
it("should render with an error if the RequestFulfillment rejects with an error", async () => {
|
|
202
|
-
// Arrange
|
|
203
|
-
const fulfillSpy = jest
|
|
204
|
-
.spyOn(RequestFulfillment.Default, "fulfill")
|
|
205
|
-
.mockResolvedValue({
|
|
206
|
-
status: "error",
|
|
207
|
-
error: new Error("CATASTROPHE!"),
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
const fakeHandler = () => Promise.resolve("YAY!");
|
|
211
|
-
const fakeChildrenFn = jest.fn(() => null);
|
|
212
|
-
|
|
213
|
-
// Act
|
|
214
|
-
render(
|
|
215
|
-
<Data handler={fakeHandler} requestId="ID">
|
|
216
|
-
{fakeChildrenFn}
|
|
217
|
-
</Data>,
|
|
218
|
-
);
|
|
219
|
-
/**
|
|
220
|
-
* We wait for the fulfillment to reject.
|
|
221
|
-
*/
|
|
222
|
-
await act(() =>
|
|
223
|
-
fulfillSpy.mock.results[0].value.catch(() => {}),
|
|
224
|
-
);
|
|
225
|
-
|
|
226
|
-
// Assert
|
|
227
|
-
expect(fakeChildrenFn).toHaveBeenCalledTimes(2);
|
|
228
|
-
expect(fakeChildrenFn).toHaveBeenLastCalledWith({
|
|
229
|
-
status: "error",
|
|
230
|
-
error: expect.any(Error),
|
|
231
|
-
});
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
it("should start loading if the id changes and request not cached", async () => {
|
|
235
|
-
// Arrange
|
|
236
|
-
const fulfillSpy = jest.spyOn(
|
|
237
|
-
RequestFulfillment.Default,
|
|
238
|
-
"fulfill",
|
|
239
|
-
);
|
|
240
|
-
|
|
241
|
-
const fakeHandler = () => Promise.resolve("HELLO!");
|
|
242
|
-
const fakeChildrenFn = jest.fn(() => null);
|
|
243
|
-
const wrapper = render(
|
|
244
|
-
<Data handler={fakeHandler} requestId="ID">
|
|
245
|
-
{fakeChildrenFn}
|
|
246
|
-
</Data>,
|
|
247
|
-
);
|
|
248
|
-
// We want to make sure we render the data state so we can
|
|
249
|
-
// see our switch back to loading.
|
|
250
|
-
await act(() => fulfillSpy.mock.results[0].value);
|
|
251
|
-
fulfillSpy.mockClear();
|
|
252
|
-
fakeChildrenFn.mockClear();
|
|
253
|
-
|
|
254
|
-
// Act
|
|
255
|
-
wrapper.rerender(
|
|
256
|
-
<Data handler={fakeHandler} requestId="NEW_ID">
|
|
257
|
-
{fakeChildrenFn}
|
|
258
|
-
</Data>,
|
|
259
|
-
);
|
|
260
|
-
|
|
261
|
-
// Assert
|
|
262
|
-
expect(fakeChildrenFn).toHaveBeenCalledTimes(1);
|
|
263
|
-
expect(fakeChildrenFn).toHaveBeenLastCalledWith({
|
|
264
|
-
status: "loading",
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
// We have to do this or testing-library gets very upset.
|
|
268
|
-
await act(() => fulfillSpy.mock.results[0].value);
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
it("should ignore resolution of pending handler fulfillment when id changes", async () => {
|
|
272
|
-
// Arrange
|
|
273
|
-
const oldRequest: any = Promise.resolve("OLD DATA");
|
|
274
|
-
const oldHandler = jest
|
|
275
|
-
.fn()
|
|
276
|
-
.mockReturnValueOnce(oldRequest)
|
|
277
|
-
.mockReturnValue(
|
|
278
|
-
new Promise(() => {
|
|
279
|
-
/*let's have the new request remain pending*/
|
|
280
|
-
}),
|
|
281
|
-
);
|
|
282
|
-
|
|
283
|
-
// Act
|
|
284
|
-
const fakeChildrenFn = jest.fn(() => null);
|
|
285
|
-
const wrapper = render(
|
|
286
|
-
<Data handler={oldHandler} requestId="ID">
|
|
287
|
-
{fakeChildrenFn}
|
|
288
|
-
</Data>,
|
|
289
|
-
);
|
|
290
|
-
wrapper.rerender(
|
|
291
|
-
<Data handler={oldHandler} requestId="NEW_ID">
|
|
292
|
-
{fakeChildrenFn}
|
|
293
|
-
</Data>,
|
|
294
|
-
);
|
|
295
|
-
await act(() => oldRequest);
|
|
296
|
-
|
|
297
|
-
// Assert
|
|
298
|
-
expect(fakeChildrenFn).not.toHaveBeenCalledWith({
|
|
299
|
-
status: "success",
|
|
300
|
-
data: "OLD DATA",
|
|
301
|
-
});
|
|
302
|
-
});
|
|
303
|
-
|
|
304
|
-
it("should ignore rejection of pending handler fulfillment when id changes", async () => {
|
|
305
|
-
// Arrange
|
|
306
|
-
const oldRequest = Promise.reject(new Error("BOOM!"));
|
|
307
|
-
const oldHandler = jest
|
|
308
|
-
.fn()
|
|
309
|
-
.mockReturnValueOnce(oldRequest)
|
|
310
|
-
.mockReturnValue(
|
|
311
|
-
new Promise(() => {
|
|
312
|
-
/*let's have the new request remain pending*/
|
|
313
|
-
}),
|
|
314
|
-
);
|
|
315
|
-
|
|
316
|
-
// Act
|
|
317
|
-
const fakeChildrenFn = jest.fn(() => null);
|
|
318
|
-
const wrapper = render(
|
|
319
|
-
<Data handler={oldHandler} requestId="ID">
|
|
320
|
-
{fakeChildrenFn}
|
|
321
|
-
</Data>,
|
|
322
|
-
);
|
|
323
|
-
wrapper.rerender(
|
|
324
|
-
<Data handler={oldHandler} requestId="NEW_ID">
|
|
325
|
-
{fakeChildrenFn}
|
|
326
|
-
</Data>,
|
|
327
|
-
);
|
|
328
|
-
await act(() =>
|
|
329
|
-
oldRequest.catch(() => {
|
|
330
|
-
/*ignore*/
|
|
331
|
-
}),
|
|
332
|
-
);
|
|
333
|
-
|
|
334
|
-
// Assert
|
|
335
|
-
expect(fakeChildrenFn).not.toHaveBeenCalledWith({
|
|
336
|
-
status: "error",
|
|
337
|
-
error: "BOOM!",
|
|
338
|
-
});
|
|
339
|
-
});
|
|
340
|
-
|
|
341
|
-
it("should ignore catastrophic request fulfillment when id changes", async () => {
|
|
342
|
-
// Arrange
|
|
343
|
-
const catastrophe: any = Promise.resolve({
|
|
344
|
-
status: "error",
|
|
345
|
-
error: new Error("CATASTROPHE!"),
|
|
346
|
-
});
|
|
347
|
-
jest.spyOn(
|
|
348
|
-
RequestFulfillment.Default,
|
|
349
|
-
"fulfill",
|
|
350
|
-
).mockReturnValueOnce(catastrophe);
|
|
351
|
-
const oldHandler = jest.fn().mockResolvedValue("OLD DATA");
|
|
352
|
-
|
|
353
|
-
// Act
|
|
354
|
-
const fakeChildrenFn = jest.fn(() => null);
|
|
355
|
-
const wrapper = render(
|
|
356
|
-
<Data handler={oldHandler} requestId="ID">
|
|
357
|
-
{fakeChildrenFn}
|
|
358
|
-
</Data>,
|
|
359
|
-
);
|
|
360
|
-
wrapper.rerender(
|
|
361
|
-
<Data handler={oldHandler} requestId="NEW_ID">
|
|
362
|
-
{fakeChildrenFn}
|
|
363
|
-
</Data>,
|
|
364
|
-
);
|
|
365
|
-
await act(() =>
|
|
366
|
-
catastrophe.catch(() => {
|
|
367
|
-
/* ignore */
|
|
368
|
-
}),
|
|
369
|
-
);
|
|
370
|
-
|
|
371
|
-
// Assert
|
|
372
|
-
expect(fakeChildrenFn).not.toHaveBeenCalledWith({
|
|
373
|
-
status: "error",
|
|
374
|
-
error: expect.any(Error),
|
|
375
|
-
});
|
|
376
|
-
});
|
|
377
|
-
|
|
378
|
-
describe("with data interceptor", () => {
|
|
379
|
-
it("should request data from interceptor", async () => {
|
|
380
|
-
// Arrange
|
|
381
|
-
const fakeHandler = jest.fn().mockResolvedValue("data");
|
|
382
|
-
const fakeChildrenFn = jest.fn(() => null);
|
|
383
|
-
const interceptHandler = jest
|
|
384
|
-
.fn()
|
|
385
|
-
.mockResolvedValue("INTERCEPTED DATA");
|
|
386
|
-
|
|
387
|
-
// Act
|
|
388
|
-
render(
|
|
389
|
-
<InterceptRequests interceptor={interceptHandler}>
|
|
390
|
-
<Data handler={fakeHandler} requestId="ID">
|
|
391
|
-
{fakeChildrenFn}
|
|
392
|
-
</Data>
|
|
393
|
-
</InterceptRequests>,
|
|
394
|
-
);
|
|
395
|
-
await act(() => interceptHandler.mock.results[0].value);
|
|
396
|
-
|
|
397
|
-
// Assert
|
|
398
|
-
expect(interceptHandler).toHaveBeenCalledTimes(1);
|
|
399
|
-
expect(fakeHandler).not.toHaveBeenCalled();
|
|
400
|
-
});
|
|
401
|
-
|
|
402
|
-
it("should invoke handler method if interceptor method returns null", async () => {
|
|
403
|
-
// Arrange
|
|
404
|
-
const fakeHandler = jest.fn().mockResolvedValue("data");
|
|
405
|
-
const fakeChildrenFn = jest.fn(() => null);
|
|
406
|
-
const interceptHandler = jest.fn(() => null);
|
|
407
|
-
|
|
408
|
-
// Act
|
|
409
|
-
render(
|
|
410
|
-
<InterceptRequests interceptor={interceptHandler}>
|
|
411
|
-
<Data handler={fakeHandler} requestId="ID">
|
|
412
|
-
{fakeChildrenFn}
|
|
413
|
-
</Data>
|
|
414
|
-
</InterceptRequests>,
|
|
415
|
-
);
|
|
416
|
-
await act(() => fakeHandler.mock.results[0].value);
|
|
417
|
-
|
|
418
|
-
// Assert
|
|
419
|
-
expect(interceptHandler).toHaveBeenCalledTimes(1);
|
|
420
|
-
expect(fakeHandler).toHaveBeenCalledTimes(1);
|
|
421
|
-
});
|
|
422
|
-
});
|
|
423
|
-
|
|
424
|
-
it("should retain old data while reloading if retainResultOnChange is true", async () => {
|
|
425
|
-
// Arrange
|
|
426
|
-
const response1: any = Promise.resolve("data1");
|
|
427
|
-
const response2: any = Promise.resolve("data2");
|
|
428
|
-
const fakeHandler1 = () => response1;
|
|
429
|
-
const fakeHandler2 = () => response2;
|
|
430
|
-
const fakeChildrenFn = jest.fn(() => null);
|
|
431
|
-
|
|
432
|
-
// Act
|
|
433
|
-
const wrapper = render(
|
|
434
|
-
<Data
|
|
435
|
-
handler={fakeHandler1}
|
|
436
|
-
requestId="ID1"
|
|
437
|
-
retainResultOnChange={true}
|
|
438
|
-
>
|
|
439
|
-
{fakeChildrenFn}
|
|
440
|
-
</Data>,
|
|
441
|
-
);
|
|
442
|
-
fakeChildrenFn.mockClear();
|
|
443
|
-
await act(() => response1);
|
|
444
|
-
wrapper.rerender(
|
|
445
|
-
<Data
|
|
446
|
-
handler={fakeHandler2}
|
|
447
|
-
requestId="ID2"
|
|
448
|
-
retainResultOnChange={true}
|
|
449
|
-
>
|
|
450
|
-
{fakeChildrenFn}
|
|
451
|
-
</Data>,
|
|
452
|
-
);
|
|
453
|
-
await act(() => response2);
|
|
454
|
-
|
|
455
|
-
// Assert
|
|
456
|
-
expect(fakeChildrenFn).not.toHaveBeenCalledWith({
|
|
457
|
-
status: "loading",
|
|
458
|
-
});
|
|
459
|
-
expect(fakeChildrenFn).toHaveBeenCalledWith({
|
|
460
|
-
status: "success",
|
|
461
|
-
data: "data1",
|
|
462
|
-
});
|
|
463
|
-
expect(fakeChildrenFn).toHaveBeenLastCalledWith({
|
|
464
|
-
status: "success",
|
|
465
|
-
data: "data2",
|
|
466
|
-
});
|
|
467
|
-
});
|
|
468
|
-
});
|
|
469
|
-
|
|
470
|
-
describe("with hydrated data", () => {
|
|
471
|
-
beforeEach(() => {
|
|
472
|
-
/**
|
|
473
|
-
* Each of these test cases will start out with some cached data
|
|
474
|
-
* retrieved.
|
|
475
|
-
*/
|
|
476
|
-
jest.spyOn(
|
|
477
|
-
SsrCache.Default,
|
|
478
|
-
"getEntry",
|
|
479
|
-
// Fake once because that's how the cache would work,
|
|
480
|
-
// deleting the hydrated value as soon as it was used.
|
|
481
|
-
).mockReturnValueOnce({
|
|
482
|
-
data: "YAY! DATA!",
|
|
483
|
-
});
|
|
484
|
-
});
|
|
485
|
-
|
|
486
|
-
it("should render first time with the cached data", () => {
|
|
487
|
-
// Arrange
|
|
488
|
-
const fakeHandler = () => Promise.resolve("data");
|
|
489
|
-
const fakeChildrenFn = jest.fn(() => null);
|
|
490
|
-
|
|
491
|
-
// Act
|
|
492
|
-
render(
|
|
493
|
-
<Data handler={fakeHandler} requestId="ID">
|
|
494
|
-
{fakeChildrenFn}
|
|
495
|
-
</Data>,
|
|
496
|
-
);
|
|
497
|
-
|
|
498
|
-
// Assert
|
|
499
|
-
expect(fakeChildrenFn).toHaveBeenCalledWith({
|
|
500
|
-
status: "success",
|
|
501
|
-
data: "YAY! DATA!",
|
|
502
|
-
});
|
|
503
|
-
});
|
|
504
|
-
|
|
505
|
-
it("should not request data when clientBehavior is WhenClientSide.ExecuteWhenNoSuccessResult and cache has a valid success result", () => {
|
|
506
|
-
// Arrange
|
|
507
|
-
const fakeHandler = jest.fn().mockResolvedValue("data");
|
|
508
|
-
const fakeChildrenFn = jest.fn(() => null);
|
|
509
|
-
|
|
510
|
-
// Act
|
|
511
|
-
render(
|
|
512
|
-
<Data
|
|
513
|
-
handler={fakeHandler}
|
|
514
|
-
requestId="ID"
|
|
515
|
-
clientBehavior={
|
|
516
|
-
WhenClientSide.ExecuteWhenNoSuccessResult
|
|
517
|
-
}
|
|
518
|
-
>
|
|
519
|
-
{fakeChildrenFn}
|
|
520
|
-
</Data>,
|
|
521
|
-
);
|
|
522
|
-
|
|
523
|
-
// Assert
|
|
524
|
-
expect(fakeHandler).not.toHaveBeenCalled();
|
|
525
|
-
});
|
|
526
|
-
|
|
527
|
-
it("should request data if cached data value is valid but clientBehavior is WhenClientSide.AlwaysExecute is true", async () => {
|
|
528
|
-
// Arrange
|
|
529
|
-
const fakeHandler = jest.fn().mockResolvedValue("data");
|
|
530
|
-
const fakeChildrenFn = jest.fn(() => null);
|
|
531
|
-
|
|
532
|
-
// Act
|
|
533
|
-
render(
|
|
534
|
-
<Data
|
|
535
|
-
handler={fakeHandler}
|
|
536
|
-
requestId="ID"
|
|
537
|
-
clientBehavior={WhenClientSide.AlwaysExecute}
|
|
538
|
-
>
|
|
539
|
-
{fakeChildrenFn}
|
|
540
|
-
</Data>,
|
|
541
|
-
);
|
|
542
|
-
await act(() => fakeHandler.mock.results[0].value);
|
|
543
|
-
|
|
544
|
-
// Assert
|
|
545
|
-
expect(fakeHandler).toHaveBeenCalledTimes(1);
|
|
546
|
-
});
|
|
547
|
-
});
|
|
548
|
-
|
|
549
|
-
describe("with cached error", () => {
|
|
550
|
-
beforeEach(() => {
|
|
551
|
-
/**
|
|
552
|
-
* Each of these test cases will start out with a cached error.
|
|
553
|
-
*/
|
|
554
|
-
jest.spyOn(
|
|
555
|
-
SsrCache.Default,
|
|
556
|
-
"getEntry",
|
|
557
|
-
// Fake once because that's how the cache would work,
|
|
558
|
-
// deleting the hydrated value as soon as it was used.
|
|
559
|
-
).mockReturnValueOnce({
|
|
560
|
-
error: "BOO! ERROR!",
|
|
561
|
-
});
|
|
562
|
-
});
|
|
563
|
-
|
|
564
|
-
it("should always request data if there's a cached error", async () => {
|
|
565
|
-
// Arrange
|
|
566
|
-
const fakeHandler = jest.fn().mockResolvedValue("data");
|
|
567
|
-
const fakeChildrenFn = jest.fn(() => null);
|
|
568
|
-
|
|
569
|
-
// Act
|
|
570
|
-
render(
|
|
571
|
-
<Data handler={fakeHandler} requestId="ID">
|
|
572
|
-
{fakeChildrenFn}
|
|
573
|
-
</Data>,
|
|
574
|
-
);
|
|
575
|
-
// Have to await the promise in an act to keep TL/R happy.
|
|
576
|
-
await act(() => fakeHandler.mock.results[0].value);
|
|
577
|
-
|
|
578
|
-
// Assert
|
|
579
|
-
expect(fakeHandler).toHaveBeenCalledTimes(1);
|
|
580
|
-
});
|
|
581
|
-
});
|
|
582
|
-
});
|
|
583
|
-
|
|
584
|
-
describe("SSR: isServerSide true", () => {
|
|
585
|
-
beforeEach(() => {
|
|
586
|
-
jest.spyOn(Server, "isServerSide").mockReturnValue(true);
|
|
587
|
-
});
|
|
588
|
-
|
|
589
|
-
describe("without cached data", () => {
|
|
590
|
-
beforeEach(() => {
|
|
591
|
-
/**
|
|
592
|
-
* Each of these test cases will never have cached data
|
|
593
|
-
* retrieved.
|
|
594
|
-
*/
|
|
595
|
-
jest.spyOn(SsrCache.Default, "getEntry").mockReturnValue(null);
|
|
596
|
-
});
|
|
597
|
-
|
|
598
|
-
it("should not request data", () => {
|
|
599
|
-
// Arrange
|
|
600
|
-
const fakeHandler = jest.fn().mockResolvedValue("data");
|
|
601
|
-
const fakeChildrenFn = jest.fn(() => null);
|
|
602
|
-
|
|
603
|
-
// Act
|
|
604
|
-
ReactDOMServer.renderToString(
|
|
605
|
-
<Data handler={fakeHandler} requestId="ID">
|
|
606
|
-
{fakeChildrenFn}
|
|
607
|
-
</Data>,
|
|
608
|
-
);
|
|
609
|
-
|
|
610
|
-
// Assert
|
|
611
|
-
expect(fakeHandler).not.toHaveBeenCalled();
|
|
612
|
-
});
|
|
613
|
-
|
|
614
|
-
it("should render children with loading", () => {
|
|
615
|
-
// Arrange
|
|
616
|
-
const fakeHandler = jest.fn().mockResolvedValue("data");
|
|
617
|
-
const fakeChildrenFn = jest.fn(() => null);
|
|
618
|
-
|
|
619
|
-
// Act
|
|
620
|
-
ReactDOMServer.renderToString(
|
|
621
|
-
<Data handler={fakeHandler} requestId="ID">
|
|
622
|
-
{fakeChildrenFn}
|
|
623
|
-
</Data>,
|
|
624
|
-
);
|
|
625
|
-
|
|
626
|
-
// Assert
|
|
627
|
-
expect(fakeChildrenFn).toHaveBeenCalledWith({
|
|
628
|
-
status: "loading",
|
|
629
|
-
});
|
|
630
|
-
});
|
|
631
|
-
|
|
632
|
-
it("should invoke the tracking call", () => {
|
|
633
|
-
// Arrange
|
|
634
|
-
const trackSpy = jest.spyOn(
|
|
635
|
-
RequestTracker.Default,
|
|
636
|
-
"trackDataRequest",
|
|
637
|
-
);
|
|
638
|
-
const fakeHandler = jest.fn().mockResolvedValue("data");
|
|
639
|
-
const fakeChildrenFn = jest.fn(() => null);
|
|
640
|
-
|
|
641
|
-
// Act
|
|
642
|
-
ReactDOMServer.renderToString(
|
|
643
|
-
<TrackData>
|
|
644
|
-
<Data handler={fakeHandler} requestId="ID">
|
|
645
|
-
{fakeChildrenFn}
|
|
646
|
-
</Data>
|
|
647
|
-
</TrackData>,
|
|
648
|
-
);
|
|
649
|
-
|
|
650
|
-
// Assert
|
|
651
|
-
expect(trackSpy).toHaveBeenCalledWith(
|
|
652
|
-
"ID",
|
|
653
|
-
expect.any(Function),
|
|
654
|
-
true,
|
|
655
|
-
);
|
|
656
|
-
});
|
|
657
|
-
|
|
658
|
-
describe("with data interceptor", () => {
|
|
659
|
-
it("should not request data from the interceptor", () => {
|
|
660
|
-
// Arrange
|
|
661
|
-
const fakeHandler = jest.fn().mockResolvedValue("data");
|
|
662
|
-
const fakeChildrenFn = jest.fn(() => null);
|
|
663
|
-
const interceptedHandler = jest.fn(() =>
|
|
664
|
-
Promise.resolve("DATA!"),
|
|
665
|
-
);
|
|
666
|
-
|
|
667
|
-
// Act
|
|
668
|
-
ReactDOMServer.renderToString(
|
|
669
|
-
<InterceptRequests interceptor={interceptedHandler}>
|
|
670
|
-
<Data handler={fakeHandler} requestId="ID">
|
|
671
|
-
{fakeChildrenFn}
|
|
672
|
-
</Data>
|
|
673
|
-
</InterceptRequests>,
|
|
674
|
-
);
|
|
675
|
-
|
|
676
|
-
// Assert
|
|
677
|
-
expect(fakeHandler).not.toHaveBeenCalled();
|
|
678
|
-
expect(interceptedHandler).not.toHaveBeenCalled();
|
|
679
|
-
});
|
|
680
|
-
|
|
681
|
-
it("should invoke the tracking call", () => {
|
|
682
|
-
// Arrange
|
|
683
|
-
const trackSpy = jest.spyOn(
|
|
684
|
-
RequestTracker.Default,
|
|
685
|
-
"trackDataRequest",
|
|
686
|
-
);
|
|
687
|
-
const fakeHandler = jest.fn().mockResolvedValue("data");
|
|
688
|
-
const fakeChildrenFn = jest.fn(() => null);
|
|
689
|
-
const interceptedHandler = jest
|
|
690
|
-
.fn()
|
|
691
|
-
.mockResolvedValue("INTERCEPTED");
|
|
692
|
-
|
|
693
|
-
// Act
|
|
694
|
-
ReactDOMServer.renderToString(
|
|
695
|
-
<TrackData>
|
|
696
|
-
<InterceptRequests interceptor={interceptedHandler}>
|
|
697
|
-
<Data handler={fakeHandler} requestId="ID">
|
|
698
|
-
{fakeChildrenFn}
|
|
699
|
-
</Data>
|
|
700
|
-
</InterceptRequests>
|
|
701
|
-
</TrackData>,
|
|
702
|
-
);
|
|
703
|
-
|
|
704
|
-
// Assert
|
|
705
|
-
expect(trackSpy).toHaveBeenCalledWith(
|
|
706
|
-
"ID",
|
|
707
|
-
expect.any(Function),
|
|
708
|
-
true,
|
|
709
|
-
);
|
|
710
|
-
});
|
|
711
|
-
});
|
|
712
|
-
});
|
|
713
|
-
|
|
714
|
-
describe("with cached data", () => {
|
|
715
|
-
beforeEach(() => {
|
|
716
|
-
/**
|
|
717
|
-
* Each of these test cases will start out with some cached data
|
|
718
|
-
* retrieved.
|
|
719
|
-
*/
|
|
720
|
-
jest.spyOn(SsrCache.Default, "getEntry").mockReturnValue({
|
|
721
|
-
data: "YAY! DATA!",
|
|
722
|
-
});
|
|
723
|
-
});
|
|
724
|
-
|
|
725
|
-
it("should not request data", () => {
|
|
726
|
-
// Arrange
|
|
727
|
-
const fakeHandler = jest.fn().mockResolvedValue("data");
|
|
728
|
-
const fakeChildrenFn = jest.fn(() => null);
|
|
729
|
-
|
|
730
|
-
// Act
|
|
731
|
-
ReactDOMServer.renderToString(
|
|
732
|
-
<Data handler={fakeHandler} requestId="ID">
|
|
733
|
-
{fakeChildrenFn}
|
|
734
|
-
</Data>,
|
|
735
|
-
);
|
|
736
|
-
|
|
737
|
-
// Assert
|
|
738
|
-
expect(fakeHandler).not.toHaveBeenCalled();
|
|
739
|
-
});
|
|
740
|
-
|
|
741
|
-
it("should render children with data", () => {
|
|
742
|
-
// Arrange
|
|
743
|
-
const fakeHandler = jest.fn().mockResolvedValue("data");
|
|
744
|
-
const fakeChildrenFn = jest.fn(() => null);
|
|
745
|
-
|
|
746
|
-
// Act
|
|
747
|
-
ReactDOMServer.renderToString(
|
|
748
|
-
<Data handler={fakeHandler} requestId="ID">
|
|
749
|
-
{fakeChildrenFn}
|
|
750
|
-
</Data>,
|
|
751
|
-
);
|
|
752
|
-
|
|
753
|
-
// Assert
|
|
754
|
-
expect(fakeChildrenFn).toHaveBeenCalledWith({
|
|
755
|
-
status: "success",
|
|
756
|
-
data: "YAY! DATA!",
|
|
757
|
-
});
|
|
758
|
-
});
|
|
759
|
-
|
|
760
|
-
it("should render children with error", () => {
|
|
761
|
-
// Arrange
|
|
762
|
-
jest.spyOn(SsrCache.Default, "getEntry").mockReturnValue({
|
|
763
|
-
error: "OH NO! IT GO BOOM",
|
|
764
|
-
});
|
|
765
|
-
const fakeHandler = jest.fn().mockResolvedValue("data");
|
|
766
|
-
const fakeChildrenFn = jest.fn(() => null);
|
|
767
|
-
|
|
768
|
-
// Act
|
|
769
|
-
ReactDOMServer.renderToString(
|
|
770
|
-
<Data handler={fakeHandler} requestId="ID">
|
|
771
|
-
{fakeChildrenFn}
|
|
772
|
-
</Data>,
|
|
773
|
-
);
|
|
774
|
-
|
|
775
|
-
// Assert
|
|
776
|
-
expect(fakeChildrenFn).toHaveBeenCalledWith({
|
|
777
|
-
status: "error",
|
|
778
|
-
error: expect.any(Error),
|
|
779
|
-
});
|
|
780
|
-
});
|
|
781
|
-
|
|
782
|
-
it("should not invoke the tracking call", () => {
|
|
783
|
-
// Arrange
|
|
784
|
-
const trackSpy = jest.spyOn(
|
|
785
|
-
RequestTracker.Default,
|
|
786
|
-
"trackDataRequest",
|
|
787
|
-
);
|
|
788
|
-
const fakeHandler = jest.fn().mockResolvedValue("data");
|
|
789
|
-
const fakeChildrenFn = jest.fn(() => null);
|
|
790
|
-
|
|
791
|
-
// Act
|
|
792
|
-
ReactDOMServer.renderToString(
|
|
793
|
-
<TrackData>
|
|
794
|
-
<Data handler={fakeHandler} requestId="ID">
|
|
795
|
-
{fakeChildrenFn}
|
|
796
|
-
</Data>
|
|
797
|
-
</TrackData>,
|
|
798
|
-
);
|
|
799
|
-
|
|
800
|
-
// Assert
|
|
801
|
-
expect(trackSpy).not.toHaveBeenCalledWith(
|
|
802
|
-
fakeHandler,
|
|
803
|
-
"options",
|
|
804
|
-
);
|
|
805
|
-
});
|
|
806
|
-
|
|
807
|
-
describe("with data interceptor", () => {
|
|
808
|
-
it("should not request data from interceptor", () => {
|
|
809
|
-
// Arrange
|
|
810
|
-
const fakeHandler = jest.fn().mockResolvedValue("data");
|
|
811
|
-
const fakeChildrenFn = jest.fn(() => null);
|
|
812
|
-
const interceptHandler = jest
|
|
813
|
-
.fn()
|
|
814
|
-
.mockResolvedValue("INTERCEPTED");
|
|
815
|
-
|
|
816
|
-
// Act
|
|
817
|
-
ReactDOMServer.renderToString(
|
|
818
|
-
<InterceptRequests interceptor={interceptHandler}>
|
|
819
|
-
<Data handler={fakeHandler} requestId="ID">
|
|
820
|
-
{fakeChildrenFn}
|
|
821
|
-
</Data>
|
|
822
|
-
</InterceptRequests>,
|
|
823
|
-
);
|
|
824
|
-
|
|
825
|
-
// Assert
|
|
826
|
-
expect(fakeHandler).not.toHaveBeenCalled();
|
|
827
|
-
expect(interceptHandler).not.toHaveBeenCalled();
|
|
828
|
-
});
|
|
829
|
-
});
|
|
830
|
-
});
|
|
831
|
-
});
|
|
832
|
-
});
|