@khanacademy/wonder-blocks-data 13.0.11 → 14.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.
- package/CHANGELOG.md +19 -0
- package/package.json +5 -5
- 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,708 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import {
|
|
3
|
-
renderHook as clientRenderHook,
|
|
4
|
-
act,
|
|
5
|
-
} from "@testing-library/react-hooks";
|
|
6
|
-
import {renderHookStatic} from "@khanacademy/wonder-blocks-testing-core";
|
|
7
|
-
|
|
8
|
-
import {Server} from "@khanacademy/wonder-blocks-core";
|
|
9
|
-
import {Status} from "../../util/status";
|
|
10
|
-
|
|
11
|
-
import {RequestFulfillment} from "../../util/request-fulfillment";
|
|
12
|
-
import * as UseRequestInterception from "../use-request-interception";
|
|
13
|
-
import * as UseServerEffect from "../use-server-effect";
|
|
14
|
-
import * as UseSharedCache from "../use-shared-cache";
|
|
15
|
-
|
|
16
|
-
import {useHydratableEffect, WhenClientSide} from "../use-hydratable-effect";
|
|
17
|
-
|
|
18
|
-
jest.mock("../use-request-interception");
|
|
19
|
-
jest.mock("../use-server-effect");
|
|
20
|
-
jest.mock("../use-shared-cache");
|
|
21
|
-
|
|
22
|
-
describe("#useHydratableEffect", () => {
|
|
23
|
-
beforeEach(() => {
|
|
24
|
-
jest.resetAllMocks();
|
|
25
|
-
|
|
26
|
-
// Clear out inflight requests between tests.
|
|
27
|
-
RequestFulfillment.Default.abortAll();
|
|
28
|
-
|
|
29
|
-
// Simple implementation of request interception that just returns
|
|
30
|
-
// the handler.
|
|
31
|
-
jest.spyOn(
|
|
32
|
-
UseRequestInterception,
|
|
33
|
-
"useRequestInterception",
|
|
34
|
-
).mockImplementation((_: any, handler: any) => handler);
|
|
35
|
-
|
|
36
|
-
// We need the cache to work a little so that we get our result.
|
|
37
|
-
const cache: Record<string, any> = {};
|
|
38
|
-
jest.spyOn(UseSharedCache, "useSharedCache").mockImplementation(
|
|
39
|
-
(id: any, _: any, defaultValue: any) => {
|
|
40
|
-
const setCache = React.useCallback(
|
|
41
|
-
(v: any) => (cache[id] = v),
|
|
42
|
-
[id],
|
|
43
|
-
);
|
|
44
|
-
const currentValue =
|
|
45
|
-
cache[id] ??
|
|
46
|
-
(typeof defaultValue === "function"
|
|
47
|
-
? defaultValue()
|
|
48
|
-
: defaultValue);
|
|
49
|
-
cache[id] = currentValue;
|
|
50
|
-
return [currentValue, setCache];
|
|
51
|
-
},
|
|
52
|
-
);
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
describe("when server-side", () => {
|
|
56
|
-
beforeEach(() => {
|
|
57
|
-
jest.spyOn(Server, "isServerSide").mockReturnValue(true);
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it("should call useRequestInterception", () => {
|
|
61
|
-
// Arrange
|
|
62
|
-
const useRequestInterceptSpy = jest
|
|
63
|
-
.spyOn(UseRequestInterception, "useRequestInterception")
|
|
64
|
-
.mockReturnValue(jest.fn());
|
|
65
|
-
const fakeHandler = jest.fn();
|
|
66
|
-
|
|
67
|
-
// Act
|
|
68
|
-
renderHookStatic(() => useHydratableEffect("ID", fakeHandler));
|
|
69
|
-
|
|
70
|
-
// Assert
|
|
71
|
-
expect(useRequestInterceptSpy).toHaveBeenCalledWith(
|
|
72
|
-
"ID",
|
|
73
|
-
fakeHandler,
|
|
74
|
-
);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it.each`
|
|
78
|
-
clientBehavior | hydrate
|
|
79
|
-
${WhenClientSide.DoNotHydrate} | ${false}
|
|
80
|
-
${WhenClientSide.AlwaysExecute} | ${true}
|
|
81
|
-
${WhenClientSide.ExecuteWhenNoResult} | ${true}
|
|
82
|
-
${WhenClientSide.ExecuteWhenNoSuccessResult} | ${true}
|
|
83
|
-
${undefined /*default*/} | ${true}
|
|
84
|
-
`(
|
|
85
|
-
"should call useServerEffect with the handler and hydrate=$hydrate for $clientBehavior",
|
|
86
|
-
({hydrate, clientBehavior}: any) => {
|
|
87
|
-
// Arrange
|
|
88
|
-
const useServerEffectSpy = jest
|
|
89
|
-
.spyOn(UseServerEffect, "useServerEffect")
|
|
90
|
-
.mockReturnValue(null);
|
|
91
|
-
const fakeHandler = jest.fn();
|
|
92
|
-
|
|
93
|
-
// Act
|
|
94
|
-
renderHookStatic(() =>
|
|
95
|
-
useHydratableEffect("ID", fakeHandler, {
|
|
96
|
-
clientBehavior,
|
|
97
|
-
}),
|
|
98
|
-
);
|
|
99
|
-
|
|
100
|
-
// Assert
|
|
101
|
-
expect(useServerEffectSpy).toHaveBeenCalledWith(
|
|
102
|
-
"ID",
|
|
103
|
-
fakeHandler,
|
|
104
|
-
{hydrate, skip: false},
|
|
105
|
-
);
|
|
106
|
-
},
|
|
107
|
-
);
|
|
108
|
-
|
|
109
|
-
it.each`
|
|
110
|
-
scope | expectedScope
|
|
111
|
-
${undefined} | ${"useHydratableEffect"}
|
|
112
|
-
${"foo"} | ${"foo"}
|
|
113
|
-
`(
|
|
114
|
-
"should call useSharedCache with id, scope=$scope, and a function to set the default",
|
|
115
|
-
({scope, expectedScope}: any) => {
|
|
116
|
-
const fakeHandler = jest.fn();
|
|
117
|
-
jest.spyOn(UseServerEffect, "useServerEffect").mockReturnValue(
|
|
118
|
-
Status.success({thisIs: "some data"}),
|
|
119
|
-
);
|
|
120
|
-
const useSharedCacheSpy = jest.spyOn(
|
|
121
|
-
UseSharedCache,
|
|
122
|
-
"useSharedCache",
|
|
123
|
-
);
|
|
124
|
-
|
|
125
|
-
// Act
|
|
126
|
-
renderHookStatic(() =>
|
|
127
|
-
useHydratableEffect("ID", fakeHandler, {scope}),
|
|
128
|
-
);
|
|
129
|
-
|
|
130
|
-
// Assert
|
|
131
|
-
expect(useSharedCacheSpy).toHaveBeenCalledWith(
|
|
132
|
-
"ID",
|
|
133
|
-
expectedScope,
|
|
134
|
-
expect.any(Function),
|
|
135
|
-
);
|
|
136
|
-
},
|
|
137
|
-
);
|
|
138
|
-
|
|
139
|
-
it("should not request data", () => {
|
|
140
|
-
// Arrange
|
|
141
|
-
const fakeHandler = jest.fn().mockResolvedValue("data");
|
|
142
|
-
|
|
143
|
-
// Act
|
|
144
|
-
renderHookStatic(() => useHydratableEffect("ID", fakeHandler));
|
|
145
|
-
|
|
146
|
-
// Assert
|
|
147
|
-
expect(fakeHandler).not.toHaveBeenCalled();
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
describe("without server result", () => {
|
|
151
|
-
it("should return a loading result", () => {
|
|
152
|
-
// Arrange
|
|
153
|
-
const fakeHandler = jest.fn();
|
|
154
|
-
|
|
155
|
-
// Act
|
|
156
|
-
const {
|
|
157
|
-
result: {current: result},
|
|
158
|
-
} = renderHookStatic(() =>
|
|
159
|
-
useHydratableEffect("ID", fakeHandler),
|
|
160
|
-
);
|
|
161
|
-
|
|
162
|
-
// Assert
|
|
163
|
-
expect(result).toEqual(Status.loading());
|
|
164
|
-
});
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
describe("with server result", () => {
|
|
168
|
-
it("should return the result", () => {
|
|
169
|
-
// Arrange
|
|
170
|
-
const fakeHandler = jest.fn();
|
|
171
|
-
const serverResult = Status.success("data");
|
|
172
|
-
jest.spyOn(UseServerEffect, "useServerEffect").mockReturnValue(
|
|
173
|
-
serverResult,
|
|
174
|
-
);
|
|
175
|
-
|
|
176
|
-
// Act
|
|
177
|
-
const {
|
|
178
|
-
result: {current: result},
|
|
179
|
-
} = renderHookStatic(() =>
|
|
180
|
-
useHydratableEffect("ID", fakeHandler),
|
|
181
|
-
);
|
|
182
|
-
|
|
183
|
-
// Assert
|
|
184
|
-
expect(result).toEqual(serverResult);
|
|
185
|
-
});
|
|
186
|
-
});
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
describe("when client-side", () => {
|
|
190
|
-
beforeEach(() => {
|
|
191
|
-
jest.spyOn(Server, "isServerSide").mockReturnValue(false);
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
it.each`
|
|
195
|
-
clientBehavior | hydrate
|
|
196
|
-
${WhenClientSide.DoNotHydrate} | ${false}
|
|
197
|
-
${WhenClientSide.AlwaysExecute} | ${true}
|
|
198
|
-
${WhenClientSide.ExecuteWhenNoResult} | ${true}
|
|
199
|
-
${WhenClientSide.ExecuteWhenNoSuccessResult} | ${true}
|
|
200
|
-
${undefined /*default*/} | ${true}
|
|
201
|
-
`(
|
|
202
|
-
"should call useServerEffect with the handler and hydrate=$hydrate for $clientBehavior",
|
|
203
|
-
({hydrate, clientBehavior}: any) => {
|
|
204
|
-
// Arrange
|
|
205
|
-
const useServerEffectSpy = jest
|
|
206
|
-
.spyOn(UseServerEffect, "useServerEffect")
|
|
207
|
-
.mockReturnValue(null);
|
|
208
|
-
const fakeHandler = jest.fn();
|
|
209
|
-
|
|
210
|
-
// Act
|
|
211
|
-
clientRenderHook(() =>
|
|
212
|
-
useHydratableEffect("ID", fakeHandler, {
|
|
213
|
-
clientBehavior,
|
|
214
|
-
}),
|
|
215
|
-
);
|
|
216
|
-
|
|
217
|
-
// Assert
|
|
218
|
-
expect(useServerEffectSpy).toHaveBeenCalledWith(
|
|
219
|
-
"ID",
|
|
220
|
-
fakeHandler,
|
|
221
|
-
{hydrate, skip: false},
|
|
222
|
-
);
|
|
223
|
-
},
|
|
224
|
-
);
|
|
225
|
-
|
|
226
|
-
it("should fulfill request when there is no server value to hydrate", () => {
|
|
227
|
-
// Arrange
|
|
228
|
-
const fakeHandler = jest.fn();
|
|
229
|
-
jest.spyOn(UseServerEffect, "useServerEffect").mockReturnValue(
|
|
230
|
-
null,
|
|
231
|
-
);
|
|
232
|
-
|
|
233
|
-
// Act
|
|
234
|
-
clientRenderHook(() => useHydratableEffect("ID", fakeHandler));
|
|
235
|
-
|
|
236
|
-
// Assert
|
|
237
|
-
expect(fakeHandler).toHaveBeenCalled();
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
it("should share inflight requests for the same requestId", () => {
|
|
241
|
-
// Arrange
|
|
242
|
-
const pending = new Promise((resolve: any, reject: any) => {
|
|
243
|
-
/*pending*/
|
|
244
|
-
});
|
|
245
|
-
const fakeHandler = jest.fn().mockReturnValue(pending);
|
|
246
|
-
|
|
247
|
-
// Act
|
|
248
|
-
clientRenderHook(() => useHydratableEffect("ID", fakeHandler));
|
|
249
|
-
clientRenderHook(() => useHydratableEffect("ID", fakeHandler));
|
|
250
|
-
|
|
251
|
-
// Assert
|
|
252
|
-
expect(fakeHandler).toHaveBeenCalledTimes(1);
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
it.each`
|
|
256
|
-
serverResult
|
|
257
|
-
${null}
|
|
258
|
-
${Status.error(new Error("some error"))}
|
|
259
|
-
`(
|
|
260
|
-
"should fulfill request when server value is $serverResult and clientBehavior is ExecuteWhenNoSuccessResult",
|
|
261
|
-
({serverResult}: any) => {
|
|
262
|
-
// Arrange
|
|
263
|
-
const fakeHandler = jest.fn();
|
|
264
|
-
jest.spyOn(UseServerEffect, "useServerEffect").mockReturnValue(
|
|
265
|
-
serverResult,
|
|
266
|
-
);
|
|
267
|
-
|
|
268
|
-
// Act
|
|
269
|
-
clientRenderHook(() =>
|
|
270
|
-
useHydratableEffect("ID", fakeHandler, {
|
|
271
|
-
clientBehavior:
|
|
272
|
-
WhenClientSide.ExecuteWhenNoSuccessResult,
|
|
273
|
-
}),
|
|
274
|
-
);
|
|
275
|
-
|
|
276
|
-
// Assert
|
|
277
|
-
expect(fakeHandler).toHaveBeenCalled();
|
|
278
|
-
},
|
|
279
|
-
);
|
|
280
|
-
|
|
281
|
-
it.each`
|
|
282
|
-
serverResult
|
|
283
|
-
${null}
|
|
284
|
-
${Status.error(new Error("some error"))}
|
|
285
|
-
${Status.success("data")}
|
|
286
|
-
`(
|
|
287
|
-
"should fulfill request when server value is $serveResult and clientBehavior is AlwaysExecute",
|
|
288
|
-
({serverResult}: any) => {
|
|
289
|
-
// Arrange
|
|
290
|
-
const fakeHandler = jest.fn();
|
|
291
|
-
jest.spyOn(UseServerEffect, "useServerEffect").mockReturnValue(
|
|
292
|
-
serverResult,
|
|
293
|
-
);
|
|
294
|
-
|
|
295
|
-
// Act
|
|
296
|
-
clientRenderHook(() =>
|
|
297
|
-
useHydratableEffect("ID", fakeHandler, {
|
|
298
|
-
clientBehavior: WhenClientSide.AlwaysExecute,
|
|
299
|
-
}),
|
|
300
|
-
);
|
|
301
|
-
|
|
302
|
-
// Assert
|
|
303
|
-
expect(fakeHandler).toHaveBeenCalled();
|
|
304
|
-
},
|
|
305
|
-
);
|
|
306
|
-
|
|
307
|
-
it("should not fulfill request when server value is success and clientBehavior is ExecuteWhenNoSuccessResult", () => {
|
|
308
|
-
// Arrange
|
|
309
|
-
const fakeHandler = jest.fn();
|
|
310
|
-
jest.spyOn(UseServerEffect, "useServerEffect").mockReturnValue(
|
|
311
|
-
Status.success("data"),
|
|
312
|
-
);
|
|
313
|
-
|
|
314
|
-
// Act
|
|
315
|
-
clientRenderHook(() =>
|
|
316
|
-
useHydratableEffect("ID", fakeHandler, {
|
|
317
|
-
clientBehavior: WhenClientSide.ExecuteWhenNoSuccessResult,
|
|
318
|
-
}),
|
|
319
|
-
);
|
|
320
|
-
|
|
321
|
-
// Assert
|
|
322
|
-
expect(fakeHandler).not.toHaveBeenCalled();
|
|
323
|
-
});
|
|
324
|
-
|
|
325
|
-
it.each`
|
|
326
|
-
serverResult
|
|
327
|
-
${Status.error(new Error("some error"))}
|
|
328
|
-
${Status.success("data")}
|
|
329
|
-
`(
|
|
330
|
-
"should not fulfill request when server value is $serverResult and clientBehavior is ExecuteWhenNoResult",
|
|
331
|
-
({serverResult}: any) => {
|
|
332
|
-
const fakeHandler = jest.fn();
|
|
333
|
-
jest.spyOn(UseServerEffect, "useServerEffect").mockReturnValue(
|
|
334
|
-
serverResult,
|
|
335
|
-
);
|
|
336
|
-
|
|
337
|
-
// Act
|
|
338
|
-
clientRenderHook(() =>
|
|
339
|
-
useHydratableEffect("ID", fakeHandler, {
|
|
340
|
-
clientBehavior: WhenClientSide.ExecuteWhenNoResult,
|
|
341
|
-
}),
|
|
342
|
-
);
|
|
343
|
-
|
|
344
|
-
// Assert
|
|
345
|
-
expect(fakeHandler).not.toHaveBeenCalled();
|
|
346
|
-
},
|
|
347
|
-
);
|
|
348
|
-
|
|
349
|
-
it("should fulfill request once only if requestId does not change", async () => {
|
|
350
|
-
const fakeHandler = jest.fn().mockResolvedValue("data");
|
|
351
|
-
jest.spyOn(UseServerEffect, "useServerEffect").mockReturnValue(
|
|
352
|
-
null,
|
|
353
|
-
);
|
|
354
|
-
|
|
355
|
-
// Act
|
|
356
|
-
const {rerender, waitForNextUpdate} = clientRenderHook(() =>
|
|
357
|
-
useHydratableEffect("ID", fakeHandler),
|
|
358
|
-
);
|
|
359
|
-
rerender();
|
|
360
|
-
await waitForNextUpdate();
|
|
361
|
-
|
|
362
|
-
// Assert
|
|
363
|
-
expect(fakeHandler).toHaveBeenCalledTimes(1);
|
|
364
|
-
});
|
|
365
|
-
|
|
366
|
-
it("should fulfill request again if requestId changes", async () => {
|
|
367
|
-
// Arrange
|
|
368
|
-
const fakeHandler = jest.fn().mockResolvedValue("data");
|
|
369
|
-
jest.spyOn(UseServerEffect, "useServerEffect").mockReturnValue(
|
|
370
|
-
null,
|
|
371
|
-
);
|
|
372
|
-
|
|
373
|
-
// Act
|
|
374
|
-
const {rerender, waitForNextUpdate} = clientRenderHook(
|
|
375
|
-
({requestId}: any) =>
|
|
376
|
-
useHydratableEffect(requestId, fakeHandler),
|
|
377
|
-
{
|
|
378
|
-
initialProps: {requestId: "ID"},
|
|
379
|
-
},
|
|
380
|
-
);
|
|
381
|
-
rerender({requestId: "ID2"});
|
|
382
|
-
await waitForNextUpdate();
|
|
383
|
-
|
|
384
|
-
// Assert
|
|
385
|
-
expect(fakeHandler).toHaveBeenCalledTimes(2);
|
|
386
|
-
});
|
|
387
|
-
|
|
388
|
-
it("should default shared cache to hydrate value for new requestId", () => {
|
|
389
|
-
// Arrange
|
|
390
|
-
const fakeHandler = jest.fn().mockResolvedValue("NEVER CALLED");
|
|
391
|
-
jest.spyOn(UseServerEffect, "useServerEffect")
|
|
392
|
-
// First requestId will get hydrated value. No fetch will occur.
|
|
393
|
-
// The hook result will be this value.
|
|
394
|
-
.mockReturnValueOnce(Status.success("BADDATA"))
|
|
395
|
-
// Second requestId will get a different hydrated value.
|
|
396
|
-
// No fetch will occur. The hook will then be this value.
|
|
397
|
-
.mockReturnValueOnce(Status.success("GOODDATA"));
|
|
398
|
-
|
|
399
|
-
// Act
|
|
400
|
-
const {rerender, result} = clientRenderHook(
|
|
401
|
-
({requestId}: any) =>
|
|
402
|
-
useHydratableEffect(requestId, fakeHandler),
|
|
403
|
-
{
|
|
404
|
-
initialProps: {requestId: "ID"},
|
|
405
|
-
},
|
|
406
|
-
);
|
|
407
|
-
rerender({requestId: "ID2"});
|
|
408
|
-
|
|
409
|
-
// Assert
|
|
410
|
-
expect(result.current).toStrictEqual(Status.success("GOODDATA"));
|
|
411
|
-
});
|
|
412
|
-
|
|
413
|
-
it("should update shared cache with result when request is fulfilled", async () => {
|
|
414
|
-
// Arrange
|
|
415
|
-
const setCacheFn = jest.fn();
|
|
416
|
-
jest.spyOn(UseSharedCache, "useSharedCache").mockReturnValue([
|
|
417
|
-
null,
|
|
418
|
-
setCacheFn,
|
|
419
|
-
]);
|
|
420
|
-
const fakeHandler = jest.fn().mockResolvedValue("DATA");
|
|
421
|
-
|
|
422
|
-
// Act
|
|
423
|
-
const {waitForNextUpdate} = clientRenderHook(() =>
|
|
424
|
-
useHydratableEffect("ID", fakeHandler),
|
|
425
|
-
);
|
|
426
|
-
await waitForNextUpdate();
|
|
427
|
-
|
|
428
|
-
// Assert
|
|
429
|
-
expect(setCacheFn).toHaveBeenCalledWith(Status.success("DATA"));
|
|
430
|
-
});
|
|
431
|
-
|
|
432
|
-
it("should ignore inflight request if requestId changes", async () => {
|
|
433
|
-
// Arrange
|
|
434
|
-
const response1 = Promise.resolve("DATA1");
|
|
435
|
-
const response2 = Promise.resolve("DATA2");
|
|
436
|
-
const fakeHandler = jest
|
|
437
|
-
.fn()
|
|
438
|
-
.mockReturnValueOnce(response1)
|
|
439
|
-
.mockReturnValueOnce(response2);
|
|
440
|
-
jest.spyOn(UseServerEffect, "useServerEffect").mockReturnValue(
|
|
441
|
-
null,
|
|
442
|
-
);
|
|
443
|
-
|
|
444
|
-
// Act
|
|
445
|
-
const {rerender, result} = clientRenderHook(
|
|
446
|
-
({requestId}: any) =>
|
|
447
|
-
useHydratableEffect(requestId, fakeHandler),
|
|
448
|
-
{
|
|
449
|
-
initialProps: {requestId: "ID"},
|
|
450
|
-
},
|
|
451
|
-
);
|
|
452
|
-
rerender({requestId: "ID2"});
|
|
453
|
-
await act((): Promise<any> => Promise.all([response1, response2]));
|
|
454
|
-
|
|
455
|
-
// Assert
|
|
456
|
-
expect(result.all).not.toContainEqual(Status.success("DATA1"));
|
|
457
|
-
});
|
|
458
|
-
|
|
459
|
-
it("should return result of fulfilled request for current requestId", async () => {
|
|
460
|
-
// Arrange
|
|
461
|
-
const response1 = Promise.resolve("DATA1");
|
|
462
|
-
const response2 = Promise.resolve("DATA2");
|
|
463
|
-
const fakeHandler = jest
|
|
464
|
-
.fn()
|
|
465
|
-
.mockReturnValueOnce(response1)
|
|
466
|
-
.mockReturnValueOnce(response2);
|
|
467
|
-
jest.spyOn(UseServerEffect, "useServerEffect").mockReturnValue(
|
|
468
|
-
null,
|
|
469
|
-
);
|
|
470
|
-
|
|
471
|
-
// Act
|
|
472
|
-
const {rerender, result} = clientRenderHook(
|
|
473
|
-
({requestId}: any) =>
|
|
474
|
-
useHydratableEffect(requestId, fakeHandler),
|
|
475
|
-
{
|
|
476
|
-
initialProps: {requestId: "ID"},
|
|
477
|
-
},
|
|
478
|
-
);
|
|
479
|
-
rerender({requestId: "ID2"});
|
|
480
|
-
await act((): Promise<any> => Promise.all([response1, response2]));
|
|
481
|
-
|
|
482
|
-
// Assert
|
|
483
|
-
expect(result.current).toStrictEqual(Status.success("DATA2"));
|
|
484
|
-
});
|
|
485
|
-
|
|
486
|
-
it("should not fulfill request when skip is true", () => {
|
|
487
|
-
// Arrange
|
|
488
|
-
const fakeHandler = jest.fn();
|
|
489
|
-
jest.spyOn(UseServerEffect, "useServerEffect").mockReturnValue(
|
|
490
|
-
null,
|
|
491
|
-
);
|
|
492
|
-
|
|
493
|
-
// Act
|
|
494
|
-
clientRenderHook(() =>
|
|
495
|
-
useHydratableEffect("ID", fakeHandler, {skip: true}),
|
|
496
|
-
);
|
|
497
|
-
|
|
498
|
-
// Assert
|
|
499
|
-
expect(fakeHandler).not.toHaveBeenCalled();
|
|
500
|
-
});
|
|
501
|
-
|
|
502
|
-
it("should ignore inflight request if skip changes", async () => {
|
|
503
|
-
// Arrange
|
|
504
|
-
const response1 = Promise.resolve("DATA1");
|
|
505
|
-
const fakeHandler = jest.fn().mockReturnValueOnce(response1);
|
|
506
|
-
jest.spyOn(UseServerEffect, "useServerEffect").mockReturnValue(
|
|
507
|
-
null,
|
|
508
|
-
);
|
|
509
|
-
|
|
510
|
-
// Act
|
|
511
|
-
const {rerender, result} = clientRenderHook(
|
|
512
|
-
({skip}: any) => useHydratableEffect("ID", fakeHandler, {skip}),
|
|
513
|
-
{
|
|
514
|
-
initialProps: {skip: false},
|
|
515
|
-
},
|
|
516
|
-
);
|
|
517
|
-
rerender({skip: true});
|
|
518
|
-
|
|
519
|
-
await act((): Promise<any> => response1);
|
|
520
|
-
|
|
521
|
-
// Assert
|
|
522
|
-
expect(result.all).not.toContainEqual(Status.success("DATA1"));
|
|
523
|
-
});
|
|
524
|
-
|
|
525
|
-
it("should not ignore inflight request if handler changes", async () => {
|
|
526
|
-
// Arrange
|
|
527
|
-
const response1 = Promise.resolve("DATA1");
|
|
528
|
-
const response2 = Promise.resolve("DATA2");
|
|
529
|
-
const fakeHandler1 = jest.fn().mockReturnValueOnce(response1);
|
|
530
|
-
const fakeHandler2 = jest.fn().mockReturnValueOnce(response2);
|
|
531
|
-
jest.spyOn(UseServerEffect, "useServerEffect").mockReturnValue(
|
|
532
|
-
null,
|
|
533
|
-
);
|
|
534
|
-
|
|
535
|
-
// Act
|
|
536
|
-
const {rerender, result} = clientRenderHook(
|
|
537
|
-
({handler}: any) => useHydratableEffect("ID", handler),
|
|
538
|
-
{
|
|
539
|
-
initialProps: {handler: fakeHandler1},
|
|
540
|
-
},
|
|
541
|
-
);
|
|
542
|
-
rerender({handler: fakeHandler2});
|
|
543
|
-
await act((): Promise<any> => Promise.all([response1, response2]));
|
|
544
|
-
|
|
545
|
-
// Assert
|
|
546
|
-
expect(result.current).toStrictEqual(Status.success("DATA1"));
|
|
547
|
-
});
|
|
548
|
-
|
|
549
|
-
it("should not ignore inflight request if options (other than skip) change", async () => {
|
|
550
|
-
// Arrange
|
|
551
|
-
const response1 = Promise.resolve("DATA1");
|
|
552
|
-
const fakeHandler = jest.fn().mockReturnValueOnce(response1);
|
|
553
|
-
jest.spyOn(UseServerEffect, "useServerEffect").mockReturnValue(
|
|
554
|
-
null,
|
|
555
|
-
);
|
|
556
|
-
|
|
557
|
-
// Act
|
|
558
|
-
const {rerender, result} = clientRenderHook(
|
|
559
|
-
({options}: any) => useHydratableEffect("ID", fakeHandler),
|
|
560
|
-
{
|
|
561
|
-
initialProps: {options: undefined},
|
|
562
|
-
},
|
|
563
|
-
);
|
|
564
|
-
rerender({
|
|
565
|
-
options: {
|
|
566
|
-
scope: "BLAH!",
|
|
567
|
-
} as any,
|
|
568
|
-
});
|
|
569
|
-
|
|
570
|
-
await act((): Promise<any> => response1);
|
|
571
|
-
|
|
572
|
-
// Assert
|
|
573
|
-
expect(result.current).toStrictEqual(Status.success("DATA1"));
|
|
574
|
-
});
|
|
575
|
-
|
|
576
|
-
it("should return previous result when requestId changes and retainResultOnChange is true", async () => {
|
|
577
|
-
// Arrange
|
|
578
|
-
const response1 = Promise.resolve("DATA1");
|
|
579
|
-
const response2 = Promise.resolve("DATA2");
|
|
580
|
-
const fakeHandler = jest
|
|
581
|
-
.fn()
|
|
582
|
-
.mockReturnValueOnce(response1)
|
|
583
|
-
.mockReturnValueOnce(response2);
|
|
584
|
-
jest.spyOn(UseServerEffect, "useServerEffect").mockReturnValue(
|
|
585
|
-
null,
|
|
586
|
-
);
|
|
587
|
-
|
|
588
|
-
// Act
|
|
589
|
-
const {
|
|
590
|
-
rerender,
|
|
591
|
-
result: hookResult,
|
|
592
|
-
waitForNextUpdate,
|
|
593
|
-
} = clientRenderHook(
|
|
594
|
-
({requestId}: any) =>
|
|
595
|
-
useHydratableEffect(requestId, fakeHandler, {
|
|
596
|
-
retainResultOnChange: true,
|
|
597
|
-
}),
|
|
598
|
-
{
|
|
599
|
-
initialProps: {requestId: "ID"},
|
|
600
|
-
},
|
|
601
|
-
);
|
|
602
|
-
|
|
603
|
-
await act((): Promise<any> => response1);
|
|
604
|
-
rerender({requestId: "ID2"});
|
|
605
|
-
const result = hookResult.current;
|
|
606
|
-
await waitForNextUpdate();
|
|
607
|
-
|
|
608
|
-
// Assert
|
|
609
|
-
expect(result).toStrictEqual(Status.success("DATA1"));
|
|
610
|
-
});
|
|
611
|
-
|
|
612
|
-
it("should return loading status when requestId changes and retainResultOnChange is false", async () => {
|
|
613
|
-
// Arrange
|
|
614
|
-
const response1 = Promise.resolve("DATA1");
|
|
615
|
-
const response2 = new Promise(() => {
|
|
616
|
-
/*pending*/
|
|
617
|
-
});
|
|
618
|
-
const fakeHandler = jest
|
|
619
|
-
.fn()
|
|
620
|
-
.mockReturnValueOnce(response1)
|
|
621
|
-
.mockReturnValueOnce(response2);
|
|
622
|
-
jest.spyOn(UseServerEffect, "useServerEffect").mockReturnValue(
|
|
623
|
-
null,
|
|
624
|
-
);
|
|
625
|
-
|
|
626
|
-
// Act
|
|
627
|
-
const {rerender, result} = clientRenderHook(
|
|
628
|
-
({requestId}: any) =>
|
|
629
|
-
useHydratableEffect(requestId, fakeHandler, {
|
|
630
|
-
retainResultOnChange: false,
|
|
631
|
-
}),
|
|
632
|
-
{
|
|
633
|
-
initialProps: {requestId: "ID"},
|
|
634
|
-
},
|
|
635
|
-
);
|
|
636
|
-
|
|
637
|
-
await act((): Promise<any> => response1);
|
|
638
|
-
rerender({requestId: "ID2"});
|
|
639
|
-
|
|
640
|
-
// Assert
|
|
641
|
-
expect(result.current).toStrictEqual(Status.loading());
|
|
642
|
-
});
|
|
643
|
-
|
|
644
|
-
it("should trigger render when request is fulfilled and onResultChanged is undefined", async () => {
|
|
645
|
-
// Arrange
|
|
646
|
-
const response = Promise.resolve("DATA");
|
|
647
|
-
const fakeHandler = jest.fn().mockReturnValue(response);
|
|
648
|
-
jest.spyOn(UseServerEffect, "useServerEffect").mockReturnValue(
|
|
649
|
-
null,
|
|
650
|
-
);
|
|
651
|
-
|
|
652
|
-
// Act
|
|
653
|
-
const {result} = clientRenderHook(() =>
|
|
654
|
-
useHydratableEffect("ID", fakeHandler),
|
|
655
|
-
);
|
|
656
|
-
|
|
657
|
-
await act((): Promise<any> => response);
|
|
658
|
-
|
|
659
|
-
// Assert
|
|
660
|
-
expect(result.current).toStrictEqual(Status.success("DATA"));
|
|
661
|
-
});
|
|
662
|
-
|
|
663
|
-
it("should not trigger render when request is fulfilled and onResultChanged is defined", async () => {
|
|
664
|
-
// Arrange
|
|
665
|
-
const response = Promise.resolve("DATA");
|
|
666
|
-
const fakeHandler = jest.fn().mockReturnValue(response);
|
|
667
|
-
jest.spyOn(UseServerEffect, "useServerEffect").mockReturnValue(
|
|
668
|
-
null,
|
|
669
|
-
);
|
|
670
|
-
|
|
671
|
-
// Act
|
|
672
|
-
const {result} = clientRenderHook(() =>
|
|
673
|
-
useHydratableEffect("ID", fakeHandler, {
|
|
674
|
-
onResultChanged: () => {},
|
|
675
|
-
}),
|
|
676
|
-
);
|
|
677
|
-
|
|
678
|
-
await act((): Promise<any> => response);
|
|
679
|
-
|
|
680
|
-
// Assert
|
|
681
|
-
expect(result.current).toStrictEqual(Status.loading());
|
|
682
|
-
});
|
|
683
|
-
|
|
684
|
-
it("should call onResultChanged when request is fulfilled and onResultChanged is defined", async () => {
|
|
685
|
-
// Arrange
|
|
686
|
-
const response = Promise.resolve("DATA");
|
|
687
|
-
const fakeHandler = jest.fn().mockReturnValue(response);
|
|
688
|
-
jest.spyOn(UseServerEffect, "useServerEffect").mockReturnValue(
|
|
689
|
-
null,
|
|
690
|
-
);
|
|
691
|
-
const onResultChanged = jest.fn();
|
|
692
|
-
|
|
693
|
-
// Act
|
|
694
|
-
clientRenderHook(() =>
|
|
695
|
-
useHydratableEffect("ID", fakeHandler, {
|
|
696
|
-
onResultChanged,
|
|
697
|
-
}),
|
|
698
|
-
);
|
|
699
|
-
|
|
700
|
-
await act((): Promise<any> => response);
|
|
701
|
-
|
|
702
|
-
// Assert
|
|
703
|
-
expect(onResultChanged).toHaveBeenCalledWith(
|
|
704
|
-
Status.success("DATA"),
|
|
705
|
-
);
|
|
706
|
-
});
|
|
707
|
-
});
|
|
708
|
-
});
|