@khanacademy/wonder-blocks-testing 7.1.10 → 7.1.12
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 +31 -0
- package/dist/es/index.js +183 -120
- package/dist/fetch/fetch-request-matches-mock.d.ts +5 -0
- package/dist/fetch/fetch-request-matches-mock.js.flow +17 -0
- package/dist/fetch/mock-fetch.d.ts +5 -0
- package/dist/fetch/mock-fetch.js.flow +13 -0
- package/dist/fetch/types.d.ts +9 -0
- package/dist/fetch/types.js.flow +19 -0
- package/dist/fixtures/fixtures.basic.stories.d.ts +13 -0
- package/dist/fixtures/fixtures.basic.stories.js.flow +22 -0
- package/dist/fixtures/fixtures.d.ts +19 -0
- package/dist/fixtures/fixtures.defaultwrapper.stories.d.ts +9 -0
- package/dist/fixtures/fixtures.defaultwrapper.stories.js.flow +17 -0
- package/dist/fixtures/fixtures.js.flow +33 -0
- package/dist/fixtures/types.d.ts +36 -0
- package/dist/fixtures/types.js.flow +37 -0
- package/dist/gql/gql-request-matches-mock.d.ts +3 -0
- package/dist/gql/gql-request-matches-mock.js.flow +15 -0
- package/dist/gql/mock-gql-fetch.d.ts +5 -0
- package/dist/gql/mock-gql-fetch.js.flow +13 -0
- package/dist/gql/types.d.ts +15 -0
- package/dist/gql/types.js.flow +39 -0
- package/dist/harness/adapters/adapters.d.ts +35 -0
- package/dist/harness/adapters/adapters.js.flow +69 -0
- package/dist/harness/adapters/css.d.ts +12 -0
- package/dist/harness/adapters/css.js.flow +23 -0
- package/dist/harness/adapters/data.d.ts +18 -0
- package/dist/harness/adapters/data.js.flow +32 -0
- package/dist/harness/adapters/portal.d.ts +12 -0
- package/dist/harness/adapters/portal.js.flow +18 -0
- package/dist/harness/adapters/router.d.ts +94 -0
- package/dist/harness/adapters/router.js.flow +122 -0
- package/dist/harness/hook-harness.d.ts +13 -0
- package/dist/harness/hook-harness.js.flow +23 -0
- package/dist/harness/make-hook-harness.d.ts +17 -0
- package/dist/harness/make-hook-harness.js.flow +42 -0
- package/dist/harness/make-test-harness.d.ts +18 -0
- package/dist/harness/make-test-harness.js.flow +48 -0
- package/dist/harness/render-adapters.d.ts +6 -0
- package/dist/harness/render-adapters.js.flow +24 -0
- package/dist/harness/test-harness.d.ts +32 -0
- package/dist/harness/test-harness.js.flow +83 -0
- package/dist/harness/types.d.ts +46 -0
- package/dist/harness/types.js.flow +66 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +185 -127
- package/dist/index.js.flow +26 -2
- package/dist/mock-requester.d.ts +5 -0
- package/dist/mock-requester.js.flow +22 -0
- package/dist/respond-with.d.ts +57 -0
- package/dist/respond-with.js.flow +91 -0
- package/dist/response-impl.d.ts +1 -0
- package/dist/response-impl.js.flow +8 -0
- package/dist/settle-controller.d.ts +19 -0
- package/dist/settle-controller.js.flow +26 -0
- package/dist/settle-signal.d.ts +18 -0
- package/dist/settle-signal.js.flow +26 -0
- package/dist/types.d.ts +25 -0
- package/dist/types.js.flow +46 -0
- package/package.json +6 -6
- package/src/__tests__/{mock-requester.test.js → mock-requester.test.ts} +1 -2
- package/src/__tests__/{respond-with.test.js → respond-with.test.ts} +0 -1
- package/src/__tests__/response-impl.test.js +1 -1
- package/src/__tests__/{settle-controller.test.js → settle-controller.test.ts} +0 -1
- package/src/__tests__/{settle-signal.test.js → settle-signal.test.ts} +0 -1
- package/src/fetch/__tests__/__snapshots__/{mock-fetch.test.js.snap → mock-fetch.test.ts.snap} +3 -3
- package/src/fetch/__tests__/{fetch-request-matches-mock.test.js → fetch-request-matches-mock.test.ts} +5 -6
- package/src/fetch/__tests__/{mock-fetch.test.js → mock-fetch.test.ts} +1 -2
- package/src/fetch/{fetch-request-matches-mock.js → fetch-request-matches-mock.ts} +3 -2
- package/src/fetch/{mock-fetch.js → mock-fetch.ts} +2 -3
- package/src/fetch/{types.js → types.ts} +5 -6
- package/src/fixtures/__tests__/{fixtures.test.js → fixtures.test.tsx} +6 -4
- package/src/fixtures/{fixtures.basic.stories.js → fixtures.basic.stories.tsx} +16 -13
- package/src/fixtures/{fixtures.defaultwrapper.stories.js → fixtures.defaultwrapper.stories.tsx} +8 -8
- package/src/fixtures/{fixtures.js → fixtures.tsx} +16 -11
- package/src/fixtures/{types.js → types.ts} +9 -13
- package/src/gql/__tests__/{gql-request-matches-mock.test.js → gql-request-matches-mock.test.ts} +17 -18
- package/src/gql/__tests__/{mock-gql-fetch.test.js → mock-gql-fetch.test.tsx} +44 -45
- package/src/gql/__tests__/{wb-data-integration.test.js → wb-data-integration.test.tsx} +21 -22
- package/src/gql/{gql-request-matches-mock.js → gql-request-matches-mock.ts} +1 -4
- package/src/gql/{mock-gql-fetch.js → mock-gql-fetch.ts} +3 -6
- package/src/gql/types.ts +33 -0
- package/src/harness/__tests__/{hook-harness.test.js → hook-harness.test.ts} +5 -6
- package/src/harness/__tests__/{make-hook-harness.test.js → make-hook-harness.test.tsx} +10 -11
- package/src/harness/__tests__/{make-test-harness.test.js → make-test-harness.test.tsx} +4 -5
- package/src/harness/__tests__/{render-adapters.test.js → render-adapters.test.tsx} +8 -9
- package/src/harness/__tests__/{test-harness.test.js → test-harness.test.ts} +5 -6
- package/src/harness/__tests__/{types.flowtest.js → types.flowtest.tsx} +24 -27
- package/src/harness/adapters/__tests__/{css.test.js → css.test.tsx} +2 -3
- package/src/harness/adapters/__tests__/{data.test.js → data.test.tsx} +4 -3
- package/src/harness/adapters/__tests__/{portal.test.js → portal.test.tsx} +0 -1
- package/src/harness/adapters/__tests__/{router.test.js → router.test.tsx} +75 -56
- package/src/harness/adapters/{adapters.js → adapters.ts} +1 -2
- package/src/harness/adapters/{css.js → css.tsx} +21 -18
- package/src/harness/adapters/{data.js → data.tsx} +7 -5
- package/src/harness/adapters/{portal.js → portal.tsx} +3 -4
- package/src/harness/adapters/router.tsx +218 -0
- package/src/harness/{hook-harness.js → hook-harness.ts} +2 -3
- package/src/harness/{make-hook-harness.js → make-hook-harness.ts} +6 -6
- package/src/harness/{make-test-harness.js → make-test-harness.tsx} +17 -20
- package/src/harness/{render-adapters.js → render-adapters.ts} +4 -4
- package/src/harness/{test-harness.js → test-harness.ts} +1 -12
- package/src/harness/{types.js → types.ts} +14 -12
- package/src/{index.js → index.ts} +0 -2
- package/src/{mock-requester.js → mock-requester.ts} +4 -3
- package/src/{respond-with.js → respond-with.ts} +42 -37
- package/src/{response-impl.js → response-impl.ts} +1 -2
- package/src/{settle-controller.js → settle-controller.ts} +9 -7
- package/src/{settle-signal.js → settle-signal.ts} +10 -7
- package/src/types.ts +40 -0
- package/tsconfig.json +11 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/src/__docs__/_overview_.stories.mdx +0 -18
- package/src/__docs__/_overview_fixtures.stories.mdx +0 -18
- package/src/__docs__/_overview_mocking.stories.mdx +0 -14
- package/src/__docs__/_overview_test_harness.stories.mdx +0 -18
- package/src/__docs__/exports.fixtures.stories.mdx +0 -31
- package/src/__docs__/exports.harness-adapters.stories.mdx +0 -187
- package/src/__docs__/exports.hook-harness.stories.mdx +0 -22
- package/src/__docs__/exports.make-hook-harness.stories.mdx +0 -25
- package/src/__docs__/exports.make-test-harness.stories.mdx +0 -28
- package/src/__docs__/exports.mock-fetch.stories.mdx +0 -40
- package/src/__docs__/exports.mock-gql-fetch.stories.mdx +0 -64
- package/src/__docs__/exports.respond-with.stories.mdx +0 -84
- package/src/__docs__/exports.settle-controller.stories.mdx +0 -32
- package/src/__docs__/exports.test-harness.stories.mdx +0 -23
- package/src/__docs__/types.fetch-mock-fn.stories.mdx +0 -22
- package/src/__docs__/types.fetch-mock-operation.stories.mdx +0 -18
- package/src/__docs__/types.fixture-fn.stories.mdx +0 -46
- package/src/__docs__/types.fixture-props.stories.mdx +0 -20
- package/src/__docs__/types.get-props-options.stories.mdx +0 -52
- package/src/__docs__/types.gql-fetch-mock-fn.stories.mdx +0 -27
- package/src/__docs__/types.gql-mock-operation.stories.mdx +0 -26
- package/src/__docs__/types.mock-response.stories.mdx +0 -22
- package/src/__docs__/types.test-harness-adapter.stories.mdx +0 -21
- package/src/__docs__/types.test-harness-adapters.stories.mdx +0 -46
- package/src/__docs__/types.test-harness-config.stories.mdx +0 -18
- package/src/__docs__/types.test-harness-configs.stories.mdx +0 -59
- package/src/gql/types.js +0 -34
- package/src/harness/adapters/router.js +0 -206
- package/src/types.js +0 -39
- /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
|
|
22
|
+
export const makeTestHarness = <TAdapters extends TestHarnessAdapters>(
|
|
24
23
|
adapters: TAdapters,
|
|
25
24
|
defaultConfigs: TestHarnessConfigs<TAdapters>,
|
|
26
|
-
): (
|
|
27
|
-
Component: React.
|
|
28
|
-
configs?:
|
|
29
|
-
) => React.
|
|
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.
|
|
34
|
+
* @param {React.ComponentType<TProps>} component The
|
|
34
35
|
* component to be wrapped.
|
|
35
|
-
* @param {
|
|
36
|
+
* @param {Partial<TestHarnessConfigs<TAdapters>>} [configs] Any adapter
|
|
36
37
|
* configuration that you want to override from the `defaultConfigs` values.
|
|
37
38
|
*/
|
|
38
|
-
return
|
|
39
|
-
Component: React.
|
|
40
|
-
configs?:
|
|
41
|
-
): React.
|
|
42
|
-
|
|
43
|
-
|
|
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
|
|
8
|
+
export const renderAdapters = <TAdapters extends TestHarnessAdapters>(
|
|
10
9
|
adapters: TAdapters,
|
|
11
10
|
configs: TestHarnessConfigs<TAdapters>,
|
|
12
|
-
children: React.
|
|
13
|
-
): React.
|
|
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
|
|
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.
|
|
7
|
+
children: React.ReactNode,
|
|
9
8
|
config: TConfig,
|
|
10
|
-
) => React.
|
|
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>(
|
|
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
|
-
|
|
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>` -
|
|
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
|
|
56
|
+
export type TestHarnessConfigs<TAdapters extends TestHarnessAdapters> = ObjMap<
|
|
55
57
|
TAdapters,
|
|
56
|
-
ExtractMaybeConfig
|
|
58
|
+
ExtractMaybeConfig
|
|
57
59
|
>;
|
|
@@ -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
|
|
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
|
-
${
|
|
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
|
-
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
| {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
|
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
|
|
62
|
-
): MockResponse<
|
|
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
|
|
82
|
-
signal:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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: (
|
|
127
|
-
|
|
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:
|
|
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:
|
|
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:
|
|
162
|
-
signal:
|
|
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 = (
|
|
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
|
-
|
|
9
|
-
|
|
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
|
|
16
|
-
(settleFn) => (this
|
|
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
|
|
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
|
|
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
|
-
|
|
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(
|
|
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
|
|
21
|
+
if (this._settled) {
|
|
19
22
|
throw new Error("SettleSignal already settled");
|
|
20
23
|
}
|
|
21
|
-
this
|
|
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
|
|
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
|
|
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>;
|