@khanacademy/wonder-blocks-testing 8.0.20 → 9.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 +20 -0
- package/dist/es/index.js +417 -24
- package/dist/harness/adapters/adapters.d.ts +1 -0
- package/dist/harness/adapters/{portal.js.flow → ssr.d.ts} +4 -9
- package/dist/harness/test-harness.d.ts +1 -0
- package/dist/harness/types.d.ts +1 -1
- package/dist/index.js +416 -24
- package/dist/mock-requester.d.ts +2 -2
- package/package.json +3 -3
- package/src/fetch/fetch-request-matches-mock.ts +2 -4
- package/src/fetch/mock-fetch.ts +1 -1
- package/src/fixtures/__tests__/fixtures.test.tsx +9 -14
- package/src/fixtures/fixtures.basic.stories.tsx +9 -3
- package/src/fixtures/fixtures.tsx +1 -2
- package/src/gql/mock-gql-fetch.ts +1 -1
- package/src/harness/__tests__/hook-harness.test.ts +4 -2
- package/src/harness/__tests__/render-adapters.test.tsx +22 -12
- package/src/harness/__tests__/test-harness.test.ts +4 -2
- package/src/harness/__tests__/types.typestest.tsx +6 -13
- package/src/harness/adapters/__tests__/data.test.tsx +12 -4
- package/src/harness/adapters/__tests__/ssr.test.tsx +41 -0
- package/src/harness/adapters/adapters.ts +3 -3
- package/src/harness/adapters/css.tsx +1 -3
- package/src/harness/adapters/router.tsx +5 -19
- package/src/harness/adapters/ssr.tsx +33 -0
- package/src/harness/{make-hook-harness.ts → make-hook-harness.tsx} +3 -2
- package/src/harness/{render-adapters.ts → render-adapters.tsx} +1 -2
- package/src/harness/types.ts +1 -3
- package/src/mock-requester.ts +4 -11
- package/src/respond-with.ts +2 -1
- package/src/settle-controller.ts +2 -5
- package/src/settle-signal.ts +0 -2
- package/tsconfig-build.tsbuildinfo +1 -1
- package/dist/fetch/fetch-request-matches-mock.js.flow +0 -16
- package/dist/fetch/mock-fetch.js.flow +0 -12
- package/dist/fetch/types.js.flow +0 -17
- package/dist/fixtures/fixtures.basic.stories.js.flow +0 -21
- package/dist/fixtures/fixtures.defaultwrapper.stories.js.flow +0 -17
- package/dist/fixtures/fixtures.js.flow +0 -24
- package/dist/fixtures/types.js.flow +0 -35
- package/dist/gql/gql-request-matches-mock.js.flow +0 -14
- package/dist/gql/mock-gql-fetch.js.flow +0 -12
- package/dist/gql/types.js.flow +0 -36
- package/dist/harness/adapters/adapters.js.flow +0 -28
- package/dist/harness/adapters/css.js.flow +0 -21
- package/dist/harness/adapters/data.js.flow +0 -29
- package/dist/harness/adapters/router.js.flow +0 -114
- package/dist/harness/hook-harness.js.flow +0 -22
- package/dist/harness/make-hook-harness.js.flow +0 -27
- package/dist/harness/make-test-harness.js.flow +0 -22
- package/dist/harness/render-adapters.js.flow +0 -17
- package/dist/harness/test-harness.js.flow +0 -74
- package/dist/harness/types.js.flow +0 -57
- package/dist/index.js.flow +0 -25
- package/dist/mock-requester.js.flow +0 -21
- package/dist/respond-with.js.flow +0 -88
- package/dist/response-impl.js.flow +0 -7
- package/dist/settle-controller.js.flow +0 -25
- package/dist/settle-signal.js.flow +0 -25
- package/dist/types.js.flow +0 -40
- package/src/harness/adapters/adapters.js.flow +0 -28
- package/src/harness/adapters/router.jsx.flow +0 -112
- package/src/harness/make-test-harness.js.flow +0 -22
- package/src/harness/types.js.flow +0 -57
|
@@ -4,7 +4,7 @@ import {renderAdapters} from "../render-adapters";
|
|
|
4
4
|
import type {TestHarnessAdapter, TestHarnessConfigs} from "../types";
|
|
5
5
|
|
|
6
6
|
describe("#renderAdapters", () => {
|
|
7
|
-
it("should
|
|
7
|
+
it("should render children if no adapters", () => {
|
|
8
8
|
// Arrange
|
|
9
9
|
const children = <div>Adapt me!</div>;
|
|
10
10
|
|
|
@@ -12,7 +12,13 @@ describe("#renderAdapters", () => {
|
|
|
12
12
|
const result = renderAdapters({}, {}, children);
|
|
13
13
|
|
|
14
14
|
// Assert
|
|
15
|
-
expect(result).
|
|
15
|
+
expect(result).toMatchInlineSnapshot(`
|
|
16
|
+
<React.Fragment>
|
|
17
|
+
<div>
|
|
18
|
+
Adapt me!
|
|
19
|
+
</div>
|
|
20
|
+
</React.Fragment>
|
|
21
|
+
`);
|
|
16
22
|
});
|
|
17
23
|
|
|
18
24
|
it("should invoke the adapter with its corresponding config", () => {
|
|
@@ -38,10 +44,8 @@ describe("#renderAdapters", () => {
|
|
|
38
44
|
it("should render each adapter and the children", () => {
|
|
39
45
|
// Arrange
|
|
40
46
|
const children = "Adapt me!";
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
return `${conf}:${c}`;
|
|
44
|
-
};
|
|
47
|
+
const adapter: TestHarnessAdapter<string> = (c: any, conf: any) =>
|
|
48
|
+
`${conf}:${c}` as any;
|
|
45
49
|
const adapters = {
|
|
46
50
|
adapterA: adapter,
|
|
47
51
|
adapterB: adapter,
|
|
@@ -57,16 +61,18 @@ describe("#renderAdapters", () => {
|
|
|
57
61
|
const result = renderAdapters(adapters, configs, children);
|
|
58
62
|
|
|
59
63
|
// Assert
|
|
60
|
-
expect(result).toMatchInlineSnapshot(`
|
|
64
|
+
expect(result).toMatchInlineSnapshot(`
|
|
65
|
+
<React.Fragment>
|
|
66
|
+
C:B:A:Adapt me!
|
|
67
|
+
</React.Fragment>
|
|
68
|
+
`);
|
|
61
69
|
});
|
|
62
70
|
|
|
63
71
|
it("should skip adapters where the corresponding config is null", () => {
|
|
64
72
|
// Arrange
|
|
65
73
|
const children = "Adapt me!";
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
return `${conf}:${c}`;
|
|
69
|
-
};
|
|
74
|
+
const adapter: TestHarnessAdapter<string> = (c: any, conf: any) =>
|
|
75
|
+
`${conf}:${c}` as any;
|
|
70
76
|
const adapters = {
|
|
71
77
|
adapterA: adapter,
|
|
72
78
|
adapterB: adapter,
|
|
@@ -82,6 +88,10 @@ describe("#renderAdapters", () => {
|
|
|
82
88
|
const result = renderAdapters(adapters, configs, children);
|
|
83
89
|
|
|
84
90
|
// Assert
|
|
85
|
-
expect(result).toMatchInlineSnapshot(`
|
|
91
|
+
expect(result).toMatchInlineSnapshot(`
|
|
92
|
+
<React.Fragment>
|
|
93
|
+
C:A:Adapt me!
|
|
94
|
+
</React.Fragment>
|
|
95
|
+
`);
|
|
86
96
|
});
|
|
87
97
|
});
|
|
@@ -35,7 +35,8 @@ describe("#testHarness", () => {
|
|
|
35
35
|
const config = {
|
|
36
36
|
router: "/boo",
|
|
37
37
|
} as const;
|
|
38
|
-
// @ts-expect-error
|
|
38
|
+
// @ts-expect-error We know harnessFake isn't real, we add it in the
|
|
39
|
+
// mocks at the top of this file.
|
|
39
40
|
const [{harnessFake}, {testHarness}] = await ws.isolateModules(() =>
|
|
40
41
|
Promise.all([
|
|
41
42
|
import("../make-test-harness"),
|
|
@@ -56,7 +57,8 @@ describe("#testHarness", () => {
|
|
|
56
57
|
const config = {
|
|
57
58
|
router: "/boo",
|
|
58
59
|
} as const;
|
|
59
|
-
// @ts-expect-error
|
|
60
|
+
// @ts-expect-error We know harnessFake isn't real, we add it in the
|
|
61
|
+
// mocks at the top of this file.
|
|
60
62
|
const [{returnValueFake}, {testHarness}] = await ws.isolateModules(() =>
|
|
61
63
|
Promise.all([
|
|
62
64
|
import("../make-test-harness"),
|
|
@@ -12,13 +12,10 @@ import type {
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
//> should assert type of config.
|
|
15
|
-
// @ts-expect-error
|
|
16
|
-
((
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
// $FlowExpectedError[incompatible-cast]
|
|
20
|
-
config: number,
|
|
21
|
-
): React.ReactElement<any> => <div />) as TestHarnessAdapter<string>;
|
|
15
|
+
// @ts-expect-error TConfig is string, but we typed config as a number
|
|
16
|
+
((children: React.ReactNode, config: number): React.ReactElement<any> => (
|
|
17
|
+
<div />
|
|
18
|
+
)) as TestHarnessAdapter<string>;
|
|
22
19
|
//<
|
|
23
20
|
|
|
24
21
|
//> should work for correct definition
|
|
@@ -36,10 +33,8 @@ import type {
|
|
|
36
33
|
//<
|
|
37
34
|
|
|
38
35
|
//> should assert if adapter is not Adapter<TConfig>
|
|
39
|
-
// @ts-expect-error
|
|
36
|
+
// @ts-expect-error String is not a adapter function
|
|
40
37
|
({
|
|
41
|
-
// String is not a adapter function
|
|
42
|
-
// $FlowExpectedError[incompatible-cast]
|
|
43
38
|
adapterString: "string",
|
|
44
39
|
} as TestHarnessAdapters);
|
|
45
40
|
//<
|
|
@@ -100,11 +95,9 @@ const adapters = {
|
|
|
100
95
|
//<
|
|
101
96
|
|
|
102
97
|
//> should assert if config does not match adapter config
|
|
103
|
-
// @ts-expect-error:
|
|
98
|
+
// @ts-expect-error: the config type here is a number, not a string
|
|
104
99
|
({
|
|
105
100
|
adapterA: "a string, this is correct",
|
|
106
|
-
// the config type here is a number, not a string
|
|
107
|
-
// $FlowExpectedError[incompatible-cast]
|
|
108
101
|
adapterB: "a string, but it should be a number",
|
|
109
102
|
} as TestHarnessConfigs<typeof adapters>);
|
|
110
103
|
//<
|
|
@@ -21,8 +21,12 @@ describe("WonderBlocksData.adapter", () => {
|
|
|
21
21
|
const TestFixture = () => {
|
|
22
22
|
const [result] = useCachedEffect("ID", jest.fn());
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
return (
|
|
25
|
+
<div>
|
|
26
|
+
CONTENT:{" "}
|
|
27
|
+
{result.status === "success" ? result.data : undefined}
|
|
28
|
+
</div>
|
|
29
|
+
);
|
|
26
30
|
};
|
|
27
31
|
|
|
28
32
|
// Act
|
|
@@ -43,8 +47,12 @@ describe("WonderBlocksData.adapter", () => {
|
|
|
43
47
|
const TestFixture = () => {
|
|
44
48
|
const [result] = useCachedEffect("ID", jest.fn());
|
|
45
49
|
|
|
46
|
-
|
|
47
|
-
|
|
50
|
+
return (
|
|
51
|
+
<div>
|
|
52
|
+
CONTENT:
|
|
53
|
+
{result.status === "success" ? result.data : undefined}
|
|
54
|
+
</div>
|
|
55
|
+
);
|
|
48
56
|
};
|
|
49
57
|
|
|
50
58
|
// Act
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import {render, screen} from "@testing-library/react";
|
|
3
|
+
|
|
4
|
+
import * as WBCore from "@khanacademy/wonder-blocks-core";
|
|
5
|
+
|
|
6
|
+
import * as SSR from "../ssr";
|
|
7
|
+
|
|
8
|
+
jest.mock("@khanacademy/wonder-stuff-core", () => ({
|
|
9
|
+
...jest.requireActual("@khanacademy/wonder-stuff-core"),
|
|
10
|
+
RenderStateRoot: (props: any) => <div {...props} />,
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
describe("SSR.adapter", () => {
|
|
14
|
+
it("should render the RenderStateRoot", () => {
|
|
15
|
+
// Arrange
|
|
16
|
+
const children = <div>CHILDREN!</div>;
|
|
17
|
+
const renderStateRootSpy = jest.spyOn(WBCore, "RenderStateRoot");
|
|
18
|
+
|
|
19
|
+
// Act
|
|
20
|
+
render(SSR.adapter(children, true));
|
|
21
|
+
|
|
22
|
+
// Assert
|
|
23
|
+
expect(renderStateRootSpy).toHaveBeenCalledWith(
|
|
24
|
+
{
|
|
25
|
+
children,
|
|
26
|
+
},
|
|
27
|
+
{},
|
|
28
|
+
);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("should render the children correctly", () => {
|
|
32
|
+
// Arrange
|
|
33
|
+
const children = <div>CHILDREN!</div>;
|
|
34
|
+
|
|
35
|
+
// Act
|
|
36
|
+
render(SSR.adapter(children, true));
|
|
37
|
+
|
|
38
|
+
// Assert
|
|
39
|
+
expect(screen.getByText("CHILDREN!")).toBeInTheDocument();
|
|
40
|
+
});
|
|
41
|
+
});
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
// WARNING: If you modify this file you may need to update adapters.js.flow
|
|
2
|
-
// to ensure that the Flow types are still correct.
|
|
3
|
-
|
|
4
1
|
import * as css from "./css";
|
|
5
2
|
import * as data from "./data";
|
|
6
3
|
import * as portal from "./portal";
|
|
7
4
|
import * as router from "./router";
|
|
5
|
+
import * as ssr from "./ssr";
|
|
8
6
|
|
|
9
7
|
import type {TestHarnessConfigs} from "../types";
|
|
10
8
|
|
|
@@ -22,6 +20,7 @@ export const DefaultAdapters = {
|
|
|
22
20
|
data: data.adapter,
|
|
23
21
|
portal: portal.adapter,
|
|
24
22
|
router: router.adapter,
|
|
23
|
+
ssr: ssr.adapter,
|
|
25
24
|
} as const;
|
|
26
25
|
|
|
27
26
|
/**
|
|
@@ -32,4 +31,5 @@ export const DefaultConfigs: TestHarnessConfigs<typeof DefaultAdapters> = {
|
|
|
32
31
|
data: data.defaultConfig,
|
|
33
32
|
portal: portal.defaultConfig,
|
|
34
33
|
router: router.defaultConfig,
|
|
34
|
+
ssr: ssr.defaultConfig,
|
|
35
35
|
} as const;
|
|
@@ -40,9 +40,7 @@ const normalizeConfig = (
|
|
|
40
40
|
return config;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
// that `config` can be.
|
|
45
|
-
return {classes: [], style: config};
|
|
43
|
+
return {classes: [], style: config as CSSProperties};
|
|
46
44
|
}
|
|
47
45
|
|
|
48
46
|
throw new Error(`Invalid config: ${config}`);
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
// WARNING: If you modify this file you may need to update router.jsx.flow
|
|
2
|
-
// to ensure that the Flow types are still correct.
|
|
3
1
|
import * as React from "react";
|
|
4
2
|
|
|
5
3
|
import {StaticRouter, MemoryRouter, Route, Switch} from "react-router-dom";
|
|
@@ -100,8 +98,7 @@ const maybeWithRoute = (
|
|
|
100
98
|
path?: string | null,
|
|
101
99
|
): React.ReactElement => {
|
|
102
100
|
if (path == null) {
|
|
103
|
-
|
|
104
|
-
return children;
|
|
101
|
+
return <>{children}</>;
|
|
105
102
|
}
|
|
106
103
|
|
|
107
104
|
return (
|
|
@@ -144,14 +141,12 @@ export const adapter: TestHarnessAdapter<Config> = (
|
|
|
144
141
|
|
|
145
142
|
// Wrap children with the various contexts and routes, as per the config.
|
|
146
143
|
const wrappedWithRoute = maybeWithRoute(children, config.path);
|
|
147
|
-
|
|
148
|
-
if (config.forceStatic) {
|
|
144
|
+
if ("forceStatic" in config && config.forceStatic) {
|
|
149
145
|
/**
|
|
150
146
|
* There may be times (SSR testing comes to mind) where we will be
|
|
151
147
|
* really strict about not permitting client-side navigation events.
|
|
152
148
|
*/
|
|
153
149
|
return (
|
|
154
|
-
// @ts-expect-error [FEI-5019] - TS2339 - Property 'location' does not exist on type 'Readonly<{ initialEntries: LocationDescriptor<unknown>[] | undefined; initialIndex?: number | undefined; getUserConfirmation?: ((message: string, callback: (ok: boolean) => void) => void) | undefined; path?: string | undefined; } | { ...; } | { ...; }>'.
|
|
155
150
|
<StaticRouter location={config.location} context={{}}>
|
|
156
151
|
{wrappedWithRoute}
|
|
157
152
|
</StaticRouter>
|
|
@@ -166,10 +161,8 @@ export const adapter: TestHarnessAdapter<Config> = (
|
|
|
166
161
|
*
|
|
167
162
|
* First, the easy one.
|
|
168
163
|
*/
|
|
169
|
-
|
|
170
|
-
if (typeof config.location !== "undefined") {
|
|
164
|
+
if ("location" in config && config.location !== undefined) {
|
|
171
165
|
return (
|
|
172
|
-
// @ts-expect-error [FEI-5019] - TS2339 - Property 'location' does not exist on type 'Readonly<{ initialEntries: LocationDescriptor<unknown>[] | undefined; initialIndex?: number | undefined; getUserConfirmation?: ((message: string, callback: (ok: boolean) => void) => void) | undefined; path?: string | undefined; } | { ...; } | { ...; }>'.
|
|
173
166
|
<MemoryRouter initialEntries={[config.location]}>
|
|
174
167
|
{wrappedWithRoute}
|
|
175
168
|
</MemoryRouter>
|
|
@@ -180,8 +173,7 @@ export const adapter: TestHarnessAdapter<Config> = (
|
|
|
180
173
|
* If it's not the easy one, it should be the complex one.
|
|
181
174
|
* Let's make sure we have good data (also keeps TypeScript happy).
|
|
182
175
|
*/
|
|
183
|
-
|
|
184
|
-
if (typeof config.initialEntries === "undefined") {
|
|
176
|
+
if (!("initialEntries" in config) || config.initialEntries === undefined) {
|
|
185
177
|
throw new Error(
|
|
186
178
|
"A location or initial history entries must be provided.",
|
|
187
179
|
);
|
|
@@ -193,25 +185,19 @@ export const adapter: TestHarnessAdapter<Config> = (
|
|
|
193
185
|
* we want, so let's ensure we always have our default location at least.
|
|
194
186
|
*/
|
|
195
187
|
const entries =
|
|
196
|
-
// @ts-expect-error [FEI-5019] - TS2339 - Property 'initialEntries' does not exist on type 'Readonly<{ initialEntries: LocationDescriptor<unknown>[] | undefined; initialIndex?: number | undefined; getUserConfirmation?: ((message: string, callback: (ok: boolean) => void) => void) | undefined; path?: string | undefined; } | { ...; } | { ...; }>'.
|
|
197
188
|
config.initialEntries.length === 0
|
|
198
189
|
? [defaultConfig.location]
|
|
199
|
-
:
|
|
200
|
-
config.initialEntries;
|
|
190
|
+
: config.initialEntries;
|
|
201
191
|
|
|
202
192
|
// Memory router doesn't allow us to pass maybe types in its TypeScript types.
|
|
203
193
|
// So let's build props then spread them.
|
|
204
194
|
const routerProps: MemoryRouterProps = {
|
|
205
195
|
initialEntries: entries,
|
|
206
196
|
};
|
|
207
|
-
// @ts-expect-error [FEI-5019] - TS2339 - Property 'initialIndex' does not exist on type 'Readonly<{ initialEntries: LocationDescriptor<unknown>[] | undefined; initialIndex?: number | undefined; getUserConfirmation?: ((message: string, callback: (ok: boolean) => void) => void) | undefined; path?: string | undefined; } | { ...; } | { ...; }>'.
|
|
208
197
|
if (config.initialIndex != null) {
|
|
209
|
-
// @ts-expect-error [FEI-5019] - TS2339 - Property 'initialIndex' does not exist on type 'Readonly<{ initialEntries: LocationDescriptor<unknown>[] | undefined; initialIndex?: number | undefined; getUserConfirmation?: ((message: string, callback: (ok: boolean) => void) => void) | undefined; path?: string | undefined; } | { ...; } | { ...; }>'.
|
|
210
198
|
routerProps.initialIndex = config.initialIndex;
|
|
211
199
|
}
|
|
212
|
-
// @ts-expect-error [FEI-5019] - TS2339 - Property 'getUserConfirmation' does not exist on type 'Readonly<{ initialEntries: LocationDescriptor<unknown>[] | undefined; initialIndex?: number | undefined; getUserConfirmation?: ((message: string, callback: (ok: boolean) => void) => void) | undefined; path?: string | undefined; } | { ...; } | { ...; }>'.
|
|
213
200
|
if (config.getUserConfirmation != null) {
|
|
214
|
-
// @ts-expect-error [FEI-5019] - TS2339 - Property 'getUserConfirmation' does not exist on type 'Readonly<{ initialEntries: LocationDescriptor<unknown>[] | undefined; initialIndex?: number | undefined; getUserConfirmation?: ((message: string, callback: (ok: boolean) => void) => void) | undefined; path?: string | undefined; } | { ...; } | { ...; }>'.
|
|
215
201
|
routerProps.getUserConfirmation = config.getUserConfirmation;
|
|
216
202
|
}
|
|
217
203
|
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import {KindError, Errors} from "@khanacademy/wonder-stuff-core";
|
|
3
|
+
import {RenderStateRoot} from "@khanacademy/wonder-blocks-core";
|
|
4
|
+
|
|
5
|
+
import type {TestHarnessAdapter} from "../types";
|
|
6
|
+
|
|
7
|
+
// We only support one configuration for this adapter; the default
|
|
8
|
+
// behavior - it's either on or off.
|
|
9
|
+
type Config = true | null;
|
|
10
|
+
|
|
11
|
+
// The default configuration is off since this will likely cause state changes
|
|
12
|
+
// and add Testing Library act/waitFor calls in tests using the harness when
|
|
13
|
+
// its enabled.
|
|
14
|
+
export const defaultConfig: Config = null;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Test harness adapter for supporting portals.
|
|
18
|
+
*
|
|
19
|
+
* Some components rely on rendering with a React Portal. This adapter ensures
|
|
20
|
+
* that the DOM contains a mounting point for the portal with the expected
|
|
21
|
+
* identifier.
|
|
22
|
+
*/
|
|
23
|
+
export const adapter: TestHarnessAdapter<Config> = (
|
|
24
|
+
children: React.ReactNode,
|
|
25
|
+
config: Config,
|
|
26
|
+
): React.ReactElement<any> => {
|
|
27
|
+
if (config !== true) {
|
|
28
|
+
throw new KindError("Unexpected configuraiton", Errors.InvalidInput, {
|
|
29
|
+
metadata: {config},
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
return <RenderStateRoot>{children}</RenderStateRoot>;
|
|
33
|
+
};
|
|
@@ -4,8 +4,9 @@ import {makeTestHarness} from "./make-test-harness";
|
|
|
4
4
|
|
|
5
5
|
import type {TestHarnessAdapters, TestHarnessConfigs} from "./types";
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
const HookHarness = ({
|
|
8
|
+
children,
|
|
9
|
+
}: React.PropsWithChildren<unknown>): React.ReactElement => <>{children}</>;
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Create a test harness method for use with React hooks.
|
|
@@ -22,6 +22,5 @@ export const renderAdapters = <TAdapters extends TestHarnessAdapters>(
|
|
|
22
22
|
currentChildren = adapter(currentChildren, config);
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
|
-
|
|
26
|
-
return currentChildren;
|
|
25
|
+
return <>{currentChildren}</>;
|
|
27
26
|
};
|
package/src/harness/types.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
// WARNING: If you modify this file you may need to update types.js.flow
|
|
2
|
-
// to ensure that the Flow types are still correct.
|
|
3
1
|
import * as React from "react";
|
|
4
2
|
|
|
5
3
|
/**
|
|
@@ -8,7 +6,7 @@ import * as React from "react";
|
|
|
8
6
|
export type TestHarnessAdapter<TConfig> = (
|
|
9
7
|
children: React.ReactNode,
|
|
10
8
|
config: TConfig,
|
|
11
|
-
) => React.ReactElement
|
|
9
|
+
) => React.ReactElement;
|
|
12
10
|
|
|
13
11
|
/**
|
|
14
12
|
* A general map of adapters by their identifiers.
|
package/src/mock-requester.ts
CHANGED
|
@@ -4,23 +4,17 @@ import type {OperationMock, OperationMatcher, MockFn} from "./types";
|
|
|
4
4
|
/**
|
|
5
5
|
* A generic mock request function for using when mocking fetch or gqlFetch.
|
|
6
6
|
*/
|
|
7
|
-
export const mockRequester = <
|
|
8
|
-
TOperationType,
|
|
9
|
-
TOperationMock extends OperationMock<TOperationType> = OperationMock<TOperationType>,
|
|
10
|
-
>(
|
|
7
|
+
export const mockRequester = <TOperationType>(
|
|
11
8
|
operationMatcher: OperationMatcher<any>,
|
|
12
|
-
operationToString: (
|
|
13
|
-
operationMock: TOperationMock,
|
|
14
|
-
...args: Array<any>
|
|
15
|
-
) => string,
|
|
9
|
+
operationToString: (...args: Array<any>) => string,
|
|
16
10
|
): MockFn<TOperationType> => {
|
|
17
11
|
// We want this to work in jest and in fixtures to make life easy for folks.
|
|
18
12
|
// This is the array of mocked operations that we will traverse and
|
|
19
13
|
// manipulate.
|
|
20
14
|
const mocks: Array<OperationMock<any>> = [];
|
|
21
15
|
|
|
22
|
-
// What we return has to be a drop in for the
|
|
23
|
-
//
|
|
16
|
+
// What we return has to be a drop in replacement for the mocked function
|
|
17
|
+
// which is how folks will then use this mock.
|
|
24
18
|
const mockFn: MockFn<TOperationType> = (
|
|
25
19
|
...args: Array<any>
|
|
26
20
|
): Promise<Response> => {
|
|
@@ -38,7 +32,6 @@ export const mockRequester = <
|
|
|
38
32
|
|
|
39
33
|
// Default is to reject with some helpful info on what request
|
|
40
34
|
// we rejected.
|
|
41
|
-
// @ts-expect-error [FEI-5019] - TS2556 - A spread argument must either have a tuple type or be passed to a rest parameter.
|
|
42
35
|
const operation = operationToString(...args);
|
|
43
36
|
return Promise.reject(
|
|
44
37
|
new Error(`No matching mock response found for request:
|
package/src/respond-with.ts
CHANGED
|
@@ -228,7 +228,8 @@ const makeMockResponse = (
|
|
|
228
228
|
if (process.env.NODE_ENV !== "production") {
|
|
229
229
|
// If we're not in production, give an immediate signal that the
|
|
230
230
|
// dev forgot to support this new type.
|
|
231
|
-
// @ts-expect-error
|
|
231
|
+
// @ts-expect-error TS knows we can't get here and so sees
|
|
232
|
+
// `response` as `never`.
|
|
232
233
|
throw new Error(`Unknown response type: ${response.type}`);
|
|
233
234
|
}
|
|
234
235
|
// Production; assume a rejection.
|
package/src/settle-controller.ts
CHANGED
|
@@ -4,10 +4,7 @@ import {SettleSignal} from "./settle-signal";
|
|
|
4
4
|
* A controller for the `RespondWith` API to control response settlement.
|
|
5
5
|
*/
|
|
6
6
|
export class SettleController {
|
|
7
|
-
|
|
8
|
-
// TODO(FEI-5000): Replace `_` with `#` after all code is on TypeScript.
|
|
9
|
-
// @ts-expect-error [FEI-5019] - TS2564 - Property '#settleFn' has no initializer and is not definitely assigned in the constructor.
|
|
10
|
-
private _settleFn: () => void;
|
|
7
|
+
private _settleFn: undefined | (() => void);
|
|
11
8
|
private _signal: SettleSignal;
|
|
12
9
|
|
|
13
10
|
constructor() {
|
|
@@ -32,6 +29,6 @@ export class SettleController {
|
|
|
32
29
|
* @throws {Error} if the signal has already been settled.
|
|
33
30
|
*/
|
|
34
31
|
settle(): void {
|
|
35
|
-
this._settleFn();
|
|
32
|
+
this._settleFn?.();
|
|
36
33
|
}
|
|
37
34
|
}
|
package/src/settle-signal.ts
CHANGED
|
@@ -5,8 +5,6 @@
|
|
|
5
5
|
* complex test scenarios.
|
|
6
6
|
*/
|
|
7
7
|
export class SettleSignal extends EventTarget {
|
|
8
|
-
// `flowgen` can't handle `#` so we're using `_` and TypeScript `private` access modifier.
|
|
9
|
-
// TODO(FEI-5000): Replace `_` with `#` after all code is on TypeScript.
|
|
10
8
|
private _settled = false;
|
|
11
9
|
|
|
12
10
|
constructor(
|