@khanacademy/wonder-blocks-testing 4.0.3 → 5.0.1
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 +212 -26
- package/dist/index.js +589 -62
- package/package.json +5 -3
- package/src/__docs__/_overview_.stories.mdx +3 -4
- package/src/__docs__/_overview_fixtures.stories.mdx +22 -0
- package/src/__docs__/_overview_mocking.stories.mdx +14 -0
- package/src/__docs__/_overview_test_harness.stories.mdx +18 -0
- package/src/__docs__/exports.fixture-adapters.stories.mdx +49 -0
- package/src/__docs__/exports.fixtures.stories.mdx +53 -0
- package/src/__docs__/exports.harness-adapters.stories.mdx +187 -0
- package/src/__docs__/exports.hook-harness.stories.mdx +22 -0
- package/src/__docs__/exports.make-hook-harness.stories.mdx +25 -0
- package/src/__docs__/exports.make-test-harness.stories.mdx +28 -0
- package/src/__docs__/exports.mock-fetch.stories.mdx +40 -0
- package/src/__docs__/exports.mock-gql-fetch.stories.mdx +13 -8
- package/src/__docs__/exports.respond-with.stories.mdx +54 -8
- package/src/__docs__/exports.setup-fixtures.stories.mdx +22 -0
- package/src/__docs__/exports.test-harness.stories.mdx +23 -0
- package/src/__docs__/types.custom-mount-props.stories.mdx +35 -0
- package/src/__docs__/types.fetch-mock-fn.stories.mdx +22 -0
- package/src/__docs__/types.fetch-mock-operation.stories.mdx +18 -0
- package/src/__docs__/types.fixtures-adapter-factory.stories.mdx +23 -0
- package/src/__docs__/types.fixtures-adapter-fixture-options.stories.mdx +35 -0
- package/src/__docs__/types.fixtures-adapter-group-options.stories.mdx +37 -0
- package/src/__docs__/types.fixtures-adapter-group.stories.mdx +43 -0
- package/src/__docs__/types.fixtures-adapter-options.stories.mdx +21 -0
- package/src/__docs__/types.fixtures-adapter.stories.mdx +35 -0
- package/src/__docs__/types.fixtures-configuration.stories.mdx +35 -0
- package/src/__docs__/types.fixtures-options.stories.mdx +51 -0
- package/src/__docs__/types.get-props-options.stories.mdx +25 -0
- package/src/__docs__/types.gql-fetch-mock-fn.stories.mdx +27 -0
- package/src/__docs__/types.gql-mock-operation.stories.mdx +26 -0
- package/src/__docs__/types.mock-response.stories.mdx +18 -0
- package/src/__docs__/types.test-harness-adapter.stories.mdx +21 -0
- package/src/__docs__/types.test-harness-adapters.stories.mdx +46 -0
- package/src/__docs__/types.test-harness-config.stories.mdx +18 -0
- package/src/__docs__/types.test-harness-configs.stories.mdx +59 -0
- package/src/fetch/types.js +0 -3
- package/src/fixtures/adapters/adapter-group.js +11 -11
- package/src/fixtures/adapters/adapter.js +8 -8
- package/src/fixtures/adapters/storybook.js +11 -8
- package/src/fixtures/fixtures.basic.stories.js +6 -2
- package/src/fixtures/fixtures.defaultwrapper.stories.js +6 -2
- package/src/fixtures/setup.js +8 -4
- package/src/fixtures/types.js +27 -16
- package/src/gql/types.js +1 -3
- package/src/harness/__tests__/hook-harness.test.js +72 -0
- package/src/harness/__tests__/make-hook-harness.test.js +94 -0
- package/src/harness/__tests__/make-test-harness.test.js +190 -0
- package/src/harness/__tests__/render-adapters.test.js +88 -0
- package/src/harness/__tests__/test-harness.test.js +74 -0
- package/src/harness/__tests__/types.flowtest.js +115 -0
- package/src/harness/adapters/__tests__/__snapshots__/router.test.js.snap +5 -0
- package/src/harness/adapters/__tests__/css.test.js +96 -0
- package/src/harness/adapters/__tests__/data.test.js +66 -0
- package/src/harness/adapters/__tests__/portal.test.js +31 -0
- package/src/harness/adapters/__tests__/router.test.js +233 -0
- package/src/harness/adapters/adapters.js +33 -0
- package/src/harness/adapters/css.js +65 -0
- package/src/harness/adapters/data.js +46 -0
- package/src/harness/adapters/portal.js +26 -0
- package/src/harness/adapters/router.js +206 -0
- package/src/harness/hook-harness.js +23 -0
- package/src/harness/make-hook-harness.js +39 -0
- package/src/harness/make-test-harness.js +68 -0
- package/src/harness/render-adapters.js +27 -0
- package/src/harness/test-harness.js +24 -0
- package/src/harness/types.js +57 -0
- package/src/index.js +22 -18
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import {Route} from "react-router-dom";
|
|
4
|
+
import {render} from "@testing-library/react";
|
|
5
|
+
|
|
6
|
+
import * as RA from "../render-adapters.js";
|
|
7
|
+
import {makeTestHarness} from "../make-test-harness.js";
|
|
8
|
+
import {DefaultConfigs, DefaultAdapters} from "../adapters/adapters.js";
|
|
9
|
+
|
|
10
|
+
describe("#makeTestHarness", () => {
|
|
11
|
+
it("should return a function", () => {
|
|
12
|
+
// Arrange
|
|
13
|
+
|
|
14
|
+
// Act
|
|
15
|
+
const result = makeTestHarness(DefaultAdapters, DefaultConfigs);
|
|
16
|
+
|
|
17
|
+
// Assert
|
|
18
|
+
expect(result).toBeFunction();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
describe("returned function", () => {
|
|
22
|
+
describe("returned component", () => {
|
|
23
|
+
it("should have displayName generated from component displayName", () => {
|
|
24
|
+
// Arrange
|
|
25
|
+
const testHarness = makeTestHarness(
|
|
26
|
+
DefaultAdapters,
|
|
27
|
+
DefaultConfigs,
|
|
28
|
+
);
|
|
29
|
+
const Component = () => <div>test</div>;
|
|
30
|
+
Component.displayName = "MyNamedComponent";
|
|
31
|
+
|
|
32
|
+
// Act
|
|
33
|
+
const {displayName: result} = testHarness(Component);
|
|
34
|
+
|
|
35
|
+
// Assert
|
|
36
|
+
expect(result).toBe("testHarness(MyNamedComponent)");
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("should have displayName generated from component name in absence of displayName", () => {
|
|
40
|
+
// Arrange
|
|
41
|
+
const testHarness = makeTestHarness(
|
|
42
|
+
DefaultAdapters,
|
|
43
|
+
DefaultConfigs,
|
|
44
|
+
);
|
|
45
|
+
const Component = function MyFunctionNamedComponent() {
|
|
46
|
+
return <div>test</div>;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// Act
|
|
50
|
+
const {displayName: result} = testHarness(Component);
|
|
51
|
+
|
|
52
|
+
// Assert
|
|
53
|
+
expect(result).toBe("testHarness(MyFunctionNamedComponent)");
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("should have default displayName in absence of displayName or name", () => {
|
|
57
|
+
// Arrange
|
|
58
|
+
const testHarness = makeTestHarness(
|
|
59
|
+
DefaultAdapters,
|
|
60
|
+
DefaultConfigs,
|
|
61
|
+
);
|
|
62
|
+
const Component = () => <div>test</div>;
|
|
63
|
+
|
|
64
|
+
// Act
|
|
65
|
+
const {displayName: result} = testHarness(Component);
|
|
66
|
+
|
|
67
|
+
// Assert
|
|
68
|
+
expect(result).toBe("testHarness(Component)");
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
describe("if config is omitted", () => {
|
|
72
|
+
it("should render with defaults", () => {
|
|
73
|
+
// Arrange
|
|
74
|
+
const testHarness = makeTestHarness(
|
|
75
|
+
DefaultAdapters,
|
|
76
|
+
DefaultConfigs,
|
|
77
|
+
);
|
|
78
|
+
const Component = () => <div>test</div>;
|
|
79
|
+
const renderSpy = jest.spyOn(RA, "renderAdapters");
|
|
80
|
+
|
|
81
|
+
// Act
|
|
82
|
+
const HarnessedComponent = testHarness(Component);
|
|
83
|
+
render(<HarnessedComponent />);
|
|
84
|
+
|
|
85
|
+
// Assert
|
|
86
|
+
expect(renderSpy).toHaveBeenCalledWith(
|
|
87
|
+
DefaultAdapters,
|
|
88
|
+
DefaultConfigs,
|
|
89
|
+
expect.anything(),
|
|
90
|
+
);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("should render harnessed component", () => {
|
|
94
|
+
// Arrange
|
|
95
|
+
const testHarness = makeTestHarness(
|
|
96
|
+
DefaultAdapters,
|
|
97
|
+
DefaultConfigs,
|
|
98
|
+
);
|
|
99
|
+
const Component = (props: {|text: string|}) => (
|
|
100
|
+
<div>{props.text}</div>
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
// Act
|
|
104
|
+
const HarnessedComponent = testHarness(Component, {
|
|
105
|
+
// Need to provide a key as this a snapshot test that will
|
|
106
|
+
// break because of MemoryRouter using random keys
|
|
107
|
+
// otherwise. Could also use `forceStatic` to avoid this.
|
|
108
|
+
router: {location: {pathname: "/", key: "root"}},
|
|
109
|
+
});
|
|
110
|
+
const {container} = render(
|
|
111
|
+
<HarnessedComponent text="Test!" />,
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
// Assert
|
|
115
|
+
expect(container).toMatchInlineSnapshot(`
|
|
116
|
+
<div>
|
|
117
|
+
<div>
|
|
118
|
+
Test!
|
|
119
|
+
</div>
|
|
120
|
+
</div>
|
|
121
|
+
`);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
describe("if config overrides a default", () => {
|
|
126
|
+
it("should render with updated config", () => {
|
|
127
|
+
// Arrange
|
|
128
|
+
const testHarness = makeTestHarness(
|
|
129
|
+
DefaultAdapters,
|
|
130
|
+
DefaultConfigs,
|
|
131
|
+
);
|
|
132
|
+
const configOverrides: $Shape<typeof DefaultConfigs> = {
|
|
133
|
+
router: "/mysecretplace",
|
|
134
|
+
};
|
|
135
|
+
const Component = () => <div>test</div>;
|
|
136
|
+
const renderSpy = jest.spyOn(RA, "renderAdapters");
|
|
137
|
+
|
|
138
|
+
// Act
|
|
139
|
+
const HarnessedComponent = testHarness(
|
|
140
|
+
Component,
|
|
141
|
+
configOverrides,
|
|
142
|
+
);
|
|
143
|
+
render(<HarnessedComponent />);
|
|
144
|
+
|
|
145
|
+
// Assert
|
|
146
|
+
expect(renderSpy).toHaveBeenCalledWith(
|
|
147
|
+
DefaultAdapters,
|
|
148
|
+
{
|
|
149
|
+
...DefaultConfigs,
|
|
150
|
+
router: configOverrides.router,
|
|
151
|
+
},
|
|
152
|
+
expect.anything(),
|
|
153
|
+
);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it("should render harnessed component", () => {
|
|
157
|
+
// Arrange
|
|
158
|
+
const testHarness = makeTestHarness(
|
|
159
|
+
DefaultAdapters,
|
|
160
|
+
DefaultConfigs,
|
|
161
|
+
);
|
|
162
|
+
const configOverrides: $Shape<typeof DefaultConfigs> = {
|
|
163
|
+
router: "/mysecretplace/test",
|
|
164
|
+
};
|
|
165
|
+
// Render a route match that only works if we render our
|
|
166
|
+
// overridden location.
|
|
167
|
+
const Component = (props: {|text: string|}) => (
|
|
168
|
+
<Route path="/mysecretplace/*">{props.text}</Route>
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
// Act
|
|
172
|
+
const HarnessedComponent = testHarness(
|
|
173
|
+
Component,
|
|
174
|
+
configOverrides,
|
|
175
|
+
);
|
|
176
|
+
const {container} = render(
|
|
177
|
+
<HarnessedComponent text="Test!" />,
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
// Assert
|
|
181
|
+
expect(container).toMatchInlineSnapshot(`
|
|
182
|
+
<div>
|
|
183
|
+
Test!
|
|
184
|
+
</div>
|
|
185
|
+
`);
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
});
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import {renderAdapters} from "../render-adapters.js";
|
|
4
|
+
|
|
5
|
+
import type {TestHarnessAdapter, TestHarnessConfigs} from "../types.js";
|
|
6
|
+
|
|
7
|
+
describe("#renderAdapters", () => {
|
|
8
|
+
it("should return children if no adapters", () => {
|
|
9
|
+
// Arrange
|
|
10
|
+
const children = <div>Adapt me!</div>;
|
|
11
|
+
|
|
12
|
+
// Act
|
|
13
|
+
const result = renderAdapters({}, {}, children);
|
|
14
|
+
|
|
15
|
+
// Assert
|
|
16
|
+
expect(result).toBe(children);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("should invoke the adapter with its corresponding config", () => {
|
|
20
|
+
// Arrange
|
|
21
|
+
const children = <div>Adapt me!</div>;
|
|
22
|
+
const adapters = {
|
|
23
|
+
adapterA: ((jest.fn(): any): TestHarnessAdapter<string>),
|
|
24
|
+
};
|
|
25
|
+
const configs: TestHarnessConfigs<typeof adapters> = {
|
|
26
|
+
adapterA: "APPLY A CONFIG",
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// Act
|
|
30
|
+
renderAdapters(adapters, configs, children);
|
|
31
|
+
|
|
32
|
+
// Assert
|
|
33
|
+
expect(adapters.adapterA).toHaveBeenCalledWith(
|
|
34
|
+
children,
|
|
35
|
+
"APPLY A CONFIG",
|
|
36
|
+
);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("should render each adapter and the children", () => {
|
|
40
|
+
// Arrange
|
|
41
|
+
const children = "Adapt me!";
|
|
42
|
+
const adapter: TestHarnessAdapter<string> = (c, conf) => {
|
|
43
|
+
// $FlowIgnore[incompatible-type] We know, it's ok.
|
|
44
|
+
return `${conf}:${c}`;
|
|
45
|
+
};
|
|
46
|
+
const adapters = {
|
|
47
|
+
adapterA: adapter,
|
|
48
|
+
adapterB: adapter,
|
|
49
|
+
adapterC: adapter,
|
|
50
|
+
};
|
|
51
|
+
const configs: TestHarnessConfigs<typeof adapters> = {
|
|
52
|
+
adapterA: "A",
|
|
53
|
+
adapterB: "B",
|
|
54
|
+
adapterC: "C",
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// Act
|
|
58
|
+
const result = renderAdapters(adapters, configs, children);
|
|
59
|
+
|
|
60
|
+
// Assert
|
|
61
|
+
expect(result).toMatchInlineSnapshot(`"C:B:A:Adapt me!"`);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("should skip adapters where the corresponding config is null", () => {
|
|
65
|
+
// Arrange
|
|
66
|
+
const children = "Adapt me!";
|
|
67
|
+
const adapter: TestHarnessAdapter<string> = (c, conf) => {
|
|
68
|
+
// $FlowIgnore[incompatible-type] We know, it's ok.
|
|
69
|
+
return `${conf}:${c}`;
|
|
70
|
+
};
|
|
71
|
+
const adapters = {
|
|
72
|
+
adapterA: adapter,
|
|
73
|
+
adapterB: adapter,
|
|
74
|
+
adapterC: adapter,
|
|
75
|
+
};
|
|
76
|
+
const configs: TestHarnessConfigs<typeof adapters> = {
|
|
77
|
+
adapterA: "A",
|
|
78
|
+
adapterB: null,
|
|
79
|
+
adapterC: "C",
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// Act
|
|
83
|
+
const result = renderAdapters(adapters, configs, children);
|
|
84
|
+
|
|
85
|
+
// Assert
|
|
86
|
+
expect(result).toMatchInlineSnapshot(`"C:A:Adapt me!"`);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import {jest as ws} from "@khanacademy/wonder-stuff-testing";
|
|
3
|
+
import * as MTH from "../make-test-harness.js";
|
|
4
|
+
import {DefaultAdapters, DefaultConfigs} from "../adapters/adapters.js";
|
|
5
|
+
|
|
6
|
+
jest.mock("../make-test-harness.js", () => {
|
|
7
|
+
const returnValueFake = {
|
|
8
|
+
thisisa: "PRETEND REACT COMPONENT",
|
|
9
|
+
};
|
|
10
|
+
const harnessFake = jest.fn().mockReturnValue(returnValueFake);
|
|
11
|
+
return {
|
|
12
|
+
harnessFake,
|
|
13
|
+
returnValueFake,
|
|
14
|
+
makeTestHarness: jest.fn().mockReturnValue(harnessFake),
|
|
15
|
+
};
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
describe("#testHarness", () => {
|
|
19
|
+
it("should be created by calling makeTestHarness with the DefaultAdapters and DefaultConfigs", async () => {
|
|
20
|
+
// Arrange
|
|
21
|
+
const makeTestHarnessSpy = jest.spyOn(MTH, "makeTestHarness");
|
|
22
|
+
|
|
23
|
+
// Act
|
|
24
|
+
await ws.isolateModules(() => import("../hook-harness.js"));
|
|
25
|
+
|
|
26
|
+
// Assert
|
|
27
|
+
expect(makeTestHarnessSpy).toHaveBeenCalledWith(
|
|
28
|
+
DefaultAdapters,
|
|
29
|
+
DefaultConfigs,
|
|
30
|
+
);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("should invoke the function made by makeTestHarness", async () => {
|
|
34
|
+
// Arrange
|
|
35
|
+
const Component = () => null;
|
|
36
|
+
const config = {
|
|
37
|
+
router: "/boo",
|
|
38
|
+
};
|
|
39
|
+
// $FlowIgnore[prop-missing] - we add this into our mock at the top.
|
|
40
|
+
const [{harnessFake}, {testHarness}] = await ws.isolateModules(() =>
|
|
41
|
+
Promise.all([
|
|
42
|
+
import("../make-test-harness.js"),
|
|
43
|
+
import("../test-harness.js"),
|
|
44
|
+
]),
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
// Act
|
|
48
|
+
testHarness(Component, config);
|
|
49
|
+
|
|
50
|
+
// Assert
|
|
51
|
+
expect(harnessFake).toHaveBeenCalledWith(Component, config);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("should return the returned value of the function made by makeTestHarness", async () => {
|
|
55
|
+
// Arrange
|
|
56
|
+
const Component = () => null;
|
|
57
|
+
const config = {
|
|
58
|
+
router: "/boo",
|
|
59
|
+
};
|
|
60
|
+
// $FlowIgnore[prop-missing] - we add this into our mock at the top.
|
|
61
|
+
const [{returnValueFake}, {testHarness}] = await ws.isolateModules(() =>
|
|
62
|
+
Promise.all([
|
|
63
|
+
import("../make-test-harness.js"),
|
|
64
|
+
import("../test-harness.js"),
|
|
65
|
+
]),
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
// Act
|
|
69
|
+
const result = testHarness(Component, config);
|
|
70
|
+
|
|
71
|
+
// Assert
|
|
72
|
+
expect(result).toBe(returnValueFake);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
|
|
4
|
+
import type {
|
|
5
|
+
TestHarnessAdapter,
|
|
6
|
+
TestHarnessAdapters,
|
|
7
|
+
TestHarnessConfig,
|
|
8
|
+
TestHarnessConfigs,
|
|
9
|
+
} from "../types.js";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* TestHarnessAdapter<TConfig>
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
//> should assert type of config.
|
|
16
|
+
((
|
|
17
|
+
children: React.Node,
|
|
18
|
+
// TConfig is string, but we typed this arg as a number
|
|
19
|
+
// $FlowExpectedError[incompatible-cast]
|
|
20
|
+
config: number,
|
|
21
|
+
): React.Element<any> => <div />: TestHarnessAdapter<string>);
|
|
22
|
+
//<
|
|
23
|
+
|
|
24
|
+
//> should work for correct definition
|
|
25
|
+
((children: React.Node, config: string): React.Element<any> => (
|
|
26
|
+
<div />
|
|
27
|
+
): TestHarnessAdapter<string>);
|
|
28
|
+
//<
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* TestHarnessAdapters
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
//> should work for empty case
|
|
35
|
+
({}: TestHarnessAdapters);
|
|
36
|
+
//<
|
|
37
|
+
|
|
38
|
+
//> should assert if adapter is not Adapter<TConfig>
|
|
39
|
+
({
|
|
40
|
+
// String is not a adapter function
|
|
41
|
+
// $FlowExpectedError[incompatible-cast]
|
|
42
|
+
adapterString: "string",
|
|
43
|
+
}: TestHarnessAdapters);
|
|
44
|
+
//<
|
|
45
|
+
|
|
46
|
+
//> should work for a function matching Adapter<TConfig>
|
|
47
|
+
({
|
|
48
|
+
adapterA: (children, config) => <div>test</div>,
|
|
49
|
+
}: TestHarnessAdapters);
|
|
50
|
+
//<
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* TestHarnessConfig<TAdapter>
|
|
54
|
+
*/
|
|
55
|
+
//> should give the config type of an adapter
|
|
56
|
+
("string": TestHarnessConfig<TestHarnessAdapter<string>>);
|
|
57
|
+
//<
|
|
58
|
+
|
|
59
|
+
//> should error if the config type is wrong
|
|
60
|
+
// 45 is not a string
|
|
61
|
+
// $FlowExpectedError[incompatible-cast]
|
|
62
|
+
(45: TestHarnessConfig<TestHarnessAdapter<string>>);
|
|
63
|
+
//<
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* TestHarnessConfigs<TAdapters>
|
|
67
|
+
*
|
|
68
|
+
* NOTE: This only works if the properties of the passed THarnasses type
|
|
69
|
+
* are explicitly typed as `TestHarnessAdapter<TConfig>` so if passing in a
|
|
70
|
+
* non-Adapters type (which we should be, to get strong TConfig types instead
|
|
71
|
+
* of `any`), then that object should make sure that each adapter is strongly
|
|
72
|
+
* marked as `TestHarnessAdapter<TConfig>` - flow does not appear to pattern
|
|
73
|
+
* match against the type definition when invoking the ExtractConfig type and I
|
|
74
|
+
* haven't worked out how to get it to multi-dispatch so that it matches
|
|
75
|
+
* functions too. Even worse, if the type doesn't match, it just allows `any`
|
|
76
|
+
* in the configs object, rather than indicating any kind of problem.
|
|
77
|
+
*/
|
|
78
|
+
const notadapters = "this is wrong";
|
|
79
|
+
const adapterA: TestHarnessAdapter<string> = (
|
|
80
|
+
children: React.Node,
|
|
81
|
+
config: ?string,
|
|
82
|
+
): React.Element<any> => <div />;
|
|
83
|
+
const adapterB: TestHarnessAdapter<number> = (
|
|
84
|
+
children: React.Node,
|
|
85
|
+
config: ?number,
|
|
86
|
+
): React.Element<any> => <div />;
|
|
87
|
+
const adapters = {
|
|
88
|
+
adapterA,
|
|
89
|
+
adapterB,
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
//> should assert if parameterized type is not valid Adapters
|
|
93
|
+
// string is not a valid Adapter
|
|
94
|
+
// $FlowExpectedError[incompatible-use]
|
|
95
|
+
// $FlowExpectedError[incompatible-type-arg]
|
|
96
|
+
({}: TestHarnessConfigs<typeof notadapters>);
|
|
97
|
+
//<
|
|
98
|
+
|
|
99
|
+
//> should expect one config per adapter
|
|
100
|
+
// both adapter configs missing
|
|
101
|
+
// $FlowExpectedError[incompatible-exact]
|
|
102
|
+
({}: TestHarnessConfigs<typeof adapters>);
|
|
103
|
+
// adapterB config missing
|
|
104
|
+
// $FlowExpectedError[prop-missing]
|
|
105
|
+
({adapterA: "test"}: TestHarnessConfigs<typeof adapters>);
|
|
106
|
+
//<
|
|
107
|
+
|
|
108
|
+
//> should assert if config does not match adapter config
|
|
109
|
+
({
|
|
110
|
+
adapterA: "a string, this is correct",
|
|
111
|
+
// the config type here is a number, not a string
|
|
112
|
+
// $FlowExpectedError[incompatible-cast]
|
|
113
|
+
adapterB: "a string, but it should be a number",
|
|
114
|
+
}: TestHarnessConfigs<typeof adapters>);
|
|
115
|
+
//<
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`Router.adapter with full config including path should throw if the path does not match the location 1`] = `"The configured path must match the configured location or your harnessed component will not render."`;
|
|
4
|
+
|
|
5
|
+
exports[`Router.adapter with location config including path should throw if the path does not match the location 1`] = `"The configured path must match the configured location or your harnessed component will not render."`;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import {render, screen} from "@testing-library/react";
|
|
4
|
+
|
|
5
|
+
import * as Css from "../css.js";
|
|
6
|
+
|
|
7
|
+
describe("Css.adapter", () => {
|
|
8
|
+
it("should throw if the config is invalid", () => {
|
|
9
|
+
// Arrange
|
|
10
|
+
const badConfig: any = 42;
|
|
11
|
+
|
|
12
|
+
// Act
|
|
13
|
+
const underTest = () => Css.adapter("CHILDREN", badConfig);
|
|
14
|
+
|
|
15
|
+
// Assert
|
|
16
|
+
expect(underTest).toThrowErrorMatchingInlineSnapshot(
|
|
17
|
+
`"Invalid config: 42"`,
|
|
18
|
+
);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("should render the children", () => {
|
|
22
|
+
// Arrange
|
|
23
|
+
const children = <div data-test-id="children">CHILDREN!</div>;
|
|
24
|
+
|
|
25
|
+
// Act
|
|
26
|
+
render(Css.adapter(children, "test"));
|
|
27
|
+
const result = screen.getByTestId("children");
|
|
28
|
+
|
|
29
|
+
// Assert
|
|
30
|
+
expect(result).toBeInTheDocument();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("should render a container element", () => {
|
|
34
|
+
// Arrange
|
|
35
|
+
const children = <div data-test-id="children">CHILDREN!</div>;
|
|
36
|
+
|
|
37
|
+
// Act
|
|
38
|
+
render(Css.adapter(children, "test"));
|
|
39
|
+
const result = screen.getByTestId("css-adapter-container");
|
|
40
|
+
|
|
41
|
+
// Assert
|
|
42
|
+
expect(result).toBeInTheDocument();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("should render children as a child of the container element", () => {
|
|
46
|
+
// Arrange
|
|
47
|
+
const children = <div data-test-id="children">CHILDREN!</div>;
|
|
48
|
+
|
|
49
|
+
// Act
|
|
50
|
+
render(Css.adapter(children, "test"));
|
|
51
|
+
const renderedContainer = screen.getByTestId("css-adapter-container");
|
|
52
|
+
const renderedChildren = screen.getByTestId("children");
|
|
53
|
+
|
|
54
|
+
// Assert
|
|
55
|
+
expect(renderedContainer).toContainElement(renderedChildren);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it.each`
|
|
59
|
+
config | expectation
|
|
60
|
+
${"class1"} | ${"class1"}
|
|
61
|
+
${["class1", "class2"]} | ${"class1 class2"}
|
|
62
|
+
${{classes: ["class1", "class2"], style: {backgroundColor: "red"}}} | ${"class1 class2"}
|
|
63
|
+
`(
|
|
64
|
+
"should apply the given class names from $config to the container element",
|
|
65
|
+
({config, expectation}) => {
|
|
66
|
+
// Arrange
|
|
67
|
+
const children = <div data-test-id="children">CHILDREN!</div>;
|
|
68
|
+
|
|
69
|
+
// Act
|
|
70
|
+
render(Css.adapter(children, config));
|
|
71
|
+
const result = screen.getByTestId("css-adapter-container");
|
|
72
|
+
|
|
73
|
+
// Assert
|
|
74
|
+
expect(result).toHaveClass(expectation);
|
|
75
|
+
},
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
it.each`
|
|
79
|
+
config | expectation
|
|
80
|
+
${{backgroundColor: "red"}} | ${{backgroundColor: "red"}}
|
|
81
|
+
${{style: {backgroundColor: "red"}, classes: []}} | ${{backgroundColor: "red"}}
|
|
82
|
+
`(
|
|
83
|
+
"should apply the given styles from $config to the container element",
|
|
84
|
+
({config, expectation}) => {
|
|
85
|
+
// Arrange
|
|
86
|
+
const children = <div data-test-id="children">CHILDREN!</div>;
|
|
87
|
+
|
|
88
|
+
// Act
|
|
89
|
+
render(Css.adapter(children, config));
|
|
90
|
+
const result = screen.getByTestId("css-adapter-container");
|
|
91
|
+
|
|
92
|
+
// Assert
|
|
93
|
+
expect(result).toHaveStyle(expectation);
|
|
94
|
+
},
|
|
95
|
+
);
|
|
96
|
+
});
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import {mount} from "enzyme"; // eslint-disable-line no-restricted-imports
|
|
4
|
+
import "jest-enzyme";
|
|
5
|
+
import * as Data from "../data.js";
|
|
6
|
+
|
|
7
|
+
describe("WonderBlocksData.adapter", () => {
|
|
8
|
+
it("should render children when configuration arrays are empty", () => {
|
|
9
|
+
// Arrange
|
|
10
|
+
const children = <div>CONTENT</div>;
|
|
11
|
+
|
|
12
|
+
// Act
|
|
13
|
+
const wrapper = mount(Data.adapter(children, []), {
|
|
14
|
+
includeDefaultTestHarness: false,
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
// Assert
|
|
18
|
+
expect(wrapper).toHaveHTML("<div>CONTENT</div>");
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("should render children within InterceptRequests when dataIntercepts configured", () => {
|
|
22
|
+
// Arrange
|
|
23
|
+
const children = <div>CONTENT</div>;
|
|
24
|
+
|
|
25
|
+
// Act
|
|
26
|
+
const wrapper = mount(
|
|
27
|
+
Data.adapter(children, () =>
|
|
28
|
+
Promise.resolve(("INTERCEPTED!": any)),
|
|
29
|
+
),
|
|
30
|
+
{
|
|
31
|
+
includeDefaultTestHarness: false,
|
|
32
|
+
},
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
// Assert
|
|
36
|
+
expect(wrapper).toContainMatchingElement("InterceptRequests");
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("should render like we expect", () => {
|
|
40
|
+
// Snapshot test is handy to visualize what's going on and help debug
|
|
41
|
+
// test failures of the other cases. The other cases assert specifics.
|
|
42
|
+
// Arrange
|
|
43
|
+
const children = <div>CONTENT</div>;
|
|
44
|
+
|
|
45
|
+
// Act
|
|
46
|
+
const wrapper = mount(
|
|
47
|
+
Data.adapter(children, () =>
|
|
48
|
+
Promise.resolve(("INTERCEPTED!": any)),
|
|
49
|
+
),
|
|
50
|
+
{
|
|
51
|
+
includeDefaultTestHarness: false,
|
|
52
|
+
},
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
// Assert
|
|
56
|
+
expect(wrapper).toMatchInlineSnapshot(`
|
|
57
|
+
<InterceptRequests
|
|
58
|
+
interceptor={[Function]}
|
|
59
|
+
>
|
|
60
|
+
<div>
|
|
61
|
+
CONTENT
|
|
62
|
+
</div>
|
|
63
|
+
</InterceptRequests>
|
|
64
|
+
`);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import {render, screen} from "@testing-library/react";
|
|
4
|
+
|
|
5
|
+
import * as Portal from "../portal.js";
|
|
6
|
+
|
|
7
|
+
describe("Portal.adapter", () => {
|
|
8
|
+
it("should render a portal root element", () => {
|
|
9
|
+
// Arrange
|
|
10
|
+
const children = <div>CHILDREN!</div>;
|
|
11
|
+
const portalId = "portal-test-id";
|
|
12
|
+
|
|
13
|
+
// Act
|
|
14
|
+
render(Portal.adapter(children, portalId));
|
|
15
|
+
|
|
16
|
+
// Assert
|
|
17
|
+
expect(screen.getByTestId(portalId)).toBeInTheDocument();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("should render the children correctly", () => {
|
|
21
|
+
// Arrange
|
|
22
|
+
const portalId = "portal-test-id";
|
|
23
|
+
const children = <div>CHILDREN!</div>;
|
|
24
|
+
|
|
25
|
+
// Act
|
|
26
|
+
render(Portal.adapter(children, portalId));
|
|
27
|
+
|
|
28
|
+
// Assert
|
|
29
|
+
expect(screen.getByText("CHILDREN!")).toBeInTheDocument();
|
|
30
|
+
});
|
|
31
|
+
});
|