@khanacademy/wonder-blocks-testing 7.1.9 → 7.1.11

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.
Files changed (147) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/dist/es/index.js +183 -120
  3. package/dist/fetch/fetch-request-matches-mock.d.ts +5 -0
  4. package/dist/fetch/fetch-request-matches-mock.js.flow +17 -0
  5. package/dist/fetch/mock-fetch.d.ts +5 -0
  6. package/dist/fetch/mock-fetch.js.flow +13 -0
  7. package/dist/fetch/types.d.ts +9 -0
  8. package/dist/fetch/types.js.flow +19 -0
  9. package/dist/fixtures/fixtures.basic.stories.d.ts +13 -0
  10. package/dist/fixtures/fixtures.basic.stories.js.flow +22 -0
  11. package/dist/fixtures/fixtures.d.ts +19 -0
  12. package/dist/fixtures/fixtures.defaultwrapper.stories.d.ts +9 -0
  13. package/dist/fixtures/fixtures.defaultwrapper.stories.js.flow +17 -0
  14. package/dist/fixtures/fixtures.js.flow +33 -0
  15. package/dist/fixtures/types.d.ts +36 -0
  16. package/dist/fixtures/types.js.flow +37 -0
  17. package/dist/gql/gql-request-matches-mock.d.ts +3 -0
  18. package/dist/gql/gql-request-matches-mock.js.flow +15 -0
  19. package/dist/gql/mock-gql-fetch.d.ts +5 -0
  20. package/dist/gql/mock-gql-fetch.js.flow +13 -0
  21. package/dist/gql/types.d.ts +15 -0
  22. package/dist/gql/types.js.flow +39 -0
  23. package/dist/harness/adapters/adapters.d.ts +35 -0
  24. package/dist/harness/adapters/adapters.js.flow +69 -0
  25. package/dist/harness/adapters/css.d.ts +12 -0
  26. package/dist/harness/adapters/css.js.flow +23 -0
  27. package/dist/harness/adapters/data.d.ts +18 -0
  28. package/dist/harness/adapters/data.js.flow +32 -0
  29. package/dist/harness/adapters/portal.d.ts +12 -0
  30. package/dist/harness/adapters/portal.js.flow +18 -0
  31. package/dist/harness/adapters/router.d.ts +94 -0
  32. package/dist/harness/adapters/router.js.flow +122 -0
  33. package/dist/harness/hook-harness.d.ts +13 -0
  34. package/dist/harness/hook-harness.js.flow +23 -0
  35. package/dist/harness/make-hook-harness.d.ts +17 -0
  36. package/dist/harness/make-hook-harness.js.flow +42 -0
  37. package/dist/harness/make-test-harness.d.ts +18 -0
  38. package/dist/harness/make-test-harness.js.flow +48 -0
  39. package/dist/harness/render-adapters.d.ts +6 -0
  40. package/dist/harness/render-adapters.js.flow +24 -0
  41. package/dist/harness/test-harness.d.ts +32 -0
  42. package/dist/harness/test-harness.js.flow +83 -0
  43. package/dist/harness/types.d.ts +46 -0
  44. package/dist/harness/types.js.flow +66 -0
  45. package/dist/index.d.ts +15 -0
  46. package/dist/index.js +185 -127
  47. package/dist/index.js.flow +26 -2
  48. package/dist/mock-requester.d.ts +5 -0
  49. package/dist/mock-requester.js.flow +22 -0
  50. package/dist/respond-with.d.ts +57 -0
  51. package/dist/respond-with.js.flow +91 -0
  52. package/dist/response-impl.d.ts +1 -0
  53. package/dist/response-impl.js.flow +8 -0
  54. package/dist/settle-controller.d.ts +19 -0
  55. package/dist/settle-controller.js.flow +26 -0
  56. package/dist/settle-signal.d.ts +18 -0
  57. package/dist/settle-signal.js.flow +26 -0
  58. package/dist/types.d.ts +25 -0
  59. package/dist/types.js.flow +46 -0
  60. package/package.json +6 -6
  61. package/src/__tests__/{mock-requester.test.js → mock-requester.test.ts} +3 -4
  62. package/src/__tests__/{respond-with.test.js → respond-with.test.ts} +2 -3
  63. package/src/__tests__/response-impl.test.js +3 -3
  64. package/src/__tests__/{settle-controller.test.js → settle-controller.test.ts} +2 -3
  65. package/src/__tests__/{settle-signal.test.js → settle-signal.test.ts} +1 -2
  66. package/src/fetch/__tests__/__snapshots__/{mock-fetch.test.js.snap → mock-fetch.test.ts.snap} +3 -3
  67. package/src/fetch/__tests__/{fetch-request-matches-mock.test.js → fetch-request-matches-mock.test.ts} +6 -7
  68. package/src/fetch/__tests__/{mock-fetch.test.js → mock-fetch.test.ts} +3 -4
  69. package/src/fetch/{fetch-request-matches-mock.js → fetch-request-matches-mock.ts} +4 -3
  70. package/src/fetch/{mock-fetch.js → mock-fetch.ts} +5 -6
  71. package/src/fetch/types.ts +14 -0
  72. package/src/fixtures/__tests__/{fixtures.test.js → fixtures.test.tsx} +7 -5
  73. package/src/fixtures/{fixtures.basic.stories.js → fixtures.basic.stories.tsx} +17 -14
  74. package/src/fixtures/{fixtures.defaultwrapper.stories.js → fixtures.defaultwrapper.stories.tsx} +9 -9
  75. package/src/fixtures/{fixtures.js → fixtures.tsx} +17 -12
  76. package/src/fixtures/{types.js → types.ts} +9 -13
  77. package/src/gql/__tests__/{gql-request-matches-mock.test.js → gql-request-matches-mock.test.ts} +18 -19
  78. package/src/gql/__tests__/{mock-gql-fetch.test.js → mock-gql-fetch.test.tsx} +46 -47
  79. package/src/gql/__tests__/{wb-data-integration.test.js → wb-data-integration.test.tsx} +23 -24
  80. package/src/gql/{gql-request-matches-mock.js → gql-request-matches-mock.ts} +2 -5
  81. package/src/gql/mock-gql-fetch.ts +15 -0
  82. package/src/gql/types.ts +33 -0
  83. package/src/harness/__tests__/{hook-harness.test.js → hook-harness.test.ts} +13 -14
  84. package/src/harness/__tests__/{make-hook-harness.test.js → make-hook-harness.test.tsx} +12 -13
  85. package/src/harness/__tests__/{make-test-harness.test.js → make-test-harness.test.tsx} +7 -8
  86. package/src/harness/__tests__/{render-adapters.test.js → render-adapters.test.tsx} +10 -11
  87. package/src/harness/__tests__/{test-harness.test.js → test-harness.test.ts} +13 -14
  88. package/src/harness/__tests__/{types.flowtest.js → types.flowtest.tsx} +25 -28
  89. package/src/harness/adapters/__tests__/{css.test.js → css.test.tsx} +3 -4
  90. package/src/harness/adapters/__tests__/{data.test.js → data.test.tsx} +5 -4
  91. package/src/harness/adapters/__tests__/{portal.test.js → portal.test.tsx} +1 -2
  92. package/src/harness/adapters/__tests__/{router.test.js → router.test.tsx} +76 -57
  93. package/src/harness/adapters/{adapters.js → adapters.ts} +6 -7
  94. package/src/harness/adapters/{css.js → css.tsx} +22 -19
  95. package/src/harness/adapters/{data.js → data.tsx} +8 -6
  96. package/src/harness/adapters/{portal.js → portal.tsx} +4 -5
  97. package/src/harness/adapters/router.tsx +218 -0
  98. package/src/harness/{hook-harness.js → hook-harness.ts} +5 -6
  99. package/src/harness/{make-hook-harness.js → make-hook-harness.ts} +8 -8
  100. package/src/harness/{make-test-harness.js → make-test-harness.tsx} +19 -22
  101. package/src/harness/{render-adapters.js → render-adapters.ts} +5 -5
  102. package/src/harness/test-harness.ts +13 -0
  103. package/src/harness/{types.js → types.ts} +14 -12
  104. package/src/index.ts +20 -0
  105. package/src/{mock-requester.js → mock-requester.ts} +6 -5
  106. package/src/{respond-with.js → respond-with.ts} +45 -40
  107. package/src/{response-impl.js → response-impl.ts} +1 -2
  108. package/src/settle-controller.ts +37 -0
  109. package/src/{settle-signal.js → settle-signal.ts} +10 -7
  110. package/src/types.ts +40 -0
  111. package/tsconfig.json +11 -0
  112. package/tsconfig.tsbuildinfo +1 -0
  113. package/src/__docs__/_overview_.stories.mdx +0 -18
  114. package/src/__docs__/_overview_fixtures.stories.mdx +0 -18
  115. package/src/__docs__/_overview_mocking.stories.mdx +0 -14
  116. package/src/__docs__/_overview_test_harness.stories.mdx +0 -18
  117. package/src/__docs__/exports.fixtures.stories.mdx +0 -31
  118. package/src/__docs__/exports.harness-adapters.stories.mdx +0 -187
  119. package/src/__docs__/exports.hook-harness.stories.mdx +0 -22
  120. package/src/__docs__/exports.make-hook-harness.stories.mdx +0 -25
  121. package/src/__docs__/exports.make-test-harness.stories.mdx +0 -28
  122. package/src/__docs__/exports.mock-fetch.stories.mdx +0 -40
  123. package/src/__docs__/exports.mock-gql-fetch.stories.mdx +0 -64
  124. package/src/__docs__/exports.respond-with.stories.mdx +0 -84
  125. package/src/__docs__/exports.settle-controller.stories.mdx +0 -32
  126. package/src/__docs__/exports.test-harness.stories.mdx +0 -23
  127. package/src/__docs__/types.fetch-mock-fn.stories.mdx +0 -22
  128. package/src/__docs__/types.fetch-mock-operation.stories.mdx +0 -18
  129. package/src/__docs__/types.fixture-fn.stories.mdx +0 -46
  130. package/src/__docs__/types.fixture-props.stories.mdx +0 -20
  131. package/src/__docs__/types.get-props-options.stories.mdx +0 -52
  132. package/src/__docs__/types.gql-fetch-mock-fn.stories.mdx +0 -27
  133. package/src/__docs__/types.gql-mock-operation.stories.mdx +0 -26
  134. package/src/__docs__/types.mock-response.stories.mdx +0 -22
  135. package/src/__docs__/types.test-harness-adapter.stories.mdx +0 -21
  136. package/src/__docs__/types.test-harness-adapters.stories.mdx +0 -46
  137. package/src/__docs__/types.test-harness-config.stories.mdx +0 -18
  138. package/src/__docs__/types.test-harness-configs.stories.mdx +0 -59
  139. package/src/fetch/types.js +0 -15
  140. package/src/gql/mock-gql-fetch.js +0 -18
  141. package/src/gql/types.js +0 -34
  142. package/src/harness/adapters/router.js +0 -206
  143. package/src/harness/test-harness.js +0 -24
  144. package/src/index.js +0 -26
  145. package/src/settle-controller.js +0 -35
  146. package/src/types.js +0 -39
  147. /package/src/harness/adapters/__tests__/__snapshots__/{router.test.js.snap → router.test.tsx.snap} +0 -0
