@khanacademy/wonder-blocks-data 12.0.0 → 13.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 +11 -0
- package/dist/components/data.d.ts +2 -2
- package/dist/es/index.js +10 -6
- package/dist/hooks/use-gql.d.ts +5 -1
- package/dist/index.js +10 -6
- package/dist/util/status.d.ts +4 -3
- package/dist/util/types.d.ts +2 -0
- package/package.json +2 -2
- package/src/components/__tests__/data.test.tsx +6 -13
- package/src/components/data.ts +2 -4
- package/src/hooks/__tests__/use-cached-effect.test.tsx +79 -40
- package/src/hooks/__tests__/use-gql-router-context.test.tsx +1 -2
- package/src/hooks/__tests__/use-hydratable-effect.test.ts +1 -2
- package/src/hooks/__tests__/use-request-interception.test.tsx +2 -5
- package/src/hooks/__tests__/use-server-effect.test.ts +3 -6
- package/src/hooks/__tests__/use-shared-cache.test.ts +17 -13
- package/src/hooks/use-cached-effect.ts +24 -20
- package/src/hooks/use-gql.ts +12 -9
- package/src/hooks/use-request-interception.ts +13 -11
- package/src/hooks/use-shared-cache.ts +4 -2
- package/src/util/__tests__/request-api.test.ts +2 -1
- package/src/util/__tests__/request-tracking.test.tsx +5 -9
- package/src/util/__tests__/result-from-cache-response.test.ts +2 -2
- package/src/util/__tests__/serializable-in-memory-cache.test.ts +1 -2
- package/src/util/__tests__/ssr-cache.test.ts +2 -4
- package/src/util/__tests__/to-gql-operation.test.ts +2 -4
- package/src/util/graphql-document-node-parser.ts +6 -6
- package/src/util/merge-gql-context.ts +2 -1
- package/src/util/request-tracking.ts +6 -2
- package/src/util/ssr-cache.ts +11 -8
- package/src/util/status.ts +6 -0
- package/src/util/types.ts +3 -0
- package/tsconfig-build.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# @khanacademy/wonder-blocks-data
|
|
2
2
|
|
|
3
|
+
## 13.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- 65c02cff: Return new no-data state from useCachedEffect when it is not loading and there is no data to return
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- Updated dependencies [1920feb8]
|
|
12
|
+
- @khanacademy/wonder-blocks-core@6.0.1
|
|
13
|
+
|
|
3
14
|
## 12.0.0
|
|
4
15
|
|
|
5
16
|
### Major Changes
|
|
@@ -41,12 +41,12 @@ TData extends ValidCacheData> = {
|
|
|
41
41
|
* loading state and data or error that gets retrieved from cache or loaded
|
|
42
42
|
* via the request if no cached value is available.
|
|
43
43
|
*/
|
|
44
|
-
children: (result: Result<TData>) => React.
|
|
44
|
+
children: (result: Result<TData>) => React.ReactElement | null;
|
|
45
45
|
};
|
|
46
46
|
/**
|
|
47
47
|
* This component is the main component of Wonder Blocks Data. With this, data
|
|
48
48
|
* requirements can be placed in a React application in a manner that will
|
|
49
49
|
* support server-side rendering and efficient caching.
|
|
50
50
|
*/
|
|
51
|
-
declare const Data: <TData extends ValidCacheData>({ requestId, handler, children, retainResultOnChange, clientBehavior, }: Props<TData>) => React.ReactElement;
|
|
51
|
+
declare const Data: <TData extends ValidCacheData>({ requestId, handler, children, retainResultOnChange, clientBehavior, }: Props<TData>) => React.ReactElement | null;
|
|
52
52
|
export default Data;
|
package/dist/es/index.js
CHANGED
|
@@ -162,9 +162,9 @@ class SsrCache {
|
|
|
162
162
|
this._ssrOnlyCache.purgeAll(realPredicate);
|
|
163
163
|
};
|
|
164
164
|
this.cloneHydratableData = () => {
|
|
165
|
-
var
|
|
165
|
+
var _ref;
|
|
166
166
|
const cache = this._hydrationCache.clone();
|
|
167
|
-
return (
|
|
167
|
+
return (_ref = cache[DefaultScope$2]) != null ? _ref : {};
|
|
168
168
|
};
|
|
169
169
|
this._ssrOnlyCache = ssrOnlyCache || new SerializableInMemoryCache();
|
|
170
170
|
this._hydrationCache = hydrationCache || new SerializableInMemoryCache();
|
|
@@ -261,7 +261,7 @@ class RequestTracker {
|
|
|
261
261
|
}
|
|
262
262
|
return _default;
|
|
263
263
|
}
|
|
264
|
-
constructor(responseCache
|
|
264
|
+
constructor(responseCache) {
|
|
265
265
|
this._trackedRequests = {};
|
|
266
266
|
this._responseCache = void 0;
|
|
267
267
|
this._requestFulfillment = void 0;
|
|
@@ -379,11 +379,15 @@ class TrackData extends React.Component {
|
|
|
379
379
|
const loadingStatus = Object.freeze({
|
|
380
380
|
status: "loading"
|
|
381
381
|
});
|
|
382
|
+
const noDataStatus = Object.freeze({
|
|
383
|
+
status: "no-data"
|
|
384
|
+
});
|
|
382
385
|
const abortedStatus = Object.freeze({
|
|
383
386
|
status: "aborted"
|
|
384
387
|
});
|
|
385
388
|
const Status = Object.freeze({
|
|
386
389
|
loading: () => loadingStatus,
|
|
390
|
+
noData: () => noDataStatus,
|
|
387
391
|
aborted: () => abortedStatus,
|
|
388
392
|
success: data => ({
|
|
389
393
|
status: "success",
|
|
@@ -527,8 +531,8 @@ const useCachedEffect = (requestId, handler, options = {}) => {
|
|
|
527
531
|
currentRequestRef.current = null;
|
|
528
532
|
};
|
|
529
533
|
}, [shouldFetch, fetchRequest]);
|
|
530
|
-
const lastResultAgnosticOfIdRef = React.useRef(Status.
|
|
531
|
-
const loadingResult = retainResultOnChange ? lastResultAgnosticOfIdRef.current : Status.loading();
|
|
534
|
+
const lastResultAgnosticOfIdRef = React.useRef(Status.noData());
|
|
535
|
+
const loadingResult = retainResultOnChange ? lastResultAgnosticOfIdRef.current : shouldFetch ? Status.loading() : Status.noData();
|
|
532
536
|
const result = (_ref = fetchPolicy === FetchPolicy.NetworkOnly ? networkResultRef.current : mostRecentResult) != null ? _ref : loadingResult;
|
|
533
537
|
lastResultAgnosticOfIdRef.current = result;
|
|
534
538
|
return [result, fetchRequest];
|
|
@@ -841,7 +845,7 @@ const useGql = (context = {}) => {
|
|
|
841
845
|
context = {}
|
|
842
846
|
} = options;
|
|
843
847
|
const finalContext = mergeGqlContext(defaultContext, context);
|
|
844
|
-
return fetch(operation, variables, finalContext).then(getGqlDataFromResponse);
|
|
848
|
+
return fetch(operation, variables, finalContext).then(response => getGqlDataFromResponse(response));
|
|
845
849
|
}, [gqlRouterContext]);
|
|
846
850
|
return gqlFetch;
|
|
847
851
|
};
|
package/dist/hooks/use-gql.d.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import type { GqlContext, GqlOperation, GqlFetchOptions } from "../util/gql-types";
|
|
2
|
+
interface GqlFetchFn<TContext extends GqlContext> {
|
|
3
|
+
<TData, TVariables extends Record<any, any>>(operation: GqlOperation<TData, TVariables>, options?: GqlFetchOptions<TVariables, TContext>): Promise<TData>;
|
|
4
|
+
}
|
|
2
5
|
/**
|
|
3
6
|
* Hook to obtain a gqlFetch function for performing GraphQL requests.
|
|
4
7
|
*
|
|
@@ -9,4 +12,5 @@ import type { GqlContext, GqlOperation, GqlFetchOptions } from "../util/gql-type
|
|
|
9
12
|
* Values in the partial context given to the returned fetch function will
|
|
10
13
|
* only be included if they have a value other than undefined.
|
|
11
14
|
*/
|
|
12
|
-
export declare const useGql: <TContext extends GqlContext>(context?: Partial<TContext>) => <
|
|
15
|
+
export declare const useGql: <TContext extends GqlContext>(context?: Partial<TContext>) => GqlFetchFn<TContext>;
|
|
16
|
+
export {};
|
package/dist/index.js
CHANGED
|
@@ -185,9 +185,9 @@ class SsrCache {
|
|
|
185
185
|
this._ssrOnlyCache.purgeAll(realPredicate);
|
|
186
186
|
};
|
|
187
187
|
this.cloneHydratableData = () => {
|
|
188
|
-
var
|
|
188
|
+
var _ref;
|
|
189
189
|
const cache = this._hydrationCache.clone();
|
|
190
|
-
return (
|
|
190
|
+
return (_ref = cache[DefaultScope$2]) != null ? _ref : {};
|
|
191
191
|
};
|
|
192
192
|
this._ssrOnlyCache = ssrOnlyCache || new SerializableInMemoryCache();
|
|
193
193
|
this._hydrationCache = hydrationCache || new SerializableInMemoryCache();
|
|
@@ -284,7 +284,7 @@ class RequestTracker {
|
|
|
284
284
|
}
|
|
285
285
|
return _default;
|
|
286
286
|
}
|
|
287
|
-
constructor(responseCache
|
|
287
|
+
constructor(responseCache) {
|
|
288
288
|
this._trackedRequests = {};
|
|
289
289
|
this._responseCache = void 0;
|
|
290
290
|
this._requestFulfillment = void 0;
|
|
@@ -402,11 +402,15 @@ class TrackData extends React__namespace.Component {
|
|
|
402
402
|
const loadingStatus = Object.freeze({
|
|
403
403
|
status: "loading"
|
|
404
404
|
});
|
|
405
|
+
const noDataStatus = Object.freeze({
|
|
406
|
+
status: "no-data"
|
|
407
|
+
});
|
|
405
408
|
const abortedStatus = Object.freeze({
|
|
406
409
|
status: "aborted"
|
|
407
410
|
});
|
|
408
411
|
const Status = Object.freeze({
|
|
409
412
|
loading: () => loadingStatus,
|
|
413
|
+
noData: () => noDataStatus,
|
|
410
414
|
aborted: () => abortedStatus,
|
|
411
415
|
success: data => ({
|
|
412
416
|
status: "success",
|
|
@@ -550,8 +554,8 @@ const useCachedEffect = (requestId, handler, options = {}) => {
|
|
|
550
554
|
currentRequestRef.current = null;
|
|
551
555
|
};
|
|
552
556
|
}, [shouldFetch, fetchRequest]);
|
|
553
|
-
const lastResultAgnosticOfIdRef = React__namespace.useRef(Status.
|
|
554
|
-
const loadingResult = retainResultOnChange ? lastResultAgnosticOfIdRef.current : Status.loading();
|
|
557
|
+
const lastResultAgnosticOfIdRef = React__namespace.useRef(Status.noData());
|
|
558
|
+
const loadingResult = retainResultOnChange ? lastResultAgnosticOfIdRef.current : shouldFetch ? Status.loading() : Status.noData();
|
|
555
559
|
const result = (_ref = fetchPolicy === FetchPolicy.NetworkOnly ? networkResultRef.current : mostRecentResult) != null ? _ref : loadingResult;
|
|
556
560
|
lastResultAgnosticOfIdRef.current = result;
|
|
557
561
|
return [result, fetchRequest];
|
|
@@ -864,7 +868,7 @@ const useGql = (context = {}) => {
|
|
|
864
868
|
context = {}
|
|
865
869
|
} = options;
|
|
866
870
|
const finalContext = mergeGqlContext(defaultContext, context);
|
|
867
|
-
return fetch(operation, variables, finalContext).then(getGqlDataFromResponse);
|
|
871
|
+
return fetch(operation, variables, finalContext).then(response => getGqlDataFromResponse(response));
|
|
868
872
|
}, [gqlRouterContext]);
|
|
869
873
|
return gqlFetch;
|
|
870
874
|
};
|
package/dist/util/status.d.ts
CHANGED
|
@@ -4,7 +4,8 @@ import type { Result, ValidCacheData } from "./types";
|
|
|
4
4
|
*/
|
|
5
5
|
export declare const Status: Readonly<{
|
|
6
6
|
loading: <TData extends ValidCacheData = ValidCacheData>() => Result<TData>;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
noData: <TData_1 extends ValidCacheData = ValidCacheData>() => Result<TData_1>;
|
|
8
|
+
aborted: <TData_2 extends ValidCacheData = ValidCacheData>() => Result<TData_2>;
|
|
9
|
+
success: <TData_3 extends ValidCacheData>(data: TData_3) => Result<TData_3>;
|
|
10
|
+
error: <TData_4 extends ValidCacheData = ValidCacheData>(error: Error) => Result<TData_4>;
|
|
10
11
|
}>;
|
package/dist/util/types.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@khanacademy/wonder-blocks-data",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "13.0.0",
|
|
4
4
|
"design": "v1",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
16
|
"@babel/runtime": "^7.18.6",
|
|
17
|
-
"@khanacademy/wonder-blocks-core": "^6.0.
|
|
17
|
+
"@khanacademy/wonder-blocks-core": "^6.0.1"
|
|
18
18
|
},
|
|
19
19
|
"peerDependencies": {
|
|
20
20
|
"@khanacademy/wonder-stuff-core": "^1.2.2",
|
|
@@ -47,7 +47,7 @@ describe("Data", () => {
|
|
|
47
47
|
|
|
48
48
|
it("should make request for data on construction", async () => {
|
|
49
49
|
// Arrange
|
|
50
|
-
const response = Promise.resolve("data");
|
|
50
|
+
const response: any = Promise.resolve("data");
|
|
51
51
|
const fakeHandler = jest.fn().mockReturnValue(response);
|
|
52
52
|
const fakeChildrenFn = jest.fn(() => null);
|
|
53
53
|
|
|
@@ -57,7 +57,6 @@ describe("Data", () => {
|
|
|
57
57
|
{fakeChildrenFn}
|
|
58
58
|
</Data>,
|
|
59
59
|
);
|
|
60
|
-
// @ts-expect-error [FEI-5019] - TS2769 - No overload matches this call.
|
|
61
60
|
await act(() => response);
|
|
62
61
|
|
|
63
62
|
// Assert
|
|
@@ -66,7 +65,7 @@ describe("Data", () => {
|
|
|
66
65
|
|
|
67
66
|
it("should initially render children with loading", async () => {
|
|
68
67
|
// Arrange
|
|
69
|
-
const response = Promise.resolve("data");
|
|
68
|
+
const response: any = Promise.resolve("data");
|
|
70
69
|
const fakeHandler = jest.fn().mockReturnValue(response);
|
|
71
70
|
const fakeChildrenFn = jest.fn(() => null);
|
|
72
71
|
|
|
@@ -76,7 +75,6 @@ describe("Data", () => {
|
|
|
76
75
|
{fakeChildrenFn}
|
|
77
76
|
</Data>,
|
|
78
77
|
);
|
|
79
|
-
// @ts-expect-error [FEI-5019] - TS2769 - No overload matches this call.
|
|
80
78
|
await act(() => response);
|
|
81
79
|
|
|
82
80
|
// Assert
|
|
@@ -272,7 +270,7 @@ describe("Data", () => {
|
|
|
272
270
|
|
|
273
271
|
it("should ignore resolution of pending handler fulfillment when id changes", async () => {
|
|
274
272
|
// Arrange
|
|
275
|
-
const oldRequest = Promise.resolve("OLD DATA");
|
|
273
|
+
const oldRequest: any = Promise.resolve("OLD DATA");
|
|
276
274
|
const oldHandler = jest
|
|
277
275
|
.fn()
|
|
278
276
|
.mockReturnValueOnce(oldRequest)
|
|
@@ -294,7 +292,6 @@ describe("Data", () => {
|
|
|
294
292
|
{fakeChildrenFn}
|
|
295
293
|
</Data>,
|
|
296
294
|
);
|
|
297
|
-
// @ts-expect-error [FEI-5019] - TS2769 - No overload matches this call.
|
|
298
295
|
await act(() => oldRequest);
|
|
299
296
|
|
|
300
297
|
// Assert
|
|
@@ -343,14 +340,13 @@ describe("Data", () => {
|
|
|
343
340
|
|
|
344
341
|
it("should ignore catastrophic request fulfillment when id changes", async () => {
|
|
345
342
|
// Arrange
|
|
346
|
-
const catastrophe = Promise.resolve({
|
|
343
|
+
const catastrophe: any = Promise.resolve({
|
|
347
344
|
status: "error",
|
|
348
345
|
error: new Error("CATASTROPHE!"),
|
|
349
346
|
});
|
|
350
347
|
jest.spyOn(
|
|
351
348
|
RequestFulfillment.Default,
|
|
352
349
|
"fulfill",
|
|
353
|
-
// @ts-expect-error [FEI-5019] - TS2345 - Argument of type 'Promise<{ status: string; error: Error; }>' is not assignable to parameter of type 'Promise<Result<ValidCacheData>>'.
|
|
354
350
|
).mockReturnValueOnce(catastrophe);
|
|
355
351
|
const oldHandler = jest.fn().mockResolvedValue("OLD DATA");
|
|
356
352
|
|
|
@@ -367,7 +363,6 @@ describe("Data", () => {
|
|
|
367
363
|
</Data>,
|
|
368
364
|
);
|
|
369
365
|
await act(() =>
|
|
370
|
-
// @ts-expect-error [FEI-5019] - TS2769 - No overload matches this call.
|
|
371
366
|
catastrophe.catch(() => {
|
|
372
367
|
/* ignore */
|
|
373
368
|
}),
|
|
@@ -428,8 +423,8 @@ describe("Data", () => {
|
|
|
428
423
|
|
|
429
424
|
it("should retain old data while reloading if retainResultOnChange is true", async () => {
|
|
430
425
|
// Arrange
|
|
431
|
-
const response1 = Promise.resolve("data1");
|
|
432
|
-
const response2 = Promise.resolve("data2");
|
|
426
|
+
const response1: any = Promise.resolve("data1");
|
|
427
|
+
const response2: any = Promise.resolve("data2");
|
|
433
428
|
const fakeHandler1 = () => response1;
|
|
434
429
|
const fakeHandler2 = () => response2;
|
|
435
430
|
const fakeChildrenFn = jest.fn(() => null);
|
|
@@ -445,7 +440,6 @@ describe("Data", () => {
|
|
|
445
440
|
</Data>,
|
|
446
441
|
);
|
|
447
442
|
fakeChildrenFn.mockClear();
|
|
448
|
-
// @ts-expect-error [FEI-5019] - TS2769 - No overload matches this call.
|
|
449
443
|
await act(() => response1);
|
|
450
444
|
wrapper.rerender(
|
|
451
445
|
<Data
|
|
@@ -456,7 +450,6 @@ describe("Data", () => {
|
|
|
456
450
|
{fakeChildrenFn}
|
|
457
451
|
</Data>,
|
|
458
452
|
);
|
|
459
|
-
// @ts-expect-error [FEI-5019] - TS2769 - No overload matches this call.
|
|
460
453
|
await act(() => response2);
|
|
461
454
|
|
|
462
455
|
// Assert
|
package/src/components/data.ts
CHANGED
|
@@ -48,7 +48,7 @@ type Props<
|
|
|
48
48
|
* loading state and data or error that gets retrieved from cache or loaded
|
|
49
49
|
* via the request if no cached value is available.
|
|
50
50
|
*/
|
|
51
|
-
children: (result: Result<TData>) => React.
|
|
51
|
+
children: (result: Result<TData>) => React.ReactElement | null;
|
|
52
52
|
};
|
|
53
53
|
|
|
54
54
|
/**
|
|
@@ -62,13 +62,11 @@ const Data = <TData extends ValidCacheData>({
|
|
|
62
62
|
children,
|
|
63
63
|
retainResultOnChange = false,
|
|
64
64
|
clientBehavior = WhenClientSide.ExecuteWhenNoSuccessResult,
|
|
65
|
-
}: Props<TData>): React.ReactElement => {
|
|
65
|
+
}: Props<TData>): React.ReactElement | null => {
|
|
66
66
|
const result = useHydratableEffect(requestId, handler, {
|
|
67
67
|
retainResultOnChange,
|
|
68
68
|
clientBehavior,
|
|
69
69
|
});
|
|
70
|
-
// @ts-expect-error: React TS types don't allow functional components to return
|
|
71
|
-
// ReactNodes even though React itself does.
|
|
72
70
|
return children(result);
|
|
73
71
|
};
|
|
74
72
|
|
|
@@ -22,8 +22,9 @@ jest.mock("../use-request-interception");
|
|
|
22
22
|
jest.mock("../use-shared-cache");
|
|
23
23
|
|
|
24
24
|
const allPolicies = Array.from(values(FetchPolicy));
|
|
25
|
-
const allPoliciesBut = (
|
|
26
|
-
|
|
25
|
+
const allPoliciesBut = (
|
|
26
|
+
...policies: Array<typeof FetchPolicy[keyof typeof FetchPolicy]>
|
|
27
|
+
) => allPolicies.filter((p: any) => !policies.includes(p));
|
|
27
28
|
|
|
28
29
|
describe("#useCachedEffect", () => {
|
|
29
30
|
beforeEach(() => {
|
|
@@ -116,7 +117,7 @@ describe("#useCachedEffect", () => {
|
|
|
116
117
|
},
|
|
117
118
|
);
|
|
118
119
|
|
|
119
|
-
describe.each(
|
|
120
|
+
describe.each(allPoliciesBut(FetchPolicy.CacheOnly))(
|
|
120
121
|
"with FetchPolicy.%s without cached result",
|
|
121
122
|
(fetchPolicy: any) => {
|
|
122
123
|
it("should return a loading result", () => {
|
|
@@ -138,6 +139,27 @@ describe("#useCachedEffect", () => {
|
|
|
138
139
|
},
|
|
139
140
|
);
|
|
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
|
+
} = serverRenderHook(() =>
|
|
153
|
+
useCachedEffect("ID", fakeHandler, {
|
|
154
|
+
fetchPolicy: FetchPolicy.CacheOnly,
|
|
155
|
+
}),
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
// Assert
|
|
159
|
+
expect(result).toStrictEqual(Status.noData());
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
141
163
|
describe.each(allPoliciesBut(FetchPolicy.NetworkOnly))(
|
|
142
164
|
"with FetchPolicy.%s with cached result",
|
|
143
165
|
(fetchPolicy: any) => {
|
|
@@ -232,7 +254,7 @@ describe("#useCachedEffect", () => {
|
|
|
232
254
|
"should provide function that causes refetch with FetchPolicy.%s",
|
|
233
255
|
async (fetchPolicy: any) => {
|
|
234
256
|
// Arrange
|
|
235
|
-
const response = Promise.resolve("DATA1");
|
|
257
|
+
const response: any = Promise.resolve("DATA1");
|
|
236
258
|
const fakeHandler = jest.fn().mockReturnValue(response);
|
|
237
259
|
|
|
238
260
|
// Act
|
|
@@ -244,11 +266,9 @@ describe("#useCachedEffect", () => {
|
|
|
244
266
|
useCachedEffect("ID", fakeHandler, {fetchPolicy}),
|
|
245
267
|
);
|
|
246
268
|
fakeHandler.mockClear();
|
|
247
|
-
|
|
248
|
-
await act((): Promise<unknown> => response);
|
|
269
|
+
await act(() => response);
|
|
249
270
|
act(refetch);
|
|
250
|
-
|
|
251
|
-
await act((): Promise<unknown> => response);
|
|
271
|
+
await act(() => response);
|
|
252
272
|
|
|
253
273
|
// Assert
|
|
254
274
|
expect(fakeHandler).toHaveBeenCalledTimes(1);
|
|
@@ -426,7 +446,7 @@ describe("#useCachedEffect", () => {
|
|
|
426
446
|
|
|
427
447
|
it("should ignore inflight request if requestId changes", async () => {
|
|
428
448
|
// Arrange
|
|
429
|
-
const response1 = Promise.resolve("DATA1");
|
|
449
|
+
const response1: any = Promise.resolve("DATA1");
|
|
430
450
|
const response2 = Promise.resolve("DATA2");
|
|
431
451
|
const fakeHandler = jest
|
|
432
452
|
.fn()
|
|
@@ -449,7 +469,7 @@ describe("#useCachedEffect", () => {
|
|
|
449
469
|
|
|
450
470
|
it("should return result of fulfilled request for current requestId", async () => {
|
|
451
471
|
// Arrange
|
|
452
|
-
const response1 = Promise.resolve("DATA1");
|
|
472
|
+
const response1: any = Promise.resolve("DATA1");
|
|
453
473
|
const response2 = Promise.resolve("DATA2");
|
|
454
474
|
const fakeHandler = jest
|
|
455
475
|
.fn()
|
|
@@ -485,7 +505,7 @@ describe("#useCachedEffect", () => {
|
|
|
485
505
|
|
|
486
506
|
it("should ignore result of inflight request if skip changes", async () => {
|
|
487
507
|
// Arrange
|
|
488
|
-
const response1 = Promise.resolve("DATA1");
|
|
508
|
+
const response1: any = Promise.resolve("DATA1");
|
|
489
509
|
const fakeHandler = jest.fn().mockReturnValueOnce(response1);
|
|
490
510
|
|
|
491
511
|
// Act
|
|
@@ -496,8 +516,7 @@ describe("#useCachedEffect", () => {
|
|
|
496
516
|
},
|
|
497
517
|
);
|
|
498
518
|
rerender({skip: true});
|
|
499
|
-
|
|
500
|
-
await act((): Promise<unknown> => response1);
|
|
519
|
+
await act(() => response1);
|
|
501
520
|
|
|
502
521
|
// Assert
|
|
503
522
|
expect(result.all).not.toContainEqual(Status.success("DATA1"));
|
|
@@ -505,7 +524,7 @@ describe("#useCachedEffect", () => {
|
|
|
505
524
|
|
|
506
525
|
it("should not ignore result of inflight request if handler changes", async () => {
|
|
507
526
|
// Arrange
|
|
508
|
-
const response1 = Promise.resolve("DATA1");
|
|
527
|
+
const response1: any = Promise.resolve("DATA1");
|
|
509
528
|
const response2 = Promise.resolve("DATA2");
|
|
510
529
|
const fakeHandler1 = jest.fn().mockReturnValueOnce(response1);
|
|
511
530
|
const fakeHandler2 = jest.fn().mockReturnValueOnce(response2);
|
|
@@ -526,7 +545,7 @@ describe("#useCachedEffect", () => {
|
|
|
526
545
|
|
|
527
546
|
it("should not ignore inflight request if options (other than skip) change", async () => {
|
|
528
547
|
// Arrange
|
|
529
|
-
const response1 = Promise.resolve("DATA1");
|
|
548
|
+
const response1: any = Promise.resolve("DATA1");
|
|
530
549
|
const fakeHandler = jest.fn().mockReturnValueOnce(response1);
|
|
531
550
|
|
|
532
551
|
// Act
|
|
@@ -537,13 +556,11 @@ describe("#useCachedEffect", () => {
|
|
|
537
556
|
},
|
|
538
557
|
);
|
|
539
558
|
rerender({
|
|
540
|
-
// @ts-expect-error [FEI-5019] - TS2322 - Type '{ scope: string; }' is not assignable to type 'undefined'.
|
|
541
559
|
options: {
|
|
542
560
|
scope: "BLAH!",
|
|
543
561
|
},
|
|
544
|
-
});
|
|
545
|
-
|
|
546
|
-
await act((): Promise<unknown> => response1);
|
|
562
|
+
} as any);
|
|
563
|
+
await act(() => response1);
|
|
547
564
|
|
|
548
565
|
// Assert
|
|
549
566
|
expect(result.current[0]).toStrictEqual(Status.success("DATA1"));
|
|
@@ -551,7 +568,7 @@ describe("#useCachedEffect", () => {
|
|
|
551
568
|
|
|
552
569
|
it("should return previous result when requestId changes and retainResultOnChange is true", async () => {
|
|
553
570
|
// Arrange
|
|
554
|
-
const response1 = Promise.resolve("DATA1");
|
|
571
|
+
const response1: any = Promise.resolve("DATA1");
|
|
555
572
|
const response2 = Promise.resolve("DATA2");
|
|
556
573
|
const fakeHandler = jest
|
|
557
574
|
.fn()
|
|
@@ -572,8 +589,7 @@ describe("#useCachedEffect", () => {
|
|
|
572
589
|
initialProps: {requestId: "ID"},
|
|
573
590
|
},
|
|
574
591
|
);
|
|
575
|
-
|
|
576
|
-
await act((): Promise<unknown> => response1);
|
|
592
|
+
await act(() => response1);
|
|
577
593
|
rerender({requestId: "ID2"});
|
|
578
594
|
const [result] = hookResult.current;
|
|
579
595
|
await waitForNextUpdate();
|
|
@@ -584,7 +600,7 @@ describe("#useCachedEffect", () => {
|
|
|
584
600
|
|
|
585
601
|
it("should return loading status when requestId changes and retainResultOnChange is false", async () => {
|
|
586
602
|
// Arrange
|
|
587
|
-
const response1 = Promise.resolve("DATA1");
|
|
603
|
+
const response1: any = Promise.resolve("DATA1");
|
|
588
604
|
const response2 = new Promise(() => {
|
|
589
605
|
/*pending*/
|
|
590
606
|
});
|
|
@@ -603,19 +619,47 @@ describe("#useCachedEffect", () => {
|
|
|
603
619
|
initialProps: {requestId: "ID"},
|
|
604
620
|
},
|
|
605
621
|
);
|
|
606
|
-
|
|
607
|
-
await act((): Promise<unknown> => response1);
|
|
622
|
+
await act(() => response1);
|
|
608
623
|
rerender({requestId: "ID2"});
|
|
609
624
|
|
|
610
625
|
// Assert
|
|
611
626
|
expect(result.current[0]).toStrictEqual(Status.loading());
|
|
612
627
|
});
|
|
613
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
|
+
|
|
614
658
|
it.each(allPoliciesBut(FetchPolicy.CacheOnly))(
|
|
615
659
|
"should trigger render when request is fulfilled and onResultChanged is undefined for FetchPolicy.%s",
|
|
616
660
|
async (fetchPolicy: any) => {
|
|
617
661
|
// Arrange
|
|
618
|
-
const response = Promise.resolve("DATA");
|
|
662
|
+
const response: any = Promise.resolve("DATA");
|
|
619
663
|
const fakeHandler = jest.fn().mockReturnValue(response);
|
|
620
664
|
let renderCount = 0;
|
|
621
665
|
const Component = React.memo(() => {
|
|
@@ -626,8 +670,7 @@ describe("#useCachedEffect", () => {
|
|
|
626
670
|
|
|
627
671
|
// Act
|
|
628
672
|
render(<Component />);
|
|
629
|
-
|
|
630
|
-
await reactAct((): Promise<unknown> => response);
|
|
673
|
+
await reactAct(() => response);
|
|
631
674
|
|
|
632
675
|
// Assert
|
|
633
676
|
expect(renderCount).toBe(2);
|
|
@@ -638,7 +681,7 @@ describe("#useCachedEffect", () => {
|
|
|
638
681
|
"should trigger render once per inflight request being fulfilled and onResultChanged is undefined for FetchPolicy.%s",
|
|
639
682
|
async (fetchPolicy: any) => {
|
|
640
683
|
// Arrange
|
|
641
|
-
const response = Promise.resolve("DATA");
|
|
684
|
+
const response: any = Promise.resolve("DATA");
|
|
642
685
|
const fakeHandler = jest.fn().mockReturnValue(response);
|
|
643
686
|
let renderCount = 0;
|
|
644
687
|
const Component = React.memo(() => {
|
|
@@ -657,8 +700,7 @@ describe("#useCachedEffect", () => {
|
|
|
657
700
|
|
|
658
701
|
// Act
|
|
659
702
|
render(<Component />);
|
|
660
|
-
|
|
661
|
-
await reactAct((): Promise<unknown> => response);
|
|
703
|
+
await reactAct(() => response);
|
|
662
704
|
|
|
663
705
|
// Assert
|
|
664
706
|
expect(renderCount).toBe(2);
|
|
@@ -669,7 +711,7 @@ describe("#useCachedEffect", () => {
|
|
|
669
711
|
"should not trigger render when request is fulfilled and onResultChanged is defined for FetchPolicy.%s",
|
|
670
712
|
async (fetchPolicy: any) => {
|
|
671
713
|
// Arrange
|
|
672
|
-
const response = Promise.resolve("DATA");
|
|
714
|
+
const response: any = Promise.resolve("DATA");
|
|
673
715
|
const fakeHandler = jest.fn().mockReturnValue(response);
|
|
674
716
|
let renderCount = 0;
|
|
675
717
|
const Component = React.memo(() => {
|
|
@@ -683,8 +725,7 @@ describe("#useCachedEffect", () => {
|
|
|
683
725
|
|
|
684
726
|
// Act
|
|
685
727
|
render(<Component />);
|
|
686
|
-
|
|
687
|
-
await reactAct((): Promise<unknown> => response);
|
|
728
|
+
await reactAct(() => response);
|
|
688
729
|
|
|
689
730
|
// Assert
|
|
690
731
|
expect(renderCount).toBe(1);
|
|
@@ -695,7 +736,7 @@ describe("#useCachedEffect", () => {
|
|
|
695
736
|
"should call onResultChanged when request is fulfilled and onResultChanged is defined for FetchPolicy.%s",
|
|
696
737
|
async (fetchPolicy: any) => {
|
|
697
738
|
// Arrange
|
|
698
|
-
const response = Promise.resolve("DATA");
|
|
739
|
+
const response: any = Promise.resolve("DATA");
|
|
699
740
|
const fakeHandler = jest.fn().mockReturnValue(response);
|
|
700
741
|
const onResultChanged = jest.fn();
|
|
701
742
|
|
|
@@ -706,8 +747,7 @@ describe("#useCachedEffect", () => {
|
|
|
706
747
|
fetchPolicy,
|
|
707
748
|
}),
|
|
708
749
|
);
|
|
709
|
-
|
|
710
|
-
await act((): Promise<unknown> => response);
|
|
750
|
+
await act(() => response);
|
|
711
751
|
|
|
712
752
|
// Assert
|
|
713
753
|
expect(onResultChanged).toHaveBeenCalledWith(
|
|
@@ -720,7 +760,7 @@ describe("#useCachedEffect", () => {
|
|
|
720
760
|
"should call onResultChanged once per inflight request being fulfilled and onResultChanged is defined for FetchPolicy.%s",
|
|
721
761
|
async (fetchPolicy: any) => {
|
|
722
762
|
// Arrange
|
|
723
|
-
const response = Promise.resolve("DATA");
|
|
763
|
+
const response: any = Promise.resolve("DATA");
|
|
724
764
|
const fakeHandler = jest.fn().mockReturnValue(response);
|
|
725
765
|
const onResultChanged = jest.fn();
|
|
726
766
|
|
|
@@ -739,8 +779,7 @@ describe("#useCachedEffect", () => {
|
|
|
739
779
|
act(refetch);
|
|
740
780
|
act(refetch);
|
|
741
781
|
act(refetch);
|
|
742
|
-
|
|
743
|
-
await act((): Promise<unknown> => response);
|
|
782
|
+
await act(() => response);
|
|
744
783
|
|
|
745
784
|
// Assert
|
|
746
785
|
expect(onResultChanged).toHaveBeenCalledTimes(1);
|
|
@@ -123,8 +123,7 @@ describe("#useGqlRouterContext", () => {
|
|
|
123
123
|
},
|
|
124
124
|
);
|
|
125
125
|
const result1 = wrapper.result.current;
|
|
126
|
-
|
|
127
|
-
wrapper.rerender({overrides: {}});
|
|
126
|
+
wrapper.rerender({overrides: {} as any});
|
|
128
127
|
const result2 = wrapper.result.current;
|
|
129
128
|
|
|
130
129
|
// Assert
|
|
@@ -562,10 +562,9 @@ describe("#useHydratableEffect", () => {
|
|
|
562
562
|
},
|
|
563
563
|
);
|
|
564
564
|
rerender({
|
|
565
|
-
// @ts-expect-error [FEI-5019] - TS2322 - Type '{ scope: string; }' is not assignable to type 'undefined'.
|
|
566
565
|
options: {
|
|
567
566
|
scope: "BLAH!",
|
|
568
|
-
},
|
|
567
|
+
} as any,
|
|
569
568
|
});
|
|
570
569
|
|
|
571
570
|
await act((): Promise<any> => response1);
|