/
main.ts
158 lines (140 loc) · 4.21 KB
/
main.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
/*
* @japa/runner
*
* (c) Japa
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import {
Emitter,
Refiner,
Test as BaseTest,
Suite as BaseSuite,
Group as BaseGroup,
Runner as BaseRunner,
TestContext as BaseTestContext,
} from '@japa/core'
import { inspect } from 'node:util'
import { AssertionError } from 'node:assert'
import { BaseReporter } from './reporters/base.js'
import type { DataSetNode, TestHooksCleanupHandler } from './types.js'
declare module '@japa/core' {
interface Test<Context extends Record<any, any>, TestData extends DataSetNode = undefined> {
throws(message: string | RegExp, errorConstructor?: any): this
}
interface TestContext {
cleanup: (cleanupCallback: TestHooksCleanupHandler<TestContext>) => void
}
}
export { Emitter, Refiner, BaseReporter }
/**
* Test context carries context data for a given test.
*/
export class TestContext extends BaseTestContext {
/**
* Register a cleanup function that runs after the test finishes
* successfully or with an error.
*/
declare cleanup: (cleanupCallback: TestHooksCleanupHandler<TestContext>) => void
constructor(public test: Test) {
super()
this.cleanup = (cleanupCallback: TestHooksCleanupHandler<TestContext>) => {
test.cleanup(cleanupCallback)
}
}
}
/**
* Test class represents an individual test and exposes API to tweak
* its runtime behavior.
*/
export class Test<TestData extends DataSetNode = undefined> extends BaseTest<
TestContext,
TestData
> {
/**
* @inheritdoc
*/
static executedCallbacks = []
/**
* @inheritdoc
*/
static executingCallbacks = []
/**
* Assert the test callback throws an exception when a certain
* error message and optionally is an instance of a given
* Error class.
*/
throws(message: string | RegExp, errorConstructor?: any) {
const errorInPoint = new AssertionError({})
const existingExecutor = this.options.executor
if (!existingExecutor) {
throw new Error('Cannot use "test.throws" method without a test callback')
}
/**
* Overwriting existing callback
*/
this.options.executor = async (...args: [any, any, any]) => {
let raisedException: any
try {
await existingExecutor(...args)
} catch (error) {
raisedException = error
}
/**
* Notify no exception has been raised
*/
if (!raisedException) {
errorInPoint.message = 'Expected test to throw an exception'
throw errorInPoint
}
/**
* Constructor mis-match
*/
if (errorConstructor && !(raisedException instanceof errorConstructor)) {
errorInPoint.message = `Expected test to throw "${inspect(errorConstructor)}"`
throw errorInPoint
}
/**
* Error does not have a message property
*/
const exceptionMessage: unknown = raisedException.message
if (!exceptionMessage || typeof exceptionMessage !== 'string') {
errorInPoint.message = 'Expected test to throw an exception with message property'
throw errorInPoint
}
/**
* Message does not match
*/
if (typeof message === 'string') {
if (exceptionMessage !== message) {
errorInPoint.message = `Expected test to throw "${message}". Instead received "${raisedException.message}"`
errorInPoint.actual = raisedException.message
errorInPoint.expected = message
throw errorInPoint
}
return
}
if (!message.test(exceptionMessage)) {
errorInPoint.message = `Expected test error to match "${message}" regular expression`
throw errorInPoint
}
}
return this
}
}
/**
* TestGroup is used to bulk configure a collection of tests and
* define lifecycle hooks for them
*/
export class Group extends BaseGroup<TestContext> {}
/**
* A suite is a collection of tests created around a given
* testing type. For example: A suite for unit tests, a
* suite for functional tests and so on.
*/
export class Suite extends BaseSuite<TestContext> {}
/**
* Runner class is used to execute the tests
*/
export class Runner extends BaseRunner<TestContext> {}