@khanacademy/wonder-blocks-testing 7.1.10 → 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 (142) hide show
  1. package/CHANGELOG.md +25 -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} +1 -2
  62. package/src/__tests__/{respond-with.test.js → respond-with.test.ts} +0 -1
  63. package/src/__tests__/response-impl.test.js +1 -1
  64. package/src/__tests__/{settle-controller.test.js → settle-controller.test.ts} +0 -1
  65. package/src/__tests__/{settle-signal.test.js → settle-signal.test.ts} +0 -1
  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} +5 -6
  68. package/src/fetch/__tests__/{mock-fetch.test.js → mock-fetch.test.ts} +1 -2
  69. package/src/fetch/{fetch-request-matches-mock.js → fetch-request-matches-mock.ts} +3 -2
  70. package/src/fetch/{mock-fetch.js → mock-fetch.ts} +2 -3
  71. package/src/fetch/{types.js → types.ts} +5 -6
  72. package/src/fixtures/__tests__/{fixtures.test.js → fixtures.test.tsx} +6 -4
  73. package/src/fixtures/{fixtures.basic.stories.js → fixtures.basic.stories.tsx} +16 -13
  74. package/src/fixtures/{fixtures.defaultwrapper.stories.js → fixtures.defaultwrapper.stories.tsx} +8 -8
  75. package/src/fixtures/{fixtures.js → fixtures.tsx} +16 -11
  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} +17 -18
  78. package/src/gql/__tests__/{mock-gql-fetch.test.js → mock-gql-fetch.test.tsx} +44 -45
  79. package/src/gql/__tests__/{wb-data-integration.test.js → wb-data-integration.test.tsx} +21 -22
  80. package/src/gql/{gql-request-matches-mock.js → gql-request-matches-mock.ts} +1 -4
  81. package/src/gql/{mock-gql-fetch.js → mock-gql-fetch.ts} +3 -6
  82. package/src/gql/types.ts +33 -0
  83. package/src/harness/__tests__/{hook-harness.test.js → hook-harness.test.ts} +5 -6
  84. package/src/harness/__tests__/{make-hook-harness.test.js → make-hook-harness.test.tsx} +10 -11
  85. package/src/harness/__tests__/{make-test-harness.test.js → make-test-harness.test.tsx} +4 -5
  86. package/src/harness/__tests__/{render-adapters.test.js → render-adapters.test.tsx} +8 -9
  87. package/src/harness/__tests__/{test-harness.test.js → test-harness.test.ts} +5 -6
  88. package/src/harness/__tests__/{types.flowtest.js → types.flowtest.tsx} +24 -27
  89. package/src/harness/adapters/__tests__/{css.test.js → css.test.tsx} +2 -3
  90. package/src/harness/adapters/__tests__/{data.test.js → data.test.tsx} +4 -3
  91. package/src/harness/adapters/__tests__/{portal.test.js → portal.test.tsx} +0 -1
  92. package/src/harness/adapters/__tests__/{router.test.js → router.test.tsx} +75 -56
  93. package/src/harness/adapters/{adapters.js → adapters.ts} +1 -2
  94. package/src/harness/adapters/{css.js → css.tsx} +21 -18
  95. package/src/harness/adapters/{data.js → data.tsx} +7 -5
  96. package/src/harness/adapters/{portal.js → portal.tsx} +3 -4
  97. package/src/harness/adapters/router.tsx +218 -0
  98. package/src/harness/{hook-harness.js → hook-harness.ts} +2 -3
  99. package/src/harness/{make-hook-harness.js → make-hook-harness.ts} +6 -6
  100. package/src/harness/{make-test-harness.js → make-test-harness.tsx} +17 -20
  101. package/src/harness/{render-adapters.js → render-adapters.ts} +4 -4
  102. package/src/harness/{test-harness.js → test-harness.ts} +1 -12
  103. package/src/harness/{types.js → types.ts} +14 -12
  104. package/src/{index.js → index.ts} +0 -2
  105. package/src/{mock-requester.js → mock-requester.ts} +4 -3
  106. package/src/{respond-with.js → respond-with.ts} +42 -37
  107. package/src/{response-impl.js → response-impl.ts} +1 -2
  108. package/src/{settle-controller.js → settle-controller.ts} +9 -7
  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/gql/types.js +0 -34
  140. package/src/harness/adapters/router.js +0 -206
  141. package/src/types.js +0 -39
  142. /package/src/harness/adapters/__tests__/__snapshots__/{router.test.js.snap → router.test.tsx.snap} +0 -0
