7
7
import avaTest , {
8
8
ExecutionContext ,
9
9
Implementation ,
10
- OneOrMoreMacros ,
10
+ ImplementationFn ,
11
+ Macro ,
12
+ MacroDeclarationOptions ,
13
+ MacroFn ,
14
+ TestFn ,
11
15
} from 'ava' ;
12
16
import * as assert from 'assert' ;
13
17
import throat from 'throat' ;
@@ -58,59 +62,72 @@ export const test = createTestInterface({
58
62
// In case someone wants to `const test = context()`
59
63
export const context = test . context ;
60
64
65
+ export type SimpleTitleFn = ( providedTitle : string | undefined ) => string ;
66
+ export type SimpleImplementationFn < Context = unknown > = (
67
+ t : ExecutionContext < Context >
68
+ ) => PromiseLike < void > ;
69
+ export type SimpleContextFn < Context , T > = (
70
+ t : ExecutionContext < Context >
71
+ ) => Promise < T > ;
72
+
61
73
export interface TestInterface <
62
74
Context
63
75
> /*extends Omit<AvaTestInterface<Context>, 'before' | 'beforeEach' | 'after' | 'afterEach' | 'failing' | 'serial'>*/ {
64
76
//#region copy-pasted from ava's .d.ts
65
77
/** Declare a concurrent test. */
66
- ( title : string , implementation : Implementation < Context > ) : void ;
78
+ ( title : string , implementation : Implementation < unknown [ ] , Context > ) : void ;
67
79
/** Declare a concurrent test that uses one or more macros. Additional arguments are passed to the macro. */
68
80
< T extends any [ ] > (
69
81
title : string ,
70
- macros : OneOrMoreMacros < T , Context > ,
82
+ implementation : Implementation < T , Context > ,
71
83
...rest : T
72
84
) : void ;
73
85
/** Declare a concurrent test that uses one or more macros. The macro is responsible for generating a unique test title. */
74
- < T extends any [ ] > ( macros : OneOrMoreMacros < T , Context > , ...rest : T ) : void ;
86
+ < T extends any [ ] > ( macro : Implementation < T , Context > , ...rest : T ) : void ;
75
87
//#endregion
76
88
77
- serial ( title : string , implementation : Implementation < Context > ) : void ;
89
+ serial (
90
+ title : string ,
91
+ implementation : Implementation < unknown [ ] , Context >
92
+ ) : void ;
78
93
/** Declare a concurrent test that uses one or more macros. Additional arguments are passed to the macro. */
79
94
serial < T extends any [ ] > (
80
95
title : string ,
81
- macros : OneOrMoreMacros < T , Context > ,
96
+ implementation : Implementation < T , Context > ,
82
97
...rest : T
83
98
) : void ;
84
99
/** Declare a concurrent test that uses one or more macros. The macro is responsible for generating a unique test title. */
85
100
serial < T extends any [ ] > (
86
- macros : OneOrMoreMacros < T , Context > ,
101
+ implementation : Implementation < T , Context > ,
87
102
...rest : T
88
103
) : void ;
89
- skip ( title : string , implementation : Implementation < Context > ) : void ;
104
+ skip ( title : string , implementation : Implementation < unknown [ ] , Context > ) : void ;
90
105
/** Declare a concurrent test that uses one or more macros. Additional arguments are passed to the macro. */
91
106
skip < T extends any [ ] > (
92
107
title : string ,
93
- macros : OneOrMoreMacros < T , Context > ,
108
+ implementation : Implementation < T , Context > ,
94
109
...rest : T
95
110
) : void ;
96
111
/** Declare a concurrent test that uses one or more macros. The macro is responsible for generating a unique test title. */
97
- skip < T extends any [ ] > ( macros : OneOrMoreMacros < T , Context > , ...rest : T ) : void ;
112
+ skip < T extends any [ ] > (
113
+ implementation : Implementation < T , Context > ,
114
+ ...rest : T
115
+ ) : void ;
98
116
99
117
macro < Args extends any [ ] , Ctx = Context > (
100
118
cb : (
101
119
...args : Args
102
120
) =>
103
- | [
104
- ( ( title : string | undefined ) => string | undefined ) | string ,
105
- ( t : ExecutionContext < Ctx > ) => Promise < void >
106
- ]
107
- | ( ( t : ExecutionContext < Ctx > ) => Promise < void > )
108
- ) : AvaMacro < Args , Ctx > ;
121
+ | [ SimpleTitleFn | string , SimpleImplementationFn < Ctx > ]
122
+ | SimpleImplementationFn < Ctx >
123
+ ) : Macro < Args , Ctx > ;
124
+
125
+ avaMacro : MacroFn < Context > ;
109
126
110
- beforeAll ( cb : ( t : ExecutionContext < Context > ) => Promise < void > ) : void ;
111
- beforeEach ( cb : ( t : ExecutionContext < Context > ) => Promise < void > ) : void ;
127
+ beforeAll ( cb : SimpleImplementationFn < Context > ) : void ;
128
+ beforeEach ( cb : SimpleImplementationFn < Context > ) : void ;
112
129
context < T extends object | void > (
113
- cb : ( t : ExecutionContext < Context > ) => Promise < T >
130
+ cb : SimpleContextFn < Context , T >
114
131
) : TestInterface < Context & T > ;
115
132
suite ( title : string , cb : ( test : TestInterface < Context > ) => void ) : void ;
116
133
@@ -125,10 +142,6 @@ export interface TestInterface<
125
142
126
143
// TODO add teardownEach
127
144
}
128
- export interface AvaMacro < Args extends any [ ] = any [ ] , Ctx = unknown > {
129
- ( test : ExecutionContext < Ctx > , ...args : Args ) : Promise < void > ;
130
- title ?( givenTitle : string | undefined , ...args : Args ) : string ;
131
- }
132
145
133
146
function createTestInterface < Context > ( opts : {
134
147
titlePrefix : string | undefined ;
@@ -145,13 +158,11 @@ function createTestInterface<Context>(opts: {
145
158
let suiteOrTestDeclared = false ;
146
159
function computeTitle < Args extends any [ ] > (
147
160
title : string | undefined ,
148
- macros ?: AvaMacro < Args , any > [ ] ,
161
+ impl ?: Implementation < Args , any > ,
149
162
...args : Args
150
163
) {
151
- for ( const macro of macros ?? [ ] ) {
152
- if ( macro . title ) {
153
- title = macro . title ( title , ...args ) ;
154
- }
164
+ if ( isMacroWithTitle ( impl ) ) {
165
+ title = impl . title ! ( title , ...args ) ;
155
166
}
156
167
assert ( title ) ;
157
168
// return `${ titlePrefix }${ separator }${ title }`;
@@ -165,13 +176,8 @@ function createTestInterface<Context>(opts: {
165
176
function parseArgs ( args : any [ ] ) {
166
177
const title =
167
178
typeof args [ 0 ] === 'string' ? ( args . shift ( ) as string ) : undefined ;
168
- const macros =
169
- typeof args [ 0 ] === 'function'
170
- ? [ args . shift ( ) as AvaMacro ]
171
- : Array . isArray ( args [ 0 ] )
172
- ? ( args . shift ( ) as AvaMacro [ ] )
173
- : [ ] ;
174
- return { title, macros, args } ;
179
+ const impl = args . shift ( ) as Implementation < any [ ] , Context > ;
180
+ return { title, impl, args } ;
175
181
}
176
182
function assertOrderingForDeclaringTest ( ) {
177
183
suiteOrTestDeclared = true ;
@@ -196,29 +202,32 @@ function createTestInterface<Context>(opts: {
196
202
*/
197
203
function declareTest (
198
204
title : string | undefined ,
199
- macros : AvaMacro < any [ ] , Context > [ ] ,
205
+ impl : Implementation < any [ ] , Context > ,
200
206
avaDeclareFunction : Function & { skip : Function } ,
201
207
args : any [ ] ,
202
208
skip = false
203
209
) {
204
- const wrappedMacros = macros . map ( ( macro ) => {
205
- return async function ( t : ExecutionContext < Context > , ...args : any [ ] ) {
206
- return concurrencyLimiter (
207
- errorPostprocessor ( async ( ) => {
208
- let i = 0 ;
209
- for ( const func of beforeEachFunctions ) {
210
- await func ( t ) ;
211
- i ++ ;
212
- }
213
- return macro ( t , ...args ) ;
214
- } )
215
- ) ;
216
- } ;
217
- } ) ;
218
- const computedTitle = computeTitle ( title , macros , ...args ) ;
210
+ const wrapped = async function (
211
+ t : ExecutionContext < Context > ,
212
+ ...args : any [ ]
213
+ ) {
214
+ return concurrencyLimiter (
215
+ errorPostprocessor ( async ( ) => {
216
+ let i = 0 ;
217
+ for ( const func of beforeEachFunctions ) {
218
+ await func ( t ) ;
219
+ i ++ ;
220
+ }
221
+ return isMacro ( impl )
222
+ ? impl . exec ( t , ...args )
223
+ : ( impl as ImplementationFn < any [ ] , Context > ) ( t , ...args ) ;
224
+ } )
225
+ ) ;
226
+ } ;
227
+ const computedTitle = computeTitle ( title , impl , ...args ) ;
219
228
( automaticallySkip || skip ? avaDeclareFunction . skip : avaDeclareFunction ) (
220
229
computedTitle ,
221
- wrappedMacros ,
230
+ wrapped ,
222
231
...args
223
232
) ;
224
233
}
@@ -229,23 +238,23 @@ function createTestInterface<Context>(opts: {
229
238
// start till it finishes.
230
239
// HOWEVER if it returns a single shared state, can tests concurrently use this shared state?
231
240
// if(!automaticallyDoSerial && mustDoSerial) throw new Error('Cannot declare non-serial tests because you have declared a beforeAll() hook for this test suite.');
232
- const { args, macros , title } = parseArgs ( inputArgs ) ;
241
+ const { args, impl , title } = parseArgs ( inputArgs ) ;
233
242
return declareTest (
234
243
title ,
235
- macros ,
244
+ impl ,
236
245
automaticallyDoSerial ? avaTest . serial : avaTest ,
237
246
args
238
247
) ;
239
248
}
240
249
test . serial = function ( ...inputArgs : any [ ] ) {
241
250
assertOrderingForDeclaringTest ( ) ;
242
- const { args, macros , title } = parseArgs ( inputArgs ) ;
243
- return declareTest ( title , macros , avaTest . serial , args ) ;
251
+ const { args, impl , title } = parseArgs ( inputArgs ) ;
252
+ return declareTest ( title , impl , avaTest . serial , args ) ;
244
253
} ;
245
254
test . skip = function ( ...inputArgs : any [ ] ) {
246
255
assertOrderingForDeclaringTest ( ) ;
247
- const { args, macros , title } = parseArgs ( inputArgs ) ;
248
- return declareTest ( title , macros , avaTest , args , true ) ;
256
+ const { args, impl , title } = parseArgs ( inputArgs ) ;
257
+ return declareTest ( title , impl , avaTest , args , true ) ;
249
258
} ;
250
259
test . beforeEach = function (
251
260
cb : ( test : ExecutionContext < Context > ) => Promise < void >
@@ -274,27 +283,29 @@ function createTestInterface<Context>(opts: {
274
283
cb : (
275
284
...args : Args
276
285
) =>
277
- | [
278
- ( ( title : string | undefined ) => string | undefined ) | string ,
279
- ( t : ExecutionContext < Context > ) => Promise < void >
280
- ]
281
- | ( ( t : ExecutionContext < Context > ) => Promise < void > )
286
+ | [ SimpleTitleFn | string , SimpleImplementationFn < Context > ]
287
+ | SimpleImplementationFn < Context >
282
288
) {
283
- function macro ( testInterface : ExecutionContext < Context > , ...args : Args ) {
284
- const ret = cb ( ...args ) ;
285
- const macroFunction = Array . isArray ( ret ) ? ret [ 1 ] : ret ;
286
- return macroFunction ( testInterface ) ;
287
- }
288
- macro . title = function ( givenTitle : string | undefined , ...args : Args ) {
289
+ function title ( givenTitle : string | undefined , ...args : Args ) {
289
290
const ret = cb ( ...args ) ;
290
291
return Array . isArray ( ret )
291
292
? typeof ret [ 0 ] === 'string'
292
293
? ret [ 0 ]
293
294
: ret [ 0 ] ( givenTitle )
294
- : givenTitle ;
295
+ : givenTitle ?? 'UNKNOWN' ;
296
+ }
297
+ function exec ( testInterface : ExecutionContext < Context > , ...args : Args ) {
298
+ const ret = cb ( ...args ) ;
299
+ const impl = Array . isArray ( ret ) ? ret [ 1 ] : ret ;
300
+ return impl ( testInterface ) ;
301
+ }
302
+ const declaration : MacroDeclarationOptions < Args , Context > = {
303
+ title,
304
+ exec,
295
305
} ;
296
- return macro ;
306
+ return ( avaTest as TestFn < Context > ) . macro < Args > ( declaration ) ;
297
307
} ;
308
+ test . avaMacro = ( avaTest as TestFn < Context > ) . macro ;
298
309
test . suite = function (
299
310
title : string ,
300
311
cb : ( test : TestInterface < Context > ) => void
@@ -322,3 +333,14 @@ function createTestInterface<Context>(opts: {
322
333
} ;
323
334
return test as any ;
324
335
}
336
+
337
+ function isMacro (
338
+ implementation ?: Implementation < any , any >
339
+ ) : implementation is Macro < any > {
340
+ return implementation != null && typeof implementation !== 'function' ;
341
+ }
342
+ function isMacroWithTitle (
343
+ implementation ?: Implementation < any , any >
344
+ ) : implementation is Macro < any > {
345
+ return ! ! ( implementation && ( implementation as Macro < [ ] > ) ?. title ) ;
346
+ }
0 commit comments