@@ -0,0 +1,218 @@
1
+ import * as React from "react";
2
+
3
+ import {StaticRouter, MemoryRouter, Route, Switch} from "react-router-dom";
4
+
5
+ // @ts-expect-error [FEI-5019] - TS2305 - Module '"react-router-dom"' has no exported member 'LocationShape'. | TS2305 - Module '"react-router-dom"' has no exported member 'Location'.
6
+ import type {LocationShape, Location} from "react-router-dom";
7
+ import type {TestHarnessAdapter} from "../types";
8
+
9
+ type MemoryRouterProps = JSX.LibraryManagedAttributes<
10
+ typeof MemoryRouter,
11
+ React.ComponentProps<typeof MemoryRouter>
12
+ >;
13
+
14
+ /**
15
+ * Configuration for the withLocation test harness adapter.
16
+ */
17
+ type Config = // The initial location to use.
18
+
19
+ | Readonly<
20
+ | {
21
+ /**
22
+ * See MemoryRouter prop for initialEntries.
23
+ */
24
+ initialEntries: MemoryRouterProps["initialEntries"];
25
+ /**
26
+ * See MemoryRouter prop for initialIndex.
27
+ */
28
+ initialIndex?: MemoryRouterProps["initialIndex"];
29
+ /**
30
+ * See MemoryRouter prop for getUserConfirmation.
31
+ */
32
+ getUserConfirmation?: MemoryRouterProps["getUserConfirmation"];
33
+ /**
34
+ * A path match to use.
35
+ *
36
+ * When this is specified, the harnessed component will be
37
+ * rendered inside a `Route` handler with this path.
38
+ *
39
+ * If the path matches the location, then the route will
40
+ * render the component.
41
+ *
42
+ * If the path does not match the location, then the route
43
+ * will not render the component.
44
+ */
45
+ path?: string;
46
+ }
47
+ | {
48
+ /**
49
+ * The location to use.
50
+ */
51
+ location: string | Location;
52
+ /**
53
+ * Force the use of a StaticRouter, instead of MemoryRouter.
54
+ */
55
+ forceStatic: true;
56
+ /**
57
+ * A path match to use.
58
+ *
59
+ * When this is specified, the harnessed component will be
60
+ * rendered inside a `Route` handler with this path.
61
+ *
62
+ * If the path matches the location, then the route will
63
+ * render the component.
64
+ *
65
+ * If the path does not match the location, then the route
66
+ * will not render the component.
67
+ */
68
+ path?: string;
69
+ }
70
+ | {
71
+ /**
72
+ * The initial location to use.
73
+ */
74
+ location: string | LocationShape;
75
+ /**
76
+ * A path match to use.
77
+ *
78
+ * When this is specified, the harnessed component will be
79
+ * rendered inside a `Route` handler with this path.
80
+ *
81
+ * If the path matches the location, then the route will
82
+ * render the component.
83
+ *
84
+ * If the path does not match the location, then the route
85
+ * will not render the component.
86
+ */
87
+ path?: string;
88
+ }
89
+ >
90
+ | string;
91
+
92
+ /**
93
+ * The default configuration for this adapter.
94
+ */
95
+ export const defaultConfig = {location: "/"} as const;
96
+
97
+ const maybeWithRoute = (
98
+ children: React.ReactNode,
99
+ path?: string | null,
100
+ ): React.ReactElement => {
101
+ if (path == null) {
102
+ // @ts-expect-error [FEI-5019] - TS2322 - Type 'ReactNode' is not assignable to type 'ReactElement<any, string | JSXElementConstructor<any>>'.
103
+ return children;
104
+ }
105
+
106
+ return (
107
+ <Switch>
108
+ <Route exact={true} path={path}>
109
+ {children}
110
+ </Route>
111
+ <Route
112
+ path="*"
113
+ render={() => {
114
+ throw new Error(
115
+ "The configured path must match the configured location or your harnessed component will not render.",
116
+ );
117
+ }}
118
+ />
119
+ </Switch>
120
+ );
121
+ };
122
+
123
+ /**
124
+ * Adapter that sets up a router and AppShell location-specific contexts.
125
+ *
126
+ * This allows you to ensure that components are being tested in the
127
+ * AppShell world.
128
+ *
129
+ * NOTE(somewhatabstract): The AppShell component itself already does
130
+ * the work of setting up routing and the AppShellContext and so using this
131
+ * adapter with the App component will have zero-effect since AppShell will
132
+ * override it.
133
+ */
134
+ export const adapter: TestHarnessAdapter<Config> = (
135
+ children: React.ReactNode,
136
+ config: Config,
137
+ ): React.ReactElement<any> => {
138
+ if (typeof config === "string") {
139
+ config = {
140
+ location: config,
141
+ };
142
+ }
143
+
144
+ // Wrap children with the various contexts and routes, as per the config.
145
+ const wrappedWithRoute = maybeWithRoute(children, config.path);
146
+ // @ts-expect-error [FEI-5019] - TS2339 - Property 'forceStatic' 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; } | { ...; } | { ...; }>'.
147
+ if (config.forceStatic) {
148
+ /**
149
+ * There may be times (SSR testing comes to mind) where we will be
150
+ * really strict about not permitting client-side navigation events.
151
+ */
152
+ return (
153
+ // @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; } | { ...; } | { ...; }>'.
154
+ <StaticRouter location={config.location} context={{}}>
155
+ {wrappedWithRoute}
156
+ </StaticRouter>
157
+ );
158
+ }
159
+
160
+ /**
161
+ * OK, we must be OK with a memory router.
162
+ *
163
+ * There are two flavors of config for this. The easy one with just a
164
+ * location, and the complex one for those gnarlier setups.
165
+ *
166
+ * First, the easy one.
167
+ */
168
+ // @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; } | { ...; } | { ...; }>'.
169
+ if (typeof config.location !== "undefined") {
170
+ return (
171
+ // @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; } | { ...; } | { ...; }>'.
172
+ <MemoryRouter initialEntries={[config.location]}>
173
+ {wrappedWithRoute}
174
+ </MemoryRouter>
175
+ );
176
+ }
177
+
178
+ /**
179
+ * If it's not the easy one, it should be the complex one.
180
+ * Let's make sure we have good data (also keeps TypeScript happy).
181
+ */
182
+ // @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; } | { ...; } | { ...; }>'.
183
+ if (typeof config.initialEntries === "undefined") {
184
+ throw new Error(
185
+ "A location or initial history entries must be provided.",
186
+ );
187
+ }
188
+
189
+ /**
190
+ * What should happen if no entries were in the array?
191
+ * It likely uses the root one anyway, but a consistent API is what
192
+ * we want, so let's ensure we always have our default location at least.
193
+ */
194
+ const entries =
195
+ // @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; } | { ...; } | { ...; }>'.
196
+ config.initialEntries.length === 0
197
+ ? [defaultConfig.location]
198
+ : // @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; } | { ...; } | { ...; }>'.
199
+ config.initialEntries;
200
+
201
+ // Memory router doesn't allow us to pass maybe types in its TypeScript types.
202
+ // So let's build props then spread them.
203
+ const routerProps: MemoryRouterProps = {
204
+ initialEntries: entries,
205
+ };
206
+ // @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; } | { ...; } | { ...; }>'.
207
+ if (config.initialIndex != null) {
208
+ // @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; } | { ...; } | { ...; }>'.
209
+ routerProps.initialIndex = config.initialIndex;
210
+ }
211
+ // @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; } | { ...; } | { ...; }>'.
212
+ if (config.getUserConfirmation != null) {
213
+ // @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; } | { ...; } | { ...; }>'.
214
+ routerProps.getUserConfirmation = config.getUserConfirmation;
215
+ }
216
+
217
+ return <MemoryRouter {...routerProps}>{wrappedWithRoute}</MemoryRouter>;
218
+ };
@@ -1,10 +1,9 @@
1
- // @flow
2
1
  import * as React from "react";