@@ -1,4 +1,3 @@
1
- // @flow
2
1
  import * as React from "react";
3
2
 
4
3
  import {renderAdapters} from "./render-adapters";
@@ -20,40 +19,38 @@ import type {TestHarnessAdapters, TestHarnessConfigs} from "./types";
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,4 +1,3 @@
1
- // @flow
2
1
  import * as React from "react";
3
2
 
4
3
  import type {TestHarnessConfigs, TestHarnessAdapters} from "./types";
@@ -6,11 +5,11 @@ import type {TestHarnessConfigs, TestHarnessAdapters} from "./types";
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
  };
@@ -1,11 +1,6 @@
1
- // @flow
2
- import * as React from "react";
3
-
4
1
  import {makeTestHarness} from "./make-test-harness";
5
2
  import {DefaultAdapters, DefaultConfigs} from "./adapters/adapters";
6
3
 
7
- import type {TestHarnessConfigs} from "./types";
8
-
9
4
  /**
10
5
  * Wrap a component with a test harness using Wonder Blocks default adapters.
11
6
  *
@@ -15,10 +10,4 @@ import type {TestHarnessConfigs} from "./types";
15
10
  * configurations, use `makeTestHarness` to create a new `testHarness`
16
11
  * function.
17
12
  */
18
- export const testHarness: <-TProps, +Instance = mixed>(
19
- Component: React.AbstractComponent<TProps, Instance>,
20
- configs?: $Shape<TestHarnessConfigs<typeof DefaultAdapters>>,
21
- ) => React.AbstractComponent<TProps, Instance> = makeTestHarness(
22
- DefaultAdapters,
23
- DefaultConfigs,
24
- );
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
  >;
@@ -1,5 +1,3 @@
1
- // @flow
2
-
3
1
  // Fixtures framework
4
2
  export {fixtures} from "./fixtures/fixtures";
5
3
  export type {FixtureFn, FixtureProps, GetPropsOptions} from "./fixtures/types";
@@ -1,4 +1,3 @@
1
- // @flow
2
1
  import type {MockResponse} from "./respond-with";
3
2
  import type {OperationMock, OperationMatcher, MockFn} from "./types";
4
3
 
@@ -7,7 +6,7 @@ import type {OperationMock, OperationMatcher, MockFn} from "./types";
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
 
@@ -1,4 +1,3 @@
1
- // @flow
2
1
  import {SettleSignal} from "./settle-signal";
3
2
  import {ResponseImpl} from "./response-impl";
4
3
  import type {GraphQLJson} from "./types";
@@ -8,33 +7,33 @@ import type {GraphQLJson} from "./types";
8
7
  // the graphql mocking framework might want to assert a response is returning
9
8
  // the expected data structure. We could use `opaque` but that would then
10
9
  // hide the `toPromise` call we want to provide.
11
- /* eslint-disable no-unused-vars */
10
+ /* eslint-disable @typescript-eslint/no-unused-vars */
12
11
  /**
13
12
  * Describes a mock response to a fetch request.
14
13
  */
15
- export type MockResponse<TData> = {|
14
+ export type MockResponse<TData> = {
16
15
  /**
17
16
  * Create a promise from the mocked response.
18
17
  *
19
18
  * If a signal was provided when the mock response was created, the promise
20
19
  * will only settle to resolution or rejection if the signal is raised.
21
20
  */
22
- +toPromise: () => Promise<Response>,
23
- |};
24
- /* eslint-enable no-unused-vars */
21
+ readonly toPromise: () => Promise<Response>;
22
+ };
23
+ /* eslint-enable @typescript-eslint/no-unused-vars */
25
24
 
26
25
  type InternalMockResponse =
27
- | {|
28
- +type: "text",
29
- +text: string | (() => string),
30
- +statusCode: number,
31
- +signal: ?SettleSignal,
32
- |}
33
- | {|
34
- +type: "reject",
35
- +error: Error | (() => Error),
36
- +signal: ?SettleSignal,
37
- |};
26
+ | {
27
+ readonly type: "text";
28
+ readonly text: string | (() => string);
29
+ readonly statusCode: number;
30
+ readonly signal: SettleSignal | null | undefined;
31
+ }
32
+ | {
33
+ readonly type: "reject";
34
+ readonly error: Error | (() => Error);
35
+ readonly signal: SettleSignal | null | undefined;
36
+ };
38
37
 
