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