@khanacademy/wonder-blocks-testing 0.0.2

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.
@@ -0,0 +1,182 @@
1
+ // @flow
2
+ import * as React from "react";
3
+ import type {StorybookOptions} from "./adapters/storybook.js";
4
+
5
+ /**
6
+ * Options injected to the function that returns the fixture props.
7
+ */
8
+ export type GetPropsOptions = {|
9
+ /**
10
+ * A function to call that will log output.
11
+ */
12
+ log: (message: string, ...args: Array<any>) => void,
13
+ |};
14
+
15
+ /**
16
+ * Adapter options keyed by the adapter name.
17
+ */
18
+ export type AdapterOptions = {|
19
+ storybook?: StorybookOptions,
20
+ [adapterName: string]: {...},
21
+ |};
22
+
23
+ /**
24
+ * Options to describe a collection of fixtures.
25
+ */
26
+ export type FixturesOptions<TProps: {...}> = {|
27
+ /**
28
+ * The component being tested by the fixtures.
29
+ */
30
+ component: React.ComponentType<TProps>,
31
+
32
+ /**
33
+ * Optional title of the fixture collection.
34
+ *
35
+ * Adapters may enforce a title, otherwise the component name is used.
36
+ */
37
+ title?: string,
38
+
39
+ /**
40
+ * Optional description of the fixture collection.
41
+ */
42
+ description?: string,
43
+
44
+ /**
45
+ * Optional default wrapper to apply around the component under test.
46
+ */
47
+ defaultWrapper?: React.ComponentType<TProps>,
48
+
49
+ /**
50
+ * Additional options to apply to specific adapters.
51
+ */
52
+ additionalAdapterOptions?: AdapterOptions,
53
+ |};
54
+
55
+ /**
56
+ * Describes a single fixture.
57
+ */
58
+ export type AdapterFixtureOptions<TProps: {...}> = {|
59
+ /**
60
+ * Description of the fixture.
61
+ */
62
+ +description: string,
63
+
64
+ /**
65
+ * Method to obtain props for the fixture.
66
+ */
67
+ +getProps: (options: $ReadOnly<GetPropsOptions>) => $ReadOnly<TProps>,
68
+
69
+ /**
70
+ * The component to render for this fixture.
71
+ */
72
+ +component: React.ComponentType<TProps>,
73
+ |};
74
+
75
+ // TODO(somewhatabstract): Allow for adapters to extend group/fixture options
76
+ // with specific support. For example, storybook subcomponents, etc.?
77
+
78
+ /**
79
+ * Describes a group of fixtures.
80
+ */
81
+ export type AdapterGroupOptions = {|
82
+ /**
83
+ * The title of the group.
84
+ */
85
+ +title: string,
86
+
87
+ /**
88
+ * Description of the group.
89
+ */
90
+ +description: ?string,
91
+ |};
92
+
93
+ /**
94
+ * Describes the props that an adapter will inject for custom wrappers.
95
+ */
96
+ export type CustomWrapperProps<TProps: {...}> = {|
97
+ /**
98
+ * The fixture props for the component to be rendered.
99
+ */
100
+ props: TProps,
101
+
102
+ /**
103
+ * The component to render.
104
+ */
105
+ component: React.ComponentType<TProps>,
106
+
107
+ /**
108
+ * The log callback for logging information.
109
+ */
110
+ log: (message: string, ...args: Array<any>) => mixed,
111
+ |};
112
+
113
+ /**
114
+ * Declares the API for describing a fixture group provided by an adapter.
115
+ */
116
+ export interface AdapterGroup<
117
+ TProps: {...},
118
+ TAdapterOptions: {...},
119
+ TAdapterExports: {...},
120
+ > {
121
+ /**
122
+ * Declare a fixture.
123
+ */
124
+ declareFixture(options: $ReadOnly<AdapterFixtureOptions<TProps>>): void;
125
+
126
+ /**
127
+ * Close the group and obtain the exports, if the adapter requires any.
128
+ *
129
+ * @param {Options} adapterOptions Some options to pass to the adapter.
130
+ * Allows callers to tailor things to a specific adapter. How these options
131
+ * are used is adapter-specific.
132
+ *
133
+ * @returns {?Exports} The exports that the adapter requires fixture files
134
+ * to export.
135
+ */
136
+ closeGroup(
137
+ adapterOptions: $ReadOnly<Partial<TAdapterOptions>>,
138
+ ): ?$ReadOnly<TAdapterExports>;
139
+ }
140
+
141
+ /**
142
+ * Declares the API for an adapter.
143
+ */
144
+ export interface Adapter<TAdapterOptions: {...}, TAdapterExports: {...}> {
145
+ /**
146
+ * The name of the adapter.
147
+ */
148
+ get name(): string;
149
+
150
+ /**
151
+ * Declare a fixture group.
152
+ *
153
+ * @returns {AdapterGroup<TProps, TAdapterOptions, TAdapterExports>} The
154
+ * declared group.
155
+ */
156
+ declareGroup<TProps: {...}>(
157
+ options: $ReadOnly<AdapterGroupOptions>,
158
+ ): AdapterGroup<TProps, TAdapterOptions, TAdapterExports>;
159
+ }
160
+
161
+ /**
162
+ * Describes the configuration for the fixture framework.
163
+ */
164
+ export type Configuration<TAdapterOptions: {...}, TAdapterExports: {...}> = {|
165
+ /**
166
+ * The adapter to use for declaring fixtures.
167
+ */
168
+ +adapter: Adapter<TAdapterOptions, TAdapterExports>,
169
+
170
+ /**
171
+ * Default options to apply to every fixture group.
172
+ *
173
+ * Each top-level option in this object will be merged with the equivalent
174
+ * top-level option that a specific fixture requests. Where collisions
175
+ * occur, the fixture options win.
176
+ */
177
+ +defaultAdapterOptions?: $ReadOnly<Partial<TAdapterOptions>>,
178
+ |};
179
+
180
+ export type AdapterFactory<TAdapterOptions: {...}, TAdapterExports: {...}> = (
181
+ MountingComponent: ?React.ComponentType<CustomWrapperProps<any>>,
182
+ ) => Adapter<TAdapterOptions, TAdapterExports>;
package/src/index.js ADDED
@@ -0,0 +1,10 @@
1
+ // @flow
2
+
3
+ // Fixtures framework
4
+ export * as adapters from "./fixtures/adapters/adapters.js";
5
+ export {fixtures} from "./fixtures/fixtures.js";
6
+ export {setup as setupFixtures} from "./fixtures/setup.js";
7
+
8
+ // Jest
9
+ // TODO(somewhatabstract): To be moved to wonder stuff
10
+ export {isolateModules} from "./jest/isolate-modules.js";
@@ -0,0 +1,31 @@
1
+ // @flow
2
+ // Opt this file out of coverage because it's super hard to test.
3
+ /* istanbul ignore file */
4
+
5
+ /**
6
+ * Isolate imports within a given action using jest.isolateModules.
7
+ *
8
+ * This is a helper for the `jest.isolateModules` API, allowing
9
+ * code to avoid the clunky closure syntax in their tests.
10
+ *
11
+ * @param {() => T} action The action that contains the isolated module imports.
12
+ * We do it this way so that any `require` calls are relative to the calling
13
+ * code and not this function. Note that we don't support promises here to
14
+ * discourage dynamic `import` use, which doesn't play well with standard
15
+ * jest yet.
16
+ */
17
+ export const isolateModules = <T>(action: () => T): T => {
18
+ if (typeof jest === "undefined") {
19
+ throw new Error(`jest is not available in global scope`);
20
+ }
21
+
22
+ let result = undefined;
23
+ jest.isolateModules(() => {
24
+ result = action();
25
+ });
26
+ // We know that we'll have a result of the appropriate type at this point.
27
+ // We could use a promise to make everything happy, but this doesn't need
28
+ // to be async, so why bother.
29
+ // $FlowIgnore[incompatible-return]
30
+ return result;
31
+ };