-
Notifications
You must be signed in to change notification settings - Fork 2
/
type.ts
165 lines (150 loc) · 4.67 KB
/
type.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
// Copyright 2023-2023 the Nifty li'l' tricks authors. All rights reserved. MIT license.
/**
* A plugin instance.
*/
export interface PluginInstance<Result> {
/**
* The output of the plugin.
*/
output: Result;
/**
* A promise that when resolved, will teardown the plugin.
*/
teardown: () => MaybePromise<void>;
}
/**
* A type indicating that a value is either a value of type `Type` or a promise of a value of type `Type`.
*/
export type MaybePromise<Type> = Type | Promise<Type>;
/**
* A plugin setup function.
*/
export type SetupTestsPluginSetupFn<Config, Result> = (
config: Config,
) => MaybePromise<PluginInstance<Result>>;
/**
* A plugin that can be used to setup tests.
*/
export type Plugin<Config, Result> = {
/**
* Setup the plugin.
*/
setup: SetupTestsPluginSetupFn<Config, Result>;
};
// This is an acceptable use of any because it's only used in the type signature
// deno-lint-ignore no-explicit-any
export type SetupTestsPlugins = Record<string, Plugin<any, any>>;
// Remove when the base config actually contains values
// deno-lint-ignore no-empty-interface
export interface SetupTestsBaseConfig {}
/**
* The configuration for the setup tests.
*/
export type SetupTestsConfig<Plugins extends SetupTestsPlugins> =
& Omit<
{ [Key in keyof Plugins]?: Parameters<Plugins[Key]["setup"]>[0] },
keyof SetupTestsBaseConfig
>
& SetupTestsBaseConfig;
/**
* The result of running setup tests.
*/
export type SetupTestsResult<
Plugins extends SetupTestsPlugins,
Config extends SetupTestsConfig<Plugins>,
> = {
/**
* The outputs of each setup plugin organised by the input keys.
*/
outputs: {
[Key in Extract<keyof Plugins, DefinedKeys<Config>>]: Awaited<
ReturnType<Plugins[Key]["setup"]>
>;
};
/**
* A promise that when resolved, will teardown all the activated plugins.
*/
teardownTests: SetupTestsTeardown;
};
export type SetupTestsPluginTeardown = () => MaybePromise<void>;
export type SetupTestsTeardown = () => Promise<void>;
/**
* The defined keys of the Setup Tests result.
*/
export type DefinedKeys<Type> = {
[Key in keyof Type]-?: undefined extends Type[Key] ? never : Key;
}[keyof Type];
/**
* Set up tests with defined plugins.
*/
export type SetupTestsFactoryPlugins<Plugins extends SetupTestsPlugins> = {
[Key in keyof Plugins]: Plugins[Key];
};
export type SetupTestsFactoryResult<Plugins extends SetupTestsPlugins> = {
/**
* Setup tests by using the loaded plugins.
*
* The loaded plugins available to the setupTests function returned
* from the factory can be configured using config namespaced to the
* name of the plugin. For example, if the plugin is named `helloWorld`,
* then the config for that plugin must be provided under the `helloWorld`
* namespace.
*
* When run, setupTests will return an object with the data returned from
* the plugin invocation. The data will be namespaced to the plugin name.
*
* Only plugins that are configured will be run. If a plugin is not configured,
* then it will not be run. The order of the plugins in the config is defined
* the order in which they defined in the config object. This follows the rules as
* defined [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in#description).
*
* The returned object will also contain a `teardown` function that when
* run, will teardown the plugins in the reverse order that they were
* setup.
*
* ```typescript
* import { setupTestsFactory } from "https://deno.land/x/nifty_lil_tricks_testing/mod.ts";
*
* const helloWorldPlugin = {
* setup: (config: { message: string }) => {
* // Setup plugin according to config
* return {
* output: config,
* teardown: () => {}
* }
* },
* }
*
* export const { setupTests } = setupTestsFactory({
* helloWorld: helloWorldPlugin,
* });
*
* const result = await setupTests({
* helloWorld: {
* message: "Hello World!",
* }
* })
*
* console.log(result.outputs.helloWorld.output); // "Hello World!"
*
* await result.teardownTests();
* ```
*/
setupTests: SetupTestsFn<Plugins>;
};
/**
* Setup Tests.
* @param config The configuration for the tests.
*/
export type SetupTestsFn<Plugins extends SetupTestsPlugins> = <
Config extends SetupTestsConfig<Plugins>,
>(
config: Config,
) => Promise<SetupTestsResult<Plugins, Config>>;
/**
* Marks a type as being one of the following:
* - A value of type `V`
* - A function that returns a value of type `V`
* - A function that returns a promise of a value of type `V`
*/
export type FunctionOrValue<V> = V | (() => Promise<V>) | (() => V);