@plugjs/expect5 0.4.0

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 (90) hide show
  1. package/README.md +7 -0
  2. package/dist/cli.d.mts +2 -0
  3. package/dist/cli.mjs +96 -0
  4. package/dist/cli.mjs.map +6 -0
  5. package/dist/execution/executable.cjs +299 -0
  6. package/dist/execution/executable.cjs.map +6 -0
  7. package/dist/execution/executable.d.ts +87 -0
  8. package/dist/execution/executable.mjs +260 -0
  9. package/dist/execution/executable.mjs.map +6 -0
  10. package/dist/execution/executor.cjs +125 -0
  11. package/dist/execution/executor.cjs.map +6 -0
  12. package/dist/execution/executor.d.ts +35 -0
  13. package/dist/execution/executor.mjs +90 -0
  14. package/dist/execution/executor.mjs.map +6 -0
  15. package/dist/execution/setup.cjs +127 -0
  16. package/dist/execution/setup.cjs.map +6 -0
  17. package/dist/execution/setup.d.ts +31 -0
  18. package/dist/execution/setup.mjs +87 -0
  19. package/dist/execution/setup.mjs.map +6 -0
  20. package/dist/expectation/basic.cjs +216 -0
  21. package/dist/expectation/basic.cjs.map +6 -0
  22. package/dist/expectation/basic.d.ts +47 -0
  23. package/dist/expectation/basic.mjs +177 -0
  24. package/dist/expectation/basic.mjs.map +6 -0
  25. package/dist/expectation/diff.cjs +253 -0
  26. package/dist/expectation/diff.cjs.map +6 -0
  27. package/dist/expectation/diff.d.ts +27 -0
  28. package/dist/expectation/diff.mjs +228 -0
  29. package/dist/expectation/diff.mjs.map +6 -0
  30. package/dist/expectation/expect.cjs +211 -0
  31. package/dist/expectation/expect.cjs.map +6 -0
  32. package/dist/expectation/expect.d.ts +140 -0
  33. package/dist/expectation/expect.mjs +219 -0
  34. package/dist/expectation/expect.mjs.map +6 -0
  35. package/dist/expectation/include.cjs +187 -0
  36. package/dist/expectation/include.cjs.map +6 -0
  37. package/dist/expectation/include.d.ts +10 -0
  38. package/dist/expectation/include.mjs +158 -0
  39. package/dist/expectation/include.mjs.map +6 -0
  40. package/dist/expectation/print.cjs +281 -0
  41. package/dist/expectation/print.cjs.map +6 -0
  42. package/dist/expectation/print.d.ts +4 -0
  43. package/dist/expectation/print.mjs +256 -0
  44. package/dist/expectation/print.mjs.map +6 -0
  45. package/dist/expectation/throwing.cjs +58 -0
  46. package/dist/expectation/throwing.cjs.map +6 -0
  47. package/dist/expectation/throwing.d.ts +8 -0
  48. package/dist/expectation/throwing.mjs +32 -0
  49. package/dist/expectation/throwing.mjs.map +6 -0
  50. package/dist/expectation/types.cjs +212 -0
  51. package/dist/expectation/types.cjs.map +6 -0
  52. package/dist/expectation/types.d.ts +57 -0
  53. package/dist/expectation/types.mjs +178 -0
  54. package/dist/expectation/types.mjs.map +6 -0
  55. package/dist/expectation/void.cjs +111 -0
  56. package/dist/expectation/void.cjs.map +6 -0
  57. package/dist/expectation/void.d.ts +39 -0
  58. package/dist/expectation/void.mjs +77 -0
  59. package/dist/expectation/void.mjs.map +6 -0
  60. package/dist/globals.cjs +2 -0
  61. package/dist/globals.cjs.map +6 -0
  62. package/dist/globals.d.ts +23 -0
  63. package/dist/globals.mjs +1 -0
  64. package/dist/globals.mjs.map +6 -0
  65. package/dist/index.cjs +66 -0
  66. package/dist/index.cjs.map +6 -0
  67. package/dist/index.d.ts +29 -0
  68. package/dist/index.mjs +41 -0
  69. package/dist/index.mjs.map +6 -0
  70. package/dist/test.cjs +229 -0
  71. package/dist/test.cjs.map +6 -0
  72. package/dist/test.d.ts +9 -0
  73. package/dist/test.mjs +194 -0
  74. package/dist/test.mjs.map +6 -0
  75. package/package.json +57 -0
  76. package/src/cli.mts +122 -0
  77. package/src/execution/executable.ts +364 -0
  78. package/src/execution/executor.ts +146 -0
  79. package/src/execution/setup.ts +108 -0
  80. package/src/expectation/basic.ts +209 -0
  81. package/src/expectation/diff.ts +445 -0
  82. package/src/expectation/expect.ts +401 -0
  83. package/src/expectation/include.ts +184 -0
  84. package/src/expectation/print.ts +386 -0
  85. package/src/expectation/throwing.ts +45 -0
  86. package/src/expectation/types.ts +263 -0
  87. package/src/expectation/void.ts +80 -0
  88. package/src/globals.ts +30 -0
  89. package/src/index.ts +54 -0
  90. package/src/test.ts +239 -0
