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