39
38
  /**
40
39
  * Helper for creating a text-based mock response.
@@ -42,7 +41,7 @@ type InternalMockResponse =
42
41
  const textResponse = <TData>(
43
42
  text: string | (() => string),
44
43
  statusCode: number,
45
- signal: ?SettleSignal,
44
+ signal?: SettleSignal | null,
46
45
  ): MockResponse<TData> => ({
47
46
  toPromise: () =>
48
47
  makeMockResponse({
@@ -58,8 +57,8 @@ const textResponse = <TData>(
58
57
  */
59
58
  const rejectResponse = (
60
59
  error: Error | (() => Error),
61
- signal: ?SettleSignal,
62
- ): MockResponse<empty> => ({
60
+ signal?: SettleSignal | null,
61
+ ): MockResponse<never> => ({
63
62
  toPromise: () =>
64
63
  makeMockResponse({
65
64
  type: "reject",
@@ -78,25 +77,25 @@ export const RespondWith = Object.freeze({
78
77
  */
79
78
  text: <TData = string>(
80
79
  text: string,
81
- statusCode: number = 200,
82
- signal: ?SettleSignal = null,
80
+ statusCode = 200,
81
+ signal: SettleSignal | null = null,
83
82
  ): MockResponse<TData> => textResponse<TData>(text, statusCode, signal),
84
83
 
85
84
  /**
86
85
  * Response with JSON body and status code 200.
87
86
  */
88
- json: <TJson: {...}>(
87
+ json: <TJson extends Record<any, any>>(
89
88
  json: TJson,
90
- signal: ?SettleSignal = null,
89
+ signal: SettleSignal | null = null,
91
90
  ): MockResponse<TJson> =>
92
91
  textResponse<TJson>(() => JSON.stringify(json), 200, signal),
93
92
 
94
93
  /**
95
94
  * Response with GraphQL data JSON body and status code 200.
96
95
  */
97
- graphQLData: <TData: {...}>(
96
+ graphQLData: <TData extends Record<any, any>>(
98
97
  data: TData,
99
- signal: ?SettleSignal = null,
98
+ signal: SettleSignal | null = null,
100
99
  ): MockResponse<GraphQLJson<TData>> =>
101
100
  textResponse<GraphQLJson<TData>>(
102
101
  () => JSON.stringify({data}),
@@ -107,13 +106,13 @@ export const RespondWith = Object.freeze({
107
106
  /**
108
107
  * Response with body that will not parse as JSON and status code 200.
109
108
  */
110
- unparseableBody: (signal: ?SettleSignal = null): MockResponse<any> =>
109
+ unparseableBody: (signal: SettleSignal | null = null): MockResponse<any> =>
111
110
  textResponse("INVALID JSON", 200, signal),
112
111
 
113
112
  /**
114
113
  * Rejects with an AbortError to simulate an aborted request.
115
114
  */
116
- abortedRequest: (signal: ?SettleSignal = null): MockResponse<any> =>
115
+ abortedRequest: (signal: SettleSignal | null = null): MockResponse<any> =>
117
116
  rejectResponse(() => {
118
117
  const abortError = new Error("Mock request aborted");
119
118
  abortError.name = "AbortError";
@@ -123,8 +122,10 @@ export const RespondWith = Object.freeze({
123
122
  /**
124
123
  * Rejects with the given error.
125
124
  */
126
- reject: (error: Error, signal: ?SettleSignal = null): MockResponse<any> =>
127
- rejectResponse(error, signal),
125
+ reject: (
126
+ error: Error,
127
+ signal: SettleSignal | null = null,
128
+ ): MockResponse<any> => rejectResponse(error, signal),
128
129
 
129
130
  /**
130
131
  * A non-200 status code with empty text body.
@@ -132,7 +133,7 @@ export const RespondWith = Object.freeze({
132
133
  */
133
134
  errorStatusCode: (
134
135
  statusCode: number,
135
- signal: ?SettleSignal = null,
136
+ signal: SettleSignal | null = null,
136
137
  ): MockResponse<any> => {
137
138
  if (statusCode < 300) {
138
139
  throw new Error(`${statusCode} is not a valid error status code`);
@@ -143,7 +144,7 @@ export const RespondWith = Object.freeze({
143
144
  /**
144
145
  * Response body that is valid JSON but not a valid GraphQL response.
145
146
  */
146
- nonGraphQLBody: (signal: ?SettleSignal = null): MockResponse<any> =>
147
+ nonGraphQLBody: (signal: SettleSignal | null = null): MockResponse<any> =>
147
148
  textResponse(
148
149
  () =>
149
150
  JSON.stringify({
@@ -158,8 +159,8 @@ export const RespondWith = Object.freeze({
158
159
  * Response that is a GraphQL errors response with status code 200.
159
160
  */
160
161
  graphQLErrors: (
161
- errorMessages: $ReadOnlyArray<string>,
162
- signal: ?SettleSignal = null,
162
+ errorMessages: ReadonlyArray<string>,
163
+ signal: SettleSignal | null = null,
163
164
  ): MockResponse<GraphQLJson<any>> =>
164
165
  textResponse<GraphQLJson<any>>(
165
166
  () =>
@@ -173,7 +174,10 @@ export const RespondWith = Object.freeze({
173
174
  ),
174
175
  });
175
176
 
176
- const callOnSettled = (signal: ?SettleSignal, fn: () => void): void => {
177
+ const callOnSettled = (
178
+ signal: SettleSignal | null | undefined,
179
+ fn: () => void,
180
+ ): void => {
177
181
  if (signal == null || signal.settled) {
178
182
  fn();
179
183
  return;
@@ -196,7 +200,7 @@ const makeMockResponse = (
196
200
 
197
201
  switch (response.type) {
198
202
  case "text":
199
- return new Promise((resolve, reject) => {
203
+ return new Promise((resolve, reject: (error?: any) => void) => {
200
204
  callOnSettled(signal, () => {
201
205
  const text =
202
206
  typeof response.text === "function"
@@ -209,7 +213,7 @@ const makeMockResponse = (
209
213
  });
210
214
 
211
215
  case "reject":
212
- return new Promise((resolve, reject) => {
216
+ return new Promise((resolve, reject: (error?: any) => void) => {
213
217
  callOnSettled(signal, () =>
214
218
  reject(
215
219
  response.error instanceof Error
@@ -224,6 +228,7 @@ const makeMockResponse = (
224
228
  if (process.env.NODE_ENV !== "production") {
225
229
  // If we're not in production, give an immediate signal that the
226
230
  // dev forgot to support this new type.
231
+ // @ts-expect-error [FEI-5019] - TS2339 - Property 'type' does not exist on type 'never'.
227
232
  throw new Error(`Unknown response type: ${response.type}`);
228
233
  }
229
234
  // Production; assume a rejection.
@@ -1,9 +1,8 @@
1
- // @flow
2
-
3
1
  // We need a version of Response. When we're in Jest JSDOM environment or a
4
2
  // version of Node that supports the fetch API (17 and up, possibly with
5
3
  // --experimental-fetch flag), then we're good, but otherwise we need an
6
4
  // implementation, so this uses node-fetch as a peer dependency and uses that
7
5
  // to provide the implementation if we don't already have one.
8
6
  export const ResponseImpl: typeof Response =
7
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
9
8
  typeof Response === "undefined" ? require("node-fetch").Response : Response;
@@ -1,19 +1,21 @@
1
- // @flow
2
1
  import {SettleSignal} from "./settle-signal";
3
2
 
4
3
  /**
5
4
  * A controller for the `RespondWith` API to control response settlement.
6
5
  */
7
6
  export class SettleController {
8
- #settleFn: () => void;
9
- #signal: SettleSignal;
7
+ // `flowgen` can't handle `#` so we're using `_` and TypeScript `private` access modifier.
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;
11
+ private _signal: SettleSignal;
10
12
 
11
13
  constructor() {
12
14
  // Create our signal.
13
15
  // We pass in a method to capture it's settle function so that
14
16
  // only we can call it.
15
- this.#signal = new SettleSignal(
16
- (settleFn) => (this.#settleFn = settleFn),
17
+ this._signal = new SettleSignal(
18
+ (settleFn: () => void) => (this._settleFn = settleFn),
17
19
  );
18
20
  }
19
21
 
@@ -21,7 +23,7 @@ export class SettleController {
21
23
  * The signal to pass to the `RespondWith` API.
22
24
  */
23
25
  get signal(): SettleSignal {
24
- return this.#signal;
26
+ return this._signal;
25
27
  }
26
28
 
27
29
  /**
@@ -30,6 +32,6 @@ export class SettleController {
30
32
  * @throws {Error} if the signal has already been settled.
31
33
  */
32
34
  settle(): void {
33
- this.#settleFn();
35
+ this._settleFn();
34
36
  }
35
37
  }
@@ -1,4 +1,3 @@
1
- // @flow
2
1
  /**
3
2
  * A signal for controlling the `RespondWith` API responses.
4
3
  *
@@ -6,19 +5,23 @@
6
5
  * complex test scenarios.
7
6
  */
8
7
  export class SettleSignal extends EventTarget {
9
- #settled: boolean = false;
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
+ private _settled = false;
10
11
 
11
- constructor(setSettleFn: ?(settleFn: () => void) => mixed = null) {
12
+ constructor(
13
+ setSettleFn: ((settleFn: () => void) => unknown) | null = null,
14
+ ) {
12
15
  super();
13
16
 
14
17
  // If we were given a function, we call it with a method that will
15
18
  // settle ourselves. This allows the appropriate SettleController
16
19
  // to be in charge of settling this instance.
17
20
  setSettleFn?.(() => {
18
- if (this.#settled) {
21
+ if (this._settled) {
19
22
  throw new Error("SettleSignal already settled");
20
23
  }
21
- this.#settled = true;
24
+ this._settled = true;
22
25
  this.dispatchEvent(new Event("settled"));
23
26
  });
24
27
  }
@@ -28,7 +31,7 @@ export class SettleSignal extends EventTarget {
28
31
  */
29
32
  static settle(): SettleSignal {
30
33
  const signal = new SettleSignal();
31
- signal.#settled = true;
34
+ signal._settled = true;
32
35
  return signal;
33
36
  }
34
37
 
@@ -36,6 +39,6 @@ export class SettleSignal extends EventTarget {
36
39
  * Has this signal been settled yet?
37
40
  */
38
41
  get settled(): boolean {
39
- return this.#settled;
42
+ return this._settled;
40
43
  }
41
44
  }
package/src/types.ts ADDED
@@ -0,0 +1,40 @@
1
+ import type {MockResponse} from "./respond-with";
2
+
3
+ /**
4
+ * A valid GraphQL response as supported by our mocking framework.
5
+ * Note that we don't currently support both data and errors being set.
6
+ */
7
+ export type GraphQLJson<TData extends Record<any, any>> =
8
+ | {
9
+ data: TData;
10
+ }
11
+ | {
12
+ errors: Array<{
13
+ message: string;
14
+ }>;
15
+ };
16
+
17
+ export type MockFn<TOperationType> = {
18
+ (...args: Array<any>): Promise<Response>;
19
+ mockOperation: MockOperationFn<TOperationType>;
20
+ mockOperationOnce: MockOperationFn<TOperationType>;
21
+ };
22
+
23
+ export type OperationMock<TOperation> = {
24
+ operation: TOperation;
25
+ onceOnly: boolean;
26
+ used: boolean;
27
+ response: () => Promise<Response>;
28
+ };
29
+
30
+ export type OperationMatcher<TOperation> = (
31
+ operation: TOperation,
32
+ ...args: Array<any>
33
+ ) => boolean;
34
+
35
+ export type MockOperationFn<TOperationType> = <
36
+ TOperation extends TOperationType,
37
+ >(
38
+ operation: TOperation,
39
+ response: MockResponse<any>,
40
+ ) => MockFn<TOperationType>;
package/tsconfig.json ADDED
@@ -0,0 +1,11 @@
1
+ {
2
+ "exclude": ["dist"],
3
+ "extends": "../tsconfig-shared.json",
4
+ "compilerOptions": {
5
+ "outDir": "./dist",
6
+ "rootDir": "src",
7
+ },
8
+ "references": [
9
+ {"path": "../wonder-blocks-data"},
10
+ ]
11
+ }