@khanacademy/wonder-blocks-data 2.3.3 → 3.1.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 +21 -0
- package/dist/es/index.js +365 -429
- package/dist/index.js +455 -461
- package/docs.md +19 -13
- package/package.json +6 -6
- package/src/__tests__/__snapshots__/generated-snapshot.test.js.snap +40 -160
- package/src/__tests__/generated-snapshot.test.js +15 -195
- package/src/components/__tests__/data.test.js +159 -965
- package/src/components/__tests__/gql-router.test.js +64 -0
- package/src/components/__tests__/intercept-data.test.js +9 -66
- package/src/components/__tests__/track-data.test.js +6 -5
- package/src/components/data.js +9 -119
- package/src/components/data.md +38 -60
- package/src/components/gql-router.js +66 -0
- package/src/components/intercept-context.js +2 -3
- package/src/components/intercept-data.js +2 -34
- package/src/components/intercept-data.md +7 -105
- package/src/hooks/__tests__/use-data.test.js +826 -0
- package/src/hooks/__tests__/use-gql.test.js +233 -0
- package/src/hooks/use-data.js +143 -0
- package/src/hooks/use-gql.js +75 -0
- package/src/index.js +7 -9
- package/src/util/__tests__/get-gql-data-from-response.test.js +187 -0
- package/src/util/__tests__/memory-cache.test.js +134 -35
- package/src/util/__tests__/request-fulfillment.test.js +21 -36
- package/src/util/__tests__/request-handler.test.js +30 -30
- package/src/util/__tests__/request-tracking.test.js +29 -30
- package/src/util/__tests__/response-cache.test.js +521 -561
- package/src/util/__tests__/result-from-cache-entry.test.js +68 -0
- package/src/util/get-gql-data-from-response.js +69 -0
- package/src/util/gql-error.js +36 -0
- package/src/util/gql-router-context.js +6 -0
- package/src/util/gql-types.js +60 -0
- package/src/util/memory-cache.js +20 -15
- package/src/util/request-fulfillment.js +4 -0
- package/src/util/request-handler.js +4 -28
- package/src/util/request-handler.md +0 -32
- package/src/util/request-tracking.js +2 -3
- package/src/util/response-cache.js +50 -110
- package/src/util/result-from-cache-entry.js +38 -0
- package/src/util/types.js +14 -35
- package/LICENSE +0 -21
- package/src/components/__tests__/intercept-cache.test.js +0 -124
- package/src/components/__tests__/internal-data.test.js +0 -1030
- package/src/components/intercept-cache.js +0 -79
- package/src/components/intercept-cache.md +0 -103
- package/src/components/internal-data.js +0 -219
- package/src/util/__tests__/no-cache.test.js +0 -112
- package/src/util/no-cache.js +0 -66
- package/src/util/no-cache.md +0 -66
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
// @flow
|
|
2
2
|
import {Server} from "@khanacademy/wonder-blocks-core";
|
|
3
3
|
import MemoryCache from "./memory-cache.js";
|
|
4
|
-
import NoCache from "./no-cache.js";
|
|
5
4
|
|
|
6
5
|
import type {
|
|
7
6
|
ValidData,
|
|
8
7
|
CacheEntry,
|
|
9
8
|
Cache,
|
|
10
|
-
ICache,
|
|
11
9
|
IRequestHandler,
|
|
12
10
|
ResponseCache as ResCache,
|
|
13
11
|
} from "./types.js";
|
|
@@ -31,32 +29,17 @@ export class ResponseCache {
|
|
|
31
29
|
return _default;
|
|
32
30
|
}
|
|
33
31
|
|
|
34
|
-
|
|
32
|
+
_hydrationCache: MemoryCache<any, any>;
|
|
35
33
|
_ssrOnlyCache: ?MemoryCache<any, any>;
|
|
36
34
|
|
|
37
35
|
constructor(
|
|
38
|
-
|
|
36
|
+
hydrationCache: ?MemoryCache<any, any> = null,
|
|
39
37
|
ssrOnlyCache: ?MemoryCache<any, any> = null,
|
|
40
38
|
) {
|
|
41
39
|
this._ssrOnlyCache = Server.isServerSide()
|
|
42
40
|
? ssrOnlyCache || new MemoryCache()
|
|
43
41
|
: undefined;
|
|
44
|
-
this.
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Returns the default cache to use for the given handler.
|
|
49
|
-
*/
|
|
50
|
-
_defaultCache<TOptions, TData: ValidData>(
|
|
51
|
-
handler: IRequestHandler<TOptions, TData>,
|
|
52
|
-
): ICache<TOptions, TData> {
|
|
53
|
-
if (handler.hydrate) {
|
|
54
|
-
return this._hydrationAndDefaultCache;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// If the handler doesn't want to hydrate, we return the SSR-only cache.
|
|
58
|
-
// If we are client-side, we return our non-caching implementation.
|
|
59
|
-
return this._ssrOnlyCache || NoCache.Default;
|
|
42
|
+
this._hydrationCache = hydrationCache || new MemoryCache();
|
|
60
43
|
}
|
|
61
44
|
|
|
62
45
|
_setCacheEntry<TOptions, TData: ValidData>(
|
|
@@ -65,15 +48,14 @@ export class ResponseCache {
|
|
|
65
48
|
entry: CacheEntry<TData>,
|
|
66
49
|
): CacheEntry<TData> {
|
|
67
50
|
const frozenEntry = Object.freeze(entry);
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
// We
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
this._defaultCache(handler).store(handler, options, frozenEntry);
|
|
51
|
+
if (this._ssrOnlyCache != null) {
|
|
52
|
+
// We are server-side.
|
|
53
|
+
// We need to store this value.
|
|
54
|
+
if (handler.hydrate) {
|
|
55
|
+
this._hydrationCache.store(handler, options, frozenEntry);
|
|
56
|
+
} else {
|
|
57
|
+
this._ssrOnlyCache.store(handler, options, frozenEntry);
|
|
58
|
+
}
|
|
77
59
|
}
|
|
78
60
|
return frozenEntry;
|
|
79
61
|
}
|
|
@@ -84,14 +66,14 @@ export class ResponseCache {
|
|
|
84
66
|
* This can only be called if the cache is not already in use.
|
|
85
67
|
*/
|
|
86
68
|
initialize: (source: ResCache) => void = (source) => {
|
|
87
|
-
if (this.
|
|
69
|
+
if (this._hydrationCache.inUse) {
|
|
88
70
|
throw new Error(
|
|
89
71
|
"Cannot initialize data response cache more than once",
|
|
90
72
|
);
|
|
91
73
|
}
|
|
92
74
|
|
|
93
75
|
try {
|
|
94
|
-
this.
|
|
76
|
+
this._hydrationCache = new MemoryCache(source);
|
|
95
77
|
} catch (e) {
|
|
96
78
|
throw new Error(
|
|
97
79
|
`An error occurred trying to initialize the data response cache: ${e}`,
|
|
@@ -101,6 +83,8 @@ export class ResponseCache {
|
|
|
101
83
|
|
|
102
84
|
/**
|
|
103
85
|
* Cache data for a specific response.
|
|
86
|
+
*
|
|
87
|
+
* This is a noop when client-side.
|
|
104
88
|
*/
|
|
105
89
|
cacheData: <TOptions, TData: ValidData>(
|
|
106
90
|
handler: IRequestHandler<TOptions, TData>,
|
|
@@ -110,12 +94,12 @@ export class ResponseCache {
|
|
|
110
94
|
handler: IRequestHandler<TOptions, TData>,
|
|
111
95
|
options: TOptions,
|
|
112
96
|
data: TData,
|
|
113
|
-
): CacheEntry<TData> => {
|
|
114
|
-
return this._setCacheEntry(handler, options, {data});
|
|
115
|
-
};
|
|
97
|
+
): CacheEntry<TData> => this._setCacheEntry(handler, options, {data});
|
|
116
98
|
|
|
117
99
|
/**
|
|
118
100
|
* Cache an error for a specific response.
|
|
101
|
+
*
|
|
102
|
+
* This is a noop when client-side.
|
|
119
103
|
*/
|
|
120
104
|
cacheError: <TOptions, TData: ValidData>(
|
|
121
105
|
handler: IRequestHandler<TOptions, TData>,
|
|
@@ -140,42 +124,27 @@ export class ResponseCache {
|
|
|
140
124
|
handler: IRequestHandler<TOptions, TData>,
|
|
141
125
|
options: TOptions,
|
|
142
126
|
): ?$ReadOnly<CacheEntry<TData>> => {
|
|
143
|
-
//
|
|
144
|
-
//
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
// Get the internal entry for the handler.
|
|
154
|
-
// This allows us to use our hydrated cache during hydration.
|
|
155
|
-
// If we just returned null when the custom cache didn't have it,
|
|
156
|
-
// we would never hydrate properly.
|
|
157
|
-
const internalEntry = this._defaultCache<TOptions, TData>(
|
|
158
|
-
handler,
|
|
159
|
-
).retrieve(handler, options);
|
|
160
|
-
|
|
161
|
-
// If we are not server-side and we hydrated something that the custom
|
|
162
|
-
// cache didn't have, we need to make sure the custom cache contains
|
|
163
|
-
// that value.
|
|
164
|
-
if (
|
|
165
|
-
this._ssrOnlyCache == null &&
|
|
166
|
-
handler.cache != null &&
|
|
167
|
-
internalEntry != null
|
|
168
|
-
) {
|
|
169
|
-
// Yes, if this throws, we will have a problem. We want that.
|
|
170
|
-
// Bad cache implementations should be overt.
|
|
171
|
-
handler.cache.store(handler, options, internalEntry);
|
|
127
|
+
// Get the cached entry for this value.
|
|
128
|
+
// If the handler wants WB Data to hydrate (i.e. handler.hydrate is
|
|
129
|
+
// true), we use our hydration cache. Otherwise, if we're server-side
|
|
130
|
+
// we use our SSR-only cache. Otherwise, there's no entry to return.
|
|
131
|
+
const cache = handler.hydrate
|
|
132
|
+
? this._hydrationCache
|
|
133
|
+
: Server.isServerSide()
|
|
134
|
+
? this._ssrOnlyCache
|
|
135
|
+
: undefined;
|
|
136
|
+
const internalEntry = cache?.retrieve(handler, options);
|
|
172
137
|
|
|
173
|
-
|
|
138
|
+
// If we are not server-side and we hydrated something, let's clear
|
|
139
|
+
// that from the hydration cache to save memory.
|
|
140
|
+
if (this._ssrOnlyCache == null && internalEntry != null) {
|
|
141
|
+
// We now delete this from our hydration cache as we don't need it.
|
|
174
142
|
// This does mean that if another handler of the same type but
|
|
175
|
-
// without
|
|
176
|
-
//
|
|
177
|
-
//
|
|
178
|
-
|
|
143
|
+
// without some sort of linked cache won't get the value, but
|
|
144
|
+
// that's not an expected use-case. If two different places use the
|
|
145
|
+
// same handler and options (i.e. the same request), then the
|
|
146
|
+
// handler should cater to that to ensure they share the result.
|
|
147
|
+
this._hydrationCache.remove(handler, options);
|
|
179
148
|
}
|
|
180
149
|
return internalEntry;
|
|
181
150
|
};
|
|
@@ -199,31 +168,19 @@ export class ResponseCache {
|
|
|
199
168
|
// to match the key of the entry we're removing, but that's an
|
|
200
169
|
// inefficient way to remove a single item, so let's not do that.
|
|
201
170
|
|
|
202
|
-
//
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
// Delete the entry from our internal cache.
|
|
208
|
-
// Even if we have a custom cache, we want to make sure we still
|
|
209
|
-
// removed the same value from internal cache since this could be
|
|
210
|
-
// getting called before hydration for some complex advanced usage
|
|
211
|
-
// reason.
|
|
212
|
-
return (
|
|
213
|
-
this._defaultCache(handler).remove(handler, options) ||
|
|
214
|
-
removedCustom
|
|
215
|
-
);
|
|
171
|
+
// Delete the entry from the appropriate cache.
|
|
172
|
+
return handler.hydrate
|
|
173
|
+
? this._hydrationCache.remove(handler, options)
|
|
174
|
+
: this._ssrOnlyCache?.remove(handler, options) ?? false;
|
|
216
175
|
};
|
|
217
176
|
|
|
218
177
|
/**
|
|
219
178
|
* Remove from cache, any entries matching the given handler and predicate.
|
|
220
179
|
*
|
|
221
|
-
* This will, if present therein, remove matching values from the
|
|
222
|
-
*
|
|
180
|
+
* This will, if present therein, remove matching values from the framework
|
|
181
|
+
* in-memory cache.
|
|
223
182
|
*
|
|
224
|
-
* It returns a count of all records removed.
|
|
225
|
-
* keys, but of unique entries. So if the same key is removed from both the
|
|
226
|
-
* framework and custom caches, that will be 2 records removed.
|
|
183
|
+
* It returns a count of all records removed.
|
|
227
184
|
*/
|
|
228
185
|
removeAll: <TOptions, TData: ValidData>(
|
|
229
186
|
handler: IRequestHandler<TOptions, TData>,
|
|
@@ -238,36 +195,19 @@ export class ResponseCache {
|
|
|
238
195
|
cachedEntry: $ReadOnly<CacheEntry<TData>>,
|
|
239
196
|
) => boolean,
|
|
240
197
|
): number => {
|
|
241
|
-
//
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
customCache?.removeAll(handler, predicate) || 0;
|
|
246
|
-
|
|
247
|
-
// Apply the predicate to what we have in our internal cached.
|
|
248
|
-
// Even if we have a custom cache, we want to make sure we still
|
|
249
|
-
// removed the same value from internal cache since this could be
|
|
250
|
-
// getting called before hydration for some complex advanced usage
|
|
251
|
-
// reason.
|
|
252
|
-
const removedCount = this._defaultCache(handler).removeAll(
|
|
253
|
-
handler,
|
|
254
|
-
predicate,
|
|
255
|
-
);
|
|
256
|
-
|
|
257
|
-
// We have no idea which keys were removed from which caches,
|
|
258
|
-
// so we can't dedupe the remove counts based on keys.
|
|
259
|
-
// That's why we return the total records deleted rather than the
|
|
260
|
-
// total keys deleted.
|
|
261
|
-
return removedCount + removedCountCustom;
|
|
198
|
+
// Apply the predicate to what we have in the appropriate cache.
|
|
199
|
+
return handler.hydrate
|
|
200
|
+
? this._hydrationCache.removeAll(handler, predicate)
|
|
201
|
+
: this._ssrOnlyCache?.removeAll(handler, predicate) ?? 0;
|
|
262
202
|
};
|
|
263
203
|
|
|
264
204
|
/**
|
|
265
205
|
* Deep clone the hydration cache.
|
|
266
206
|
*
|
|
267
|
-
* By design, this
|
|
207
|
+
* By design, this only clones the data that is to be used for hydration.
|
|
268
208
|
*/
|
|
269
209
|
cloneHydratableData: () => $ReadOnly<Cache> = (): $ReadOnly<Cache> => {
|
|
270
210
|
// We return our hydration cache only.
|
|
271
|
-
return this.
|
|
211
|
+
return this._hydrationCache.cloneData();
|
|
272
212
|
};
|
|
273
213
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import type {ValidData, CacheEntry, Result} from "./types.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Turns a cache entry into a stateful result.
|
|
6
|
+
*/
|
|
7
|
+
export const resultFromCacheEntry = <TData: ValidData>(
|
|
8
|
+
cacheEntry: ?CacheEntry<TData>,
|
|
9
|
+
): Result<TData> => {
|
|
10
|
+
// No cache entry means we didn't load one yet.
|
|
11
|
+
if (cacheEntry == null) {
|
|
12
|
+
return {
|
|
13
|
+
status: "loading",
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const {data, error} = cacheEntry;
|
|
18
|
+
|
|
19
|
+
if (data != null) {
|
|
20
|
+
return {
|
|
21
|
+
status: "success",
|
|
22
|
+
data,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (error == null) {
|
|
27
|
+
// We should never get here ever.
|
|
28
|
+
return {
|
|
29
|
+
status: "error",
|
|
30
|
+
error: "Loaded result has invalid state where data and error are missing",
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
status: "error",
|
|
36
|
+
error,
|
|
37
|
+
};
|
|
38
|
+
};
|
package/src/util/types.js
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
// @flow
|
|
2
2
|
export type ValidData = string | boolean | number | {...};
|
|
3
3
|
|
|
4
|
+
export type Status = "loading" | "success" | "error";
|
|
5
|
+
|
|
4
6
|
export type Result<TData: ValidData> =
|
|
5
7
|
| {|
|
|
6
|
-
|
|
7
|
-
data?: void,
|
|
8
|
-
error?: void,
|
|
8
|
+
status: "loading",
|
|
9
9
|
|}
|
|
10
10
|
| {|
|
|
11
|
-
|
|
11
|
+
status: "success",
|
|
12
12
|
data?: TData,
|
|
13
|
-
|
|
13
|
+
|}
|
|
14
|
+
| {|
|
|
15
|
+
status: "error",
|
|
16
|
+
error: string,
|
|
14
17
|
|};
|
|
15
18
|
|
|
16
19
|
export type CacheEntry<TData: ValidData> =
|
|
@@ -28,24 +31,12 @@ type HandlerSubcache = {
|
|
|
28
31
|
...
|
|
29
32
|
};
|
|
30
33
|
|
|
31
|
-
export type InterceptCacheFn<TOptions, TData: ValidData> = (
|
|
32
|
-
options: TOptions,
|
|
33
|
-
cacheEntry: ?$ReadOnly<CacheEntry<TData>>,
|
|
34
|
-
) => ?$ReadOnly<CacheEntry<TData>>;
|
|
35
|
-
|
|
36
34
|
export type InterceptFulfillRequestFn<TOptions, TData: ValidData> = (
|
|
37
35
|
options: TOptions,
|
|
38
36
|
) => ?Promise<TData>;
|
|
39
37
|
|
|
40
|
-
export type InterceptShouldRefreshCacheFn<TOptions, TData: ValidData> = (
|
|
41
|
-
options: TOptions,
|
|
42
|
-
cachedEntry: ?$ReadOnly<CacheEntry<TData>>,
|
|
43
|
-
) => ?boolean;
|
|
44
|
-
|
|
45
38
|
export type Interceptor = {|
|
|
46
|
-
|
|
47
|
-
fulfillRequest?: ?InterceptFulfillRequestFn<any, any>,
|
|
48
|
-
shouldRefreshCache?: ?InterceptShouldRefreshCacheFn<any, any>,
|
|
39
|
+
fulfillRequest: InterceptFulfillRequestFn<any, any>,
|
|
49
40
|
|};
|
|
50
41
|
|
|
51
42
|
export type InterceptContextData = {
|
|
@@ -120,31 +111,19 @@ export interface IRequestHandler<TOptions, TData: ValidData> {
|
|
|
120
111
|
*/
|
|
121
112
|
get type(): string;
|
|
122
113
|
|
|
123
|
-
/**
|
|
124
|
-
* A custom cache to use with data that this handler requests.
|
|
125
|
-
* This only affects client-side caching of data.
|
|
126
|
-
*/
|
|
127
|
-
get cache(): ?ICache<TOptions, TData>;
|
|
128
|
-
|
|
129
114
|
/**
|
|
130
115
|
* When true, server-side results are cached and hydrated in the client.
|
|
131
116
|
* When false, the server-side cache is not used and results are not
|
|
132
117
|
* hydrated.
|
|
133
118
|
* This should only be set to false if something is ensuring that the
|
|
134
119
|
* hydrated client result will match the server result.
|
|
135
|
-
*/
|
|
136
|
-
get hydrate(): boolean;
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Determine if the cached data should be refreshed.
|
|
140
120
|
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
121
|
+
* For example, if Apollo is used to handle GraphQL requests in SSR mode,
|
|
122
|
+
* it has its own cache that is used to hydrate the client. Setting this
|
|
123
|
+
* to false makes sure we don't store the data twice, which would
|
|
124
|
+
* unnecessarily bloat the data sent back to the client.
|
|
143
125
|
*/
|
|
144
|
-
|
|
145
|
-
options: TOptions,
|
|
146
|
-
cachedEntry: ?$ReadOnly<CacheEntry<TData>>,
|
|
147
|
-
): boolean;
|
|
126
|
+
get hydrate(): boolean;
|
|
148
127
|
|
|
149
128
|
/**
|
|
150
129
|
* Get the key to use for a given request. This should be idempotent for a
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2018 Khan Academy
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
// @flow
|
|
2
|
-
import * as React from "react";
|
|
3
|
-
import {mount} from "enzyme";
|
|
4
|
-
|
|
5
|
-
import InterceptContext from "../intercept-context.js";
|
|
6
|
-
import InterceptData from "../intercept-data.js";
|
|
7
|
-
import InterceptCache from "../intercept-cache.js";
|
|
8
|
-
|
|
9
|
-
import type {IRequestHandler} from "../../util/types.js";
|
|
10
|
-
|
|
11
|
-
describe("InterceptCache", () => {
|
|
12
|
-
afterEach(() => {
|
|
13
|
-
jest.resetAllMocks();
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
it("should update context with getEntry", () => {
|
|
17
|
-
// Arrange
|
|
18
|
-
const fakeHandler: IRequestHandler<string, string> = {
|
|
19
|
-
fulfillRequest: () => Promise.resolve("data"),
|
|
20
|
-
getKey: (o) => o,
|
|
21
|
-
shouldRefreshCache: () => false,
|
|
22
|
-
type: "MY_HANDLER",
|
|
23
|
-
cache: null,
|
|
24
|
-
hydrate: true,
|
|
25
|
-
};
|
|
26
|
-
const getEntryFn = jest.fn();
|
|
27
|
-
const captureContextFn = jest.fn();
|
|
28
|
-
|
|
29
|
-
// Act
|
|
30
|
-
mount(
|
|
31
|
-
<InterceptCache handler={fakeHandler} getEntry={getEntryFn}>
|
|
32
|
-
<InterceptContext.Consumer>
|
|
33
|
-
{captureContextFn}
|
|
34
|
-
</InterceptContext.Consumer>
|
|
35
|
-
</InterceptCache>,
|
|
36
|
-
);
|
|
37
|
-
|
|
38
|
-
// Assert
|
|
39
|
-
expect(captureContextFn).toHaveBeenCalledWith(
|
|
40
|
-
expect.objectContaining({
|
|
41
|
-
MY_HANDLER: {
|
|
42
|
-
getEntry: getEntryFn,
|
|
43
|
-
},
|
|
44
|
-
}),
|
|
45
|
-
);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it("should override parent InterceptData", () => {
|
|
49
|
-
// Arrange
|
|
50
|
-
const fakeHandler: IRequestHandler<string, string> = {
|
|
51
|
-
fulfillRequest: () => Promise.resolve("data"),
|
|
52
|
-
getKey: (o) => o,
|
|
53
|
-
shouldRefreshCache: () => false,
|
|
54
|
-
type: "MY_HANDLER",
|
|
55
|
-
cache: null,
|
|
56
|
-
hydrate: true,
|
|
57
|
-
};
|
|
58
|
-
const getEntry1Fn = jest.fn();
|
|
59
|
-
const getEntry2Fn = jest.fn();
|
|
60
|
-
const captureContextFn = jest.fn();
|
|
61
|
-
|
|
62
|
-
// Act
|
|
63
|
-
mount(
|
|
64
|
-
<InterceptCache handler={fakeHandler} getEntry={getEntry1Fn}>
|
|
65
|
-
<InterceptCache handler={fakeHandler} getEntry={getEntry2Fn}>
|
|
66
|
-
<InterceptContext.Consumer>
|
|
67
|
-
{captureContextFn}
|
|
68
|
-
</InterceptContext.Consumer>
|
|
69
|
-
</InterceptCache>
|
|
70
|
-
</InterceptCache>,
|
|
71
|
-
);
|
|
72
|
-
|
|
73
|
-
// Assert
|
|
74
|
-
expect(captureContextFn).toHaveBeenCalledWith(
|
|
75
|
-
expect.objectContaining({
|
|
76
|
-
MY_HANDLER: {
|
|
77
|
-
getEntry: getEntry2Fn,
|
|
78
|
-
},
|
|
79
|
-
}),
|
|
80
|
-
);
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it("should not change InterceptData methods on existing interceptor", () => {
|
|
84
|
-
// Arrange
|
|
85
|
-
const fakeHandler: IRequestHandler<string, string> = {
|
|
86
|
-
fulfillRequest: () => Promise.resolve("data"),
|
|
87
|
-
getKey: (o) => o,
|
|
88
|
-
shouldRefreshCache: () => false,
|
|
89
|
-
type: "MY_HANDLER",
|
|
90
|
-
cache: null,
|
|
91
|
-
hydrate: true,
|
|
92
|
-
};
|
|
93
|
-
const fulfillRequestFn = jest.fn();
|
|
94
|
-
const shouldRefreshCacheFn = jest.fn();
|
|
95
|
-
const getEntryFn = jest.fn();
|
|
96
|
-
const captureContextFn = jest.fn();
|
|
97
|
-
|
|
98
|
-
// Act
|
|
99
|
-
mount(
|
|
100
|
-
<InterceptData
|
|
101
|
-
handler={fakeHandler}
|
|
102
|
-
fulfillRequest={fulfillRequestFn}
|
|
103
|
-
shouldRefreshCache={shouldRefreshCacheFn}
|
|
104
|
-
>
|
|
105
|
-
<InterceptCache handler={fakeHandler} getEntry={getEntryFn}>
|
|
106
|
-
<InterceptContext.Consumer>
|
|
107
|
-
{captureContextFn}
|
|
108
|
-
</InterceptContext.Consumer>
|
|
109
|
-
</InterceptCache>
|
|
110
|
-
</InterceptData>,
|
|
111
|
-
);
|
|
112
|
-
|
|
113
|
-
// Assert
|
|
114
|
-
expect(captureContextFn).toHaveBeenCalledWith(
|
|
115
|
-
expect.objectContaining({
|
|
116
|
-
MY_HANDLER: {
|
|
117
|
-
fulfillRequest: fulfillRequestFn,
|
|
118
|
-
shouldRefreshCache: shouldRefreshCacheFn,
|
|
119
|
-
getEntry: getEntryFn,
|
|
120
|
-
},
|
|
121
|
-
}),
|
|
122
|
-
);
|
|
123
|
-
});
|
|
124
|
-
});
|