3
2
 
4
- import {makeHookHarness} from "./make-hook-harness.js";
5
- import {DefaultAdapters, DefaultConfigs} from "./adapters/adapters.js";
3
+ import {makeHookHarness} from "./make-hook-harness";
4
+ import {DefaultAdapters, DefaultConfigs} from "./adapters/adapters";
6
5
 
7
- import type {TestHarnessConfigs} from "./types.js";
6
+ import type {TestHarnessConfigs} from "./types";
8
7
 
9
8
  /**
10
9
  * Create test wrapper for hook testing with Wonder Blocks default adapters.
@@ -16,8 +15,8 @@ import type {TestHarnessConfigs} from "./types.js";
16
15
  * function.
17
16
  */
18
17
  export const hookHarness: (
19
- configs?: $Shape<TestHarnessConfigs<typeof DefaultAdapters>>,
20
- ) => React.AbstractComponent<any, any> = makeHookHarness(
18
+ configs?: Partial<TestHarnessConfigs<typeof DefaultAdapters>>,
19
+ ) => React.ForwardRefExoticComponent<any> = makeHookHarness(
21
20
  DefaultAdapters,
22
21
  DefaultConfigs,
23
22
  );
@@ -1,10 +1,10 @@
1
- // @flow
2
1
  import * as React from "react";