@@ -0,0 +1,364 @@
1
+ import assert from 'node:assert'
2
+ import { AsyncLocalStorage } from 'node:async_hooks'
3
+
4
+ /**
5
+ * A _callable_ (possibly async) function.
6
+ *
7
+ * When the timeout configured is reached, the passed `signal` will be aborted.
8
+ */
9
+ export type Call = (this: undefined, signal: AbortSignal) => void | Promise<void>
10
+
11
+ /** Flag types for an {@link Executable} */
12
+ export type Flag = 'skip' | 'only' | undefined
13
+
14
+ /** An {@link Executor} notifying lifecycle events for {@link Executable}s */
15
+ export interface Executor {
16
+ start(executable: Suite | Spec | Hook): {
17
+ notify(error: Error): void,
18
+ done(skip?: boolean): void,
19
+ }
20
+ }
21
+
22
+ /* ========================================================================== */
23
+
24
+ /** Execute a {@link Call} invoking the {@link Done} */
25
+ function execute(
26
+ call: Call,
27
+ timeout: number,
28
+ notify?: (error: Error) => void,
29
+ ): Promise<Error | undefined> {
30
+ return new Promise<Error | undefined>((resolve) => {
31
+ let resolved = false
32
+
33
+ /* Create the abort controller */
34
+ const abort = new AbortController()
35
+ const handle = setTimeout(() => {
36
+ /* coverage ignore if */
37
+ if (resolved) return
38
+
39
+ const error = new Error(`Timeout of ${timeout} ms reached`)
40
+ resolve(error)
41
+ notify?.(error)
42
+ resolved = true
43
+ }, timeout).unref()
44
+
45
+ /* Use a secondary promise to wrap the (possibly async) call */
46
+ void Promise.resolve().then(async () => {
47
+ try {
48
+ await call.call(undefined, abort.signal)
49
+ resolve(undefined)
50
+ resolved = true
51
+ } catch (cause: any) {
52
+ const error = cause instanceof Error ? cause : new Error(String(cause))
53
+ notify?.(error)
54
+ resolve(error)
55
+ resolved = true
56
+ } finally {
57
+ abort.abort('Spec finished')
58
+ clearTimeout(handle)
59
+ }
60
+ })
61
+ })
62
+ }
63
+
64
+ /* ========================================================================== */
65
+
66
+ /*
67
+ * Suite and skip storages must be unique _per process_. We might get called
68
+ * from two (or three) different versions of this file: the .cjs transpiled one,
69
+ * the .mjs transpiled one (or the .ts dynamically transpiled by ts-loader).
70
+ * In all these cases, we must return the _same_ object, so we store those as
71
+ * a global variables associated with a couple of global symbols
72
+ */
73
+ const suiteKey = Symbol.for('plugjs.expect5.async.suiteStorage')
74
+ const skipKey = Symbol.for('plugjs.expect5.async.skipStorage')
75
+
76
+ function getSuiteStorage(): AsyncLocalStorage<Suite> {
77
+ let storage: AsyncLocalStorage<Suite> = (<any> globalThis)[suiteKey]
78
+ if (! storage) {
79
+ storage = new AsyncLocalStorage<Suite>()
80
+ ;(<any> globalThis)[suiteKey] = storage
81
+ }
82
+ return storage
83
+ }
84
+
85
+ function getSkipStorage(): AsyncLocalStorage<{ skipped: boolean }> {
86
+ let storage: AsyncLocalStorage<{ skipped: boolean }> = (<any> globalThis)[skipKey]
87
+ if (! storage) {
88
+ storage = new AsyncLocalStorage<{ skipped: boolean }>()
89
+ ;(<any> globalThis)[skipKey] = storage
90
+ }
91
+ return storage
92
+ }
93
+
94
+ const suiteStorage = getSuiteStorage()
95
+ const skipStorage = getSkipStorage()
96
+
97
+ export function getCurrentSuite(): Suite {
98
+ const suite = suiteStorage.getStore()
99
+ assert(suite, 'No suite found')
100
+ return suite
101
+ }
102
+
103
+ export function skip(): void {
104
+ const skipState = skipStorage.getStore()
105
+ assert(skipState, 'The "skip" function can only be used in specs or hooks')
106
+ skipState.skipped = true
107
+ }
108
+
109
+ /* ========================================================================== */
110
+
111
+ /** A symbol marking {@link Suite} instances */
112
+ const suiteMarker = Symbol.for('plugjs:expect5:marker:Suite')
113
+
114
+ /** Our {@link Suite} implementation */
115
+ export class Suite {
116
+ private _beforeAll: Hook[] = []
117
+ private _beforeEach: Hook[] = []
118
+ private _afterAll: Hook[] = []
119
+ private _afterEach: Hook[] = []
120
+ private _suites: Suite[] = []
121
+ private _specs: Spec[] = []
122
+ private _children: (Suite | Spec)[] = []
123
+ private _setup: boolean = false
124
+
125
+ constructor(
126
+ public readonly parent: Suite | undefined,
127
+ public readonly name: string,
128
+ public readonly call: Call,
129
+ public readonly timeout: number = 5000,
130
+ public flag: Flag = undefined,
131
+ ) {}
132
+
133
+ static {
134
+ (this.prototype as any)[suiteMarker] = suiteMarker
135
+ }
136
+
137
+ static [Symbol.hasInstance](instance: any): boolean {
138
+ return instance && instance[suiteMarker] === suiteMarker
139
+ }
140
+
141
+ get specs(): number {
142
+ return this._suites.reduce((n, s) => n + s.specs, 0) + this._specs.length
143
+ }
144
+
145
+ /** Add a child {@link Suite} to this */
146
+ addSuite(suite: Suite): void {
147
+ assert.strictEqual(suite.parent, this, 'Suite is not a child of this')
148
+ this._children.push(suite)
149
+ this._suites.push(suite)
150
+ }
151
+
152
+ /** Add a {@link Spec} to this */
153
+ addSpec(spec: Spec): void {
154
+ assert.strictEqual(spec.parent, this, 'Spec is not a child of this')
155
+ this._children.push(spec)
156
+ this._specs.push(spec)
157
+ }
158
+
159
+ /** Add a _before all_ {@link Hook} to this */
160
+ addBeforeAllHook(hook: Hook): void {
161
+ assert.strictEqual(hook.parent, this, 'Hook is not a child of this')
162
+ assert.strictEqual(hook.name, 'beforeAll', `Invalid before all hook name "${hook.name}"`)
163
+ this._beforeAll.push(hook)
164
+ }
165
+
166
+ /** Add a _before each_ {@link Hook} to this */
167
+ addBeforeEachHook(hook: Hook): void {
168
+ assert.strictEqual(hook.parent, this, 'Hook is not a child of this')
169
+ assert.strictEqual(hook.name, 'beforeEach', `Invalid before each hook name "${hook.name}"`)
170
+ this._beforeEach.push(hook)
171
+ }
172
+
173
+ /** Add a _after all_ {@link Hook} to this */
174
+ addAfterAllHook(hook: Hook): void {
175
+ assert.strictEqual(hook.parent, this, 'Hook is not a child of this')
176
+ assert.strictEqual(hook.name, 'afterAll', `Invalid after all hook name "${hook.name}"`)
177
+ this._afterAll.push(hook)
178
+ }
179
+
180
+ /** Add a _after each_ {@link Hook} to this */
181
+ addAfterEachHook(hook: Hook): void {
182
+ assert.strictEqual(hook.parent, this, 'Hook is not a child of this')
183
+ assert.strictEqual(hook.name, 'afterEach', `Invalid after each hook name "${hook.name}"`)
184
+ this._afterEach.push(hook)
185
+ }
186
+
187
+ /**
188
+ * Setup this {@link Suite} invoking its main function, then initializing all
189
+ * children {@link Suite Suites}, and finally normalizing execution flags.
190
+ */
191
+ async setup(): Promise<void> {
192
+ /* If this suite was already setup, this becomes a no-op */
193
+ if (this._setup) return
194
+
195
+ /* Run the setup call */
196
+ this._setup = true
197
+ await suiteStorage.run(this, async () => {
198
+ const error = await execute(this.call, this.timeout)
199
+ if (error) throw error
200
+ })
201
+
202
+ /* Copy before and after hooks from parent */
203
+ if (this.parent) {
204
+ this._beforeEach.unshift(...this.parent._beforeEach.map((h) => h.clone(this)))
205
+ this._afterEach.push(...this.parent._afterEach.map((h) => h.clone(this)))
206
+ }
207
+
208
+ /* Setup all sub-suites of this instance */
209
+ for (const suite of this._suites) {
210
+ await suite.setup()
211
+ }
212
+
213
+ /* Setup all before/after hooks in the spec */
214
+ for (const spec of this._specs) {
215
+ spec.before.push(...this._beforeEach.map((h) => h.clone(spec)))
216
+ spec.after.push(...this._afterEach.map((h) => h.clone(spec)))
217
+ }
218
+
219
+ /* If _any_ of this suite's children is marked as "only", then all children
220
+ * not marked as such will be skipped, and this suite will also be marked
221
+ * as "only" (to inform parent suites) */
222
+ const only = this._children.reduce((o, c) => o || (c.flag === 'only'), false)
223
+ if (only) {
224
+ this._children.forEach((c) => (c.flag !== 'only') && (c.flag = 'skip'))
225
+ this.flag = 'only'
226
+ }
227
+
228
+ /* If _this_ suite is marked as only, any child not marked with "skip" will
229
+ * be marked as "only" and included in the execution */
230
+ if (this.flag === 'only') {
231
+ this._children.forEach((c) => (c.flag !== 'skip') && (c.flag = 'only'))
232
+ }
233
+
234
+ /* If all children are skipped, then this instance is skipped, too */
235
+ for (const child of this._children) {
236
+ if (child.flag !== 'skip') return
237
+ }
238
+ this.flag = 'skip'
239
+ }
240
+
241
+ /**
242
+ * Execute this suite, executing all {@link Hook hooks} and children
243
+ * {@link Spec specs} and {@link Suite suites}
244
+ */
245
+ async execute(executor: Executor, skip: boolean = false): Promise<Error | void> {
246
+ const { done } = executor.start(this)
247
+
248
+ /* Potentially skip this (and all children) */
249
+ if (skip || (this.flag === 'skip')) {
250
+ for (const child of this._children) await child.execute(executor, true)
251
+ return done()
252
+ }
253
+
254
+ /* Execute all our _before all_ hooks */
255
+ for (const hook of this._beforeAll) {
256
+ const failed = await hook.execute(executor)
257
+ /* Skip this (and all children on) _before all_ failure */
258
+ if (failed) {
259
+ for (const child of this._children) await child.execute(executor, true)
260
+ return done()
261
+ }
262
+ }
263
+
264
+ /* Execute all our children (specs or suites) */
265
+ for (const child of this._children) await child.execute(executor)
266
+
267
+ /* Execute all our _after all_ hooks (regardless of failures) */
268
+ for (const hook of this._afterAll) await hook.execute(executor)
269
+
270
+ /* Done */
271
+ done()
272
+ }
273
+ }
274
+
275
+ /* ========================================================================== */
276
+
277
+ /** A symbol marking {@link Spec} instances */
278
+ const specMarker = Symbol.for('plugjs:expect5:marker:Spec')
279
+
280
+ /** Our {@link Spec} implementation */
281
+ export class Spec {
282
+ public before: Hook[] = []
283
+ public after: Hook[] = []
284
+
285
+ constructor(
286
+ public readonly parent: Suite,
287
+ public readonly name: string,
288
+ public readonly call: Call,
289
+ public readonly timeout: number = 5000,
290
+ public flag: Flag = undefined,
291
+ ) {}
292
+
293
+ static {
294
+ (this.prototype as any)[specMarker] = specMarker
295
+ }
296
+
297
+ static [Symbol.hasInstance](instance: any): boolean {
298
+ return instance && instance[specMarker] === specMarker
299
+ }
300
+
301
+ /** Execute this spec */
302
+ async execute(executor: Executor, skip: boolean = false): Promise<void> {
303
+ const { done, notify } = executor.start(this)
304
+
305
+ /* Potentially skip this */
306
+ if (skip || (this.flag == 'skip')) return done(true)
307
+
308
+ /* Execute all our _before each_ hooks */
309
+ for (const hook of this.before) {
310
+ const failed = await hook.execute(executor)
311
+ if (failed) return done(true)
312
+ }
313
+
314
+ /* Execute our spec */
315
+ const skipState = { skipped: false }
316
+ await skipStorage.run(skipState, () => execute(this.call, this.timeout, notify))
317
+
318
+ /* Execute all our _after all_ hooks (regardless of failures) */
319
+ for (const hook of this.after) await hook.execute(executor)
320
+
321
+ /* Done! */
322
+ return done(skipState.skipped)
323
+ }
324
+ }
325
+
326
+ /* ========================================================================== */
327
+
328
+ /** A symbol marking {@link Hook} instances */
329
+ const hookMarker = Symbol.for('plugjs:expect5:marker:Hook')
330
+
331
+ /** Our {@link Hook} implementation */
332
+ export class Hook {
333
+ constructor(
334
+ public readonly parent: Suite | Spec,
335
+ public readonly name: 'beforeAll' | 'afterAll' | 'beforeEach' | 'afterEach',
336
+ public readonly call: Call,
337
+ public readonly timeout: number = 5000,
338
+ public readonly flag: Exclude<Flag, 'only'> = undefined,
339
+ ) {}
340
+
341
+ static {
342
+ (this.prototype as any)[hookMarker] = hookMarker
343
+ }
344
+
345
+ static [Symbol.hasInstance](instance: any): boolean {
346
+ return instance && instance[hookMarker] === hookMarker
347
+ }
348
+
349
+ /** Execute this hook */
350
+ async execute(executor: Executor): Promise<boolean> {
351
+ if (this.flag === 'skip') return false
352
+ const { done, notify } = executor.start(this)
353
+
354
+ const skipState = { skipped: false }
355
+ const error = await skipStorage.run(skipState, () => execute(this.call, this.timeout, notify))
356
+ done(skipState.skipped)
357
+ return !! error
358
+ }
359
+
360
+ /** Clone this associating it with a new {@link Suite} or {@link Spec} */
361
+ clone(parent: Suite | Spec): Hook {
362
+ return new Hook(parent, this.name, this.call, this.timeout, this.flag)
363
+ }
364
+ }
@@ -0,0 +1,146 @@
1
+ import assert from 'node:assert'
2
+ import { EventEmitter } from 'node:events'
3
+
4
+ import { Spec, Suite, Hook } from './executable'
5
+
6
+ import type { Executor } from './executable'
7
+
8
+ /* ========================================================================== */
9
+
10
+ export interface ExecutionFailure {
11
+ number: number,
12
+ error: Error,
13
+ source: Suite | Spec | Hook,
14
+ type: 'suite' | 'spec' | 'hook',
15
+ }
16
+
17
+ export interface ExecutionEvents {
18
+ 'suite:start': (suite: Suite) => void
19
+ 'suite:done': (suite: Suite, time: number) => void
20
+
21
+ 'spec:start': (spec: Spec) => void
22
+ 'spec:error': (spec: Spec, failure: ExecutionFailure) => void
23
+ 'spec:skip': (spec: Spec, time: number) => void
24
+ 'spec:pass': (spec: Spec, time: number) => void
25
+ 'spec:fail': (spec: Spec, time: number, failure: ExecutionFailure) => void
26
+
27
+ 'hook:start': (hook: Hook) => void
28
+ 'hook:error': (hook: Hook, failure: ExecutionFailure) => void
29
+ 'hook:skip': (hook: Hook, time: number) => void
30
+ 'hook:pass': (hook: Hook, time: number) => void
31
+ 'hook:fail': (hook: Hook, time: number, failure: ExecutionFailure) => void
32
+ }
33
+
34
+ export interface Execution {
35
+ on<E extends keyof ExecutionEvents>(event: E, listener: ExecutionEvents[E]): this
36
+ off<E extends keyof ExecutionEvents>(event: E, listener: ExecutionEvents[E]): this
37
+ once<E extends keyof ExecutionEvents>(event: E, listener: ExecutionEvents[E]): this
38
+ result: Promise<ExecutionResult>
39
+ }
40
+
41
+ export interface ExecutionResult {
42
+ passed: number
43
+ failed: number
44
+ skipped: number
45
+ time: number
46
+ failures: ExecutionFailure[],
47
+ }
48
+
49
+ /* ========================================================================== */
50
+
51
+ export function runSuite(suite: Suite): Execution {
52
+ const _emitter = new EventEmitter()
53
+
54
+ let resolve: (value: ExecutionResult | PromiseLike<ExecutionResult>) => void
55
+ let reject: (reason?: any) => void
56
+ const promise = new Promise<ExecutionResult>((_resolve, _reject) => {
57
+ resolve = _resolve
58
+ reject = _reject
59
+ })
60
+
61
+ const result: ExecutionResult = {
62
+ time: 0,
63
+ passed: 0,
64
+ failed: 0,
65
+ skipped: 0,
66
+ failures: [],
67
+ }
68
+
69
+ const execution: Execution = {
70
+ result: promise,
71
+ on: (event: string, listener: (...args: any[]) => void): Execution => {
72
+ _emitter.on(event, listener)
73
+ return execution
74
+ },
75
+ off: (event: string, listener: (...args: any[]) => void): Execution => {
76
+ _emitter.off(event, listener)
77
+ return execution
78
+ },
79
+ once: (event: string, listener: (...args: any[]) => void): Execution => {
80
+ _emitter.once(event, listener)
81
+ return execution
82
+ },
83
+ }
84
+
85
+ const start = (executable: Suite | Spec | Hook): ReturnType<Executor['start']> => {
86
+ const type: 'suite' | 'spec' | 'hook' =
87
+ executable instanceof Suite ? 'suite' :
88
+ executable instanceof Spec ? 'spec' :
89
+ executable instanceof Hook ? 'hook' :
90
+ /* coverage ignore next */
91
+ assert.fail(`Unable to start ${Object.getPrototypeOf(executable)?.constructor?.name}`)
92
+
93
+ const now = Date.now()
94
+ _emitter.emit(`${type}:start`, executable)
95
+
96
+ let done = false
97
+ let failure: ExecutionFailure | undefined
98
+
99
+ return {
100
+ done(skipped: boolean = false): void {
101
+ const time = Date.now() - now
102
+ done = true
103
+
104
+ if (type === 'suite') {
105
+ _emitter.emit(`${type}:done`, executable, time)
106
+ return
107
+ }
108
+
109
+ if (failure) {
110
+ _emitter.emit(`${type}:fail`, executable, time, failure)
111
+ if (type === 'spec') result.failed ++
112
+ } else if (skipped) {
113
+ _emitter.emit(`${type}:skip`, executable, time)
114
+ if (type === 'spec') result.skipped ++
115
+ } else {
116
+ _emitter.emit(`${type}:pass`, executable, time)
117
+ if (type === 'spec') result.passed ++
118
+ }
119
+ },
120
+ notify(error: Error): void {
121
+ const number = result.failures.length + 1
122
+ const fail = { error, number, source: executable, type }
123
+ result.failures.push(fail)
124
+
125
+ // notify error after done, or include in failure?
126
+ if (failure || done) {
127
+ _emitter.emit(`${type}:error`, executable, fail)
128
+ } else {
129
+ failure = fail
130
+ }
131
+ },
132
+ }
133
+ }
134
+
135
+ setImmediate(() => Promise.resolve().then(async () => {
136
+ const now = Date.now()
137
+ await suite.setup()
138
+ await suite.execute({ start })
139
+
140
+ result.time = Date.now() - now
141
+
142
+ resolve(result)
143
+ }).catch((error) => reject(error)))
144
+
145
+ return execution
146
+ }
@@ -0,0 +1,108 @@
1
+ import { getCurrentSuite, Suite, Spec, Hook } from './executable'
2
+
3
+ import type { Call } from './executable'
4
+
5
+ export type SuiteSetup = (name: string, call: Call, timeout?: number) => void
6
+ export type SuiteFunction = SuiteSetup & {
7
+ only: SuiteSetup,
8
+ skip: SuiteSetup,
9
+ }
10
+
11
+ export const describe: SuiteFunction = (name: string, call: Call, timeout?: number): void => {
12
+ const parent = getCurrentSuite()
13
+ parent.addSuite(new Suite(parent, name, call, timeout))
14
+ }
15
+
16
+ export const fdescribe: SuiteSetup = (name: string, call: Call, timeout?: number): void => {
17
+ const parent = getCurrentSuite()
18
+ parent.addSuite(new Suite(parent, name, call, timeout, 'only'))
19
+ }
20
+
21
+ export const xdescribe: SuiteSetup = (name: string, call: Call, timeout?: number): void => {
22
+ const parent = getCurrentSuite()
23
+ parent.addSuite(new Suite(parent, name, call, timeout, 'skip'))
24
+ }
25
+
26
+ describe.skip = xdescribe
27
+ describe.only = fdescribe
28
+
29
+ /* ========================================================================== */
30
+
31
+ export type SpecSetup = (name: string, call: Call, timeout?: number) => void
32
+ export type SpecFunction = SpecSetup & {
33
+ only: SpecSetup,
34
+ skip: SpecSetup,
35
+ }
36
+
37
+ export const it: SpecFunction = (name: string, call: Call, timeout?: number): void => {
38
+ const parent = getCurrentSuite()
39
+ parent.addSpec(new Spec(parent, name, call, timeout))
40
+ }
41
+
42
+ export const fit: SpecSetup = (name: string, call: Call, timeout?: number): void => {
43
+ const parent = getCurrentSuite()
44
+ parent.addSpec(new Spec(parent, name, call, timeout, 'only'))
45
+ }
46
+
47
+ export const xit: SpecSetup = (name: string, call: Call, timeout?: number): void => {
48
+ const parent = getCurrentSuite()
49
+ parent.addSpec(new Spec(parent, name, call, timeout, 'skip'))
50
+ }
51
+
52
+ it.skip = xit
53
+ it.only = fit
54
+
55
+ /* ========================================================================== */
56
+
57
+ export type HookSetup = (call: Call, timeout?: number) => void
58
+ export type HookFunction = HookSetup & {
59
+ skip: HookSetup,
60
+ }
61
+
62
+ export const beforeAll: HookFunction = (call: Call, timeout?: number): void => {
63
+ const parent = getCurrentSuite()
64
+ parent.addBeforeAllHook(new Hook(parent, 'beforeAll', call, timeout))
65
+ }
66
+
67
+ export const xbeforeAll: HookSetup = (call: Call, timeout?: number): void => {
68
+ const parent = getCurrentSuite()
69
+ parent.addBeforeAllHook(new Hook(parent, 'beforeAll', call, timeout, 'skip'))
70
+ }
71
+
72
+ export const beforeEach: HookFunction = (call: Call, timeout?: number): void => {
73
+ const parent = getCurrentSuite()
74
+ parent.addBeforeEachHook(new Hook(parent, 'beforeEach', call, timeout))
75
+ }
76
+
77
+ export const xbeforeEach: HookSetup = (call: Call, timeout?: number): void => {
78
+ const parent = getCurrentSuite()
79
+ parent.addBeforeEachHook(new Hook(parent, 'beforeEach', call, timeout, 'skip'))
80
+ }
81
+
82
+ export const afterAll: HookFunction = (call: Call, timeout?: number): void => {
83
+ const parent = getCurrentSuite()
84
+ parent.addAfterAllHook(new Hook(parent, 'afterAll', call, timeout))
85
+ }
86
+
87
+ export const xafterAll: HookSetup = (call: Call, timeout?: number): void => {
88
+ const parent = getCurrentSuite()
89
+ parent.addAfterAllHook(new Hook(parent, 'afterAll', call, timeout, 'skip'))
90
+ }
91
+
92
+ export const afterEach: HookFunction = (call: Call, timeout?: number): void => {
93
+ const parent = getCurrentSuite()
94
+ parent.addAfterEachHook(new Hook(parent, 'afterEach', call, timeout))
95
+ }
96
+
97
+ export const xafterEach: HookSetup = (call: Call, timeout?: number): void => {
98
+ const parent = getCurrentSuite()
99
+ parent.addAfterEachHook(new Hook(parent, 'afterEach', call, timeout, 'skip'))
100
+ }
101
+
102
+ beforeAll.skip = xbeforeAll
103
+ beforeEach.skip = xbeforeEach
104
+ afterAll.skip = xafterAll
105
+ afterEach.skip = xafterEach
106
+
107
+ export const before: HookSetup = beforeAll
108
+ export const after: HookSetup = afterAll