3
2
 
4
- import {makeTestHarness} from "./make-test-harness.js";
3
+ import {makeTestHarness} from "./make-test-harness";
5
4
 
6
- import type {TestHarnessAdapters, TestHarnessConfigs} from "./types.js";
5
+ import type {TestHarnessAdapters, TestHarnessConfigs} from "./types";
7
6
 
7
+ // @ts-expect-error [FEI-5019] - TS7031 - Binding element 'children' implicitly has an 'any' type.
8
8
  const HookHarness = ({children}) => children;
9
9
 
10
10
  /**
@@ -21,12 +21,12 @@ const HookHarness = ({children}) => children;
21
21
  * configs?: $Shape<TestHarnessConfigs<TAdapters>>,
22
22
  * ) => React.AbstractComponent<any, any>} A test harness.
23
23
  */
24
- export const makeHookHarness = <TAdapters: TestHarnessAdapters>(
24
+ export const makeHookHarness = <TAdapters extends TestHarnessAdapters>(
25
25
  adapters: TAdapters,
26
26
  defaultConfigs: TestHarnessConfigs<TAdapters>,
27
27
  ): ((
28
- configs?: $Shape<TestHarnessConfigs<TAdapters>>,
29
- ) => React.AbstractComponent<any, any>) => {
28
+ configs?: Partial<TestHarnessConfigs<TAdapters>>,
29
+ ) => React.ForwardRefExoticComponent<any>) => {
30
30
  const testHarness = makeTestHarness<TAdapters>(adapters, defaultConfigs);
31
31
  /**
32
32
  * Create a harness to use as a wrapper when rendering hooks.
@@ -34,6 +34,6 @@ export const makeHookHarness = <TAdapters: TestHarnessAdapters>(
34
34
  * @param {$Shape<Configs<typeof DefaultAdapters>>} [configs] Any adapter
35
35
  * configuration that you want to override from the DefaultConfigs values.
36
36
  */
37
- return (configs?: $Shape<TestHarnessConfigs<TAdapters>>) =>
38
- testHarness<any, any>(HookHarness, configs);
37
+ return (configs?: Partial<TestHarnessConfigs<TAdapters>>) =>
38
+ testHarness<any>(HookHarness, configs);
39
39
  };
@@ -1,9 +1,8 @@
1
- // @flow
2
1
  import * as React from "react";
3
2
 
4
- import {renderAdapters} from "./render-adapters.js";
3
+ import {renderAdapters} from "./render-adapters";
5
4
 
6
- import type {TestHarnessAdapters, TestHarnessConfigs} from "./types.js";
5
+ import type {TestHarnessAdapters, TestHarnessConfigs} from "./types";
7
6
 
8
7
  /**
9
8
  * Create a test harness method for use with React components.
@@ -20,40 +19,38 @@ import type {TestHarnessAdapters, TestHarnessConfigs} from "./types.js";
20
19
  * configs?: $Shape<Configs<TAdapters>>,
21
20
  * ) => React.AbstractComponent<TProps, Instance>} A test harness.
22
21
  */
23
- export const makeTestHarness = <TAdapters: TestHarnessAdapters>(
22
+ export const makeTestHarness = <TAdapters extends TestHarnessAdapters>(
24
23
  adapters: TAdapters,
25
24
  defaultConfigs: TestHarnessConfigs<TAdapters>,
26
- ): (<-TProps, +Instance = mixed>(
27
- Component: React.AbstractComponent<TProps, Instance>,
28
- configs?: $Shape<TestHarnessConfigs<TAdapters>>,
29
- ) => React.AbstractComponent<TProps, Instance>) => {
25
+ ): (<TProps extends object>(
26
+ Component: React.ComponentType<TProps>,
27
+ configs?: Partial<TestHarnessConfigs<TAdapters>>,
28
+ ) => React.ForwardRefExoticComponent<
29
+ React.PropsWithoutRef<TProps> & React.RefAttributes<unknown>
30
+ >) => {
30
31
  /**
31
32
  * Create a harnessed version of the given component.
32
33
  *
33
- * @param {React.AbstractComponent<TProps, Instance>} component The
34
+ * @param {React.ComponentType<TProps>} component The
34
35
  * component to be wrapped.
35
- * @param {$Shape<Configs<TAdapters>>} [configs] Any adapter
36
+ * @param {Partial<TestHarnessConfigs<TAdapters>>} [configs] Any adapter
36
37
  * configuration that you want to override from the `defaultConfigs` values.
37
38
  */
38
- return <-TProps, +Instance = mixed>(
39
- Component: React.AbstractComponent<TProps, Instance>,
40
- configs?: $Shape<TestHarnessConfigs<TAdapters>>,
41
- ): React.AbstractComponent<TProps, Instance> => {
42
- /**
43
- * $FlowIgnore[cannot-spread-indexer]
44
- * We know `configs` may overwrite `DefaultConfigs` and we're ok with it.
45
- * The interface definition handles the typing for us so we're ok
46
- * with it not being inferred here.
47
- */
39
+ return <TProps extends object>(
40
+ Component: React.ComponentType<TProps>,
41
+ configs?: Partial<TestHarnessConfigs<TAdapters>>,
42
+ ): React.ForwardRefExoticComponent<
43
+ React.PropsWithoutRef<TProps> & React.RefAttributes<unknown>
44
+ > => {
48
45
  const fullConfig: TestHarnessConfigs<TAdapters> = {
49
46
  ...defaultConfigs,
50
47
  ...configs,
51
48
  };
52
- const harnessedComponent = React.forwardRef((props, ref) =>
49
+ const harnessedComponent = React.forwardRef((props: TProps, ref) =>
53
50
  renderAdapters<TAdapters>(
54
51
  adapters,
55
52
  fullConfig,
56
- <Component {...props} ref={ref} />,
53
+ <Component {...(props as TProps)} ref={ref} />,
57
54
  ),
58
55
  );
59
56
 
@@ -1,16 +1,15 @@
1
- // @flow
2
1
  import * as React from "react";
3
2
 
4
- import type {TestHarnessConfigs, TestHarnessAdapters} from "./types.js";
3
+ import type {TestHarnessConfigs, TestHarnessAdapters} from "./types";
5
4
 
6
5
  /**
7
6
  * Render test adapters around a child component.
8
7
  */
9
- export const renderAdapters = <TAdapters: TestHarnessAdapters>(
8
+ export const renderAdapters = <TAdapters extends TestHarnessAdapters>(
10
9
  adapters: TAdapters,
11
10
  configs: TestHarnessConfigs<TAdapters>,
12
- children: React.Node,
13
- ): React.Node => {
11
+ children: React.ReactNode,
12
+ ): React.ReactElement => {
14
13
  let currentChildren = children;
15
14
  for (const adapterName of Object.keys(adapters)) {
16
15
  const adapter = adapters[adapterName];
@@ -23,5 +22,6 @@ export const renderAdapters = <TAdapters: TestHarnessAdapters>(
23
22
  currentChildren = adapter(currentChildren, config);
24
23
  }
25
24
  }
25
+ // @ts-expect-error [FEI-5019] - TS2322 - Type 'ReactNode' is not assignable to type 'ReactElement<any, string | JSXElementConstructor<any>>'.
26
26
  return currentChildren;
27
27
  };
@@ -0,0 +1,13 @@
1
+ import {makeTestHarness} from "./make-test-harness";
2
+ import {DefaultAdapters, DefaultConfigs} from "./adapters/adapters";
3
+
4
+ /**
5
+ * Wrap a component with a test harness using Wonder Blocks default adapters.
6
+ *
7
+ * This is primarily useful for tests within Wonder Blocks.
8
+ *
9
+ * If you want to expand the range of adapters or change the default
10
+ * configurations, use `makeTestHarness` to create a new `testHarness`
11
+ * function.
12
+ */
13
+ export const testHarness = makeTestHarness(DefaultAdapters, DefaultConfigs);
@@ -1,13 +1,12 @@
1
- // @flow
2
1
  import * as React from "react";
3
2
 
4
3
  /**
5
4
  * A adapter to be composed with our test harnass infrastructure.
6
5
  */
7
6
  export type TestHarnessAdapter<TConfig> = (
8
- children: React.Node,
7
+ children: React.ReactNode,
9
8
  config: TConfig,
10
- ) => React.Element<any>;
9
+ ) => React.ReactElement<any>;
11
10
 
12
11
  /**
13
12
  * A general map of adapters by their identifiers.
@@ -21,22 +20,25 @@ export type TestHarnessAdapter<TConfig> = (
21
20
  * should infer their type in most cases to ensure the strongest typing of
22
21
  * the adapters.
23
22
  */
24
- export type TestHarnessAdapters = {|
25
- [adapterID: string]: TestHarnessAdapter<any>,
26
- |};
23
+ export type TestHarnessAdapters = {
24
+ [adapterID: string]: TestHarnessAdapter<any>;
25
+ };
27
26
 
28
27
  /**
29
28
  * Mapping functions from a adapter-like function to config type.
30
29
  */
31
- type ExtractConfig = <TConfig>(TestHarnessAdapter<TConfig>) => TConfig;
32
- type ExtractMaybeConfig = <TConfig>(TestHarnessAdapter<TConfig>) => ?TConfig;
30
+ type ExtractConfig = <TConfig>(arg1: TestHarnessAdapter<TConfig>) => TConfig;
31
+ type ExtractMaybeConfig = <TConfig>(
32
+ arg1: TestHarnessAdapter<TConfig>,
33
+ ) => TConfig | null | undefined;
33
34
 
34
35
  /**
35
36
  * Type for easily defining an adapter's config type.
36
37
  *
37
38
  * This is the `TestHarnessAdapter` equivalent of `React.ElementConfig`.
38
39
  */
39
- export type TestHarnessConfig<TAdapter> = $Call<ExtractConfig, TAdapter>;
40
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
41
+ export type TestHarnessConfig<TAdapter> = ReturnType<ExtractConfig>;
40
42
 
41
43
  /**
42
44
  * The `TestHarnessConfigs` type as defined by parsing a given set of adapters.
@@ -45,13 +47,13 @@ export type TestHarnessConfig<TAdapter> = $Call<ExtractConfig, TAdapter>;
45
47
  * are explicitly typed as `TestHarnessAdapter<TConfig>` so if passing in a
46
48
  * non-Adapters type (which we should be, to get strong `TConfig` types instead
47
49
  * of `any`), then that object should make sure that each adapter is strongly
48
- * marked as `TestHarnessAdapter<TConfig>` - flow does not appear to pattern
50
+ * marked as `TestHarnessAdapter<TConfig>` - TypeScript does not appear to pattern
49
51
  * match against the type definition when invoking the `ExtractConfig` type and I
50
52
  * haven't worked out how to get it to multi-dispatch so that it matches
51
53
  * functions too. Even worse, if the type doesn't match, it just allows `any`
52
54
  * in the `Configs` object, rather than indicating any kind of problem.
53
55
  */
54
- export type TestHarnessConfigs<TAdapters: TestHarnessAdapters> = $ObjMap<
56
+ export type TestHarnessConfigs<TAdapters extends TestHarnessAdapters> = ObjMap<
55
57
  TAdapters,
56
- ExtractMaybeConfig,
58
+ ExtractMaybeConfig
57
59
  >;
package/src/index.ts ADDED
@@ -0,0 +1,20 @@
1
+ // Fixtures framework
2
+ export {fixtures} from "./fixtures/fixtures";
3
+ export type {FixtureFn, FixtureProps, GetPropsOptions} from "./fixtures/types";
4
+
5
+ // Fetch mocking framework
6
+ export {mockFetch} from "./fetch/mock-fetch";
7
+ export {mockGqlFetch} from "./gql/mock-gql-fetch";
8
+ export {RespondWith} from "./respond-with";
9
+ export {SettleController} from "./settle-controller";
10
+ export type {MockResponse} from "./respond-with";
11
+ export type {FetchMockFn, FetchMockOperation} from "./fetch/types";
12
+ export type {GqlFetchMockFn, GqlMockOperation} from "./gql/types";
13
+
14
+ // Test harness framework
15
+ export * from "./harness/types";
16
+ export * as harnessAdapters from "./harness/adapters/adapters";
17
+ export {makeHookHarness} from "./harness/make-hook-harness";
18
+ export {makeTestHarness} from "./harness/make-test-harness";
19
+ export {hookHarness} from "./harness/hook-harness";
20
+ export {testHarness} from "./harness/test-harness";
@@ -1,13 +1,12 @@
1
- // @flow
2
- import type {MockResponse} from "./respond-with.js";
3
- import type {OperationMock, OperationMatcher, MockFn} from "./types.js";
1
+ import type {MockResponse} from "./respond-with";
2
+ import type {OperationMock, OperationMatcher, MockFn} from "./types";
4
3
 
5
4
  /**
6
5
  * A generic mock request function for using when mocking fetch or gqlFetch.
7
6
  */
8
7
  export const mockRequester = <
9
8
  TOperationType,
10
- TOperationMock: OperationMock<TOperationType>,
9
+ TOperationMock extends OperationMock<TOperationType> = OperationMock<TOperationType>,
11
10
  >(
12
11
  operationMatcher: OperationMatcher<any>,
13
12
  operationToString: (
@@ -39,9 +38,11 @@ export const mockRequester = <
39
38
 
40
39
  // Default is to reject with some helpful info on what request
41
40
  // 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
+ const operation = operationToString(...args);
42
43
  return Promise.reject(
43
44
  new Error(`No matching mock response found for request:
44
- ${operationToString(...args)}`),
45
+ ${operation}`),
45
46
  );
46
47
  };
47
48