Skip to content

Commit b7a5f68

Browse files
committed
feat: support async output, after, before, errors hooks
1 parent 4b2367e commit b7a5f68

File tree

2 files changed

+37
-23
lines changed

2 files changed

+37
-23
lines changed

src/rule-tester.ts

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* eslint-disable no-useless-call */
12
import type {
23
InvalidTestCase,
34
RuleTester,
@@ -47,7 +48,7 @@ export function createRuleTester(options: RuleTesterInitOptions): RuleTester {
4748
...options.defaultFilenames,
4849
}
4950

50-
function each(c: TestCase) {
51+
async function each(c: TestCase) {
5152
const testcase = normalizeTestCase(c, languageOptions, defaultFilenames)
5253

5354
const {
@@ -111,6 +112,8 @@ export function createRuleTester(options: RuleTesterInitOptions): RuleTester {
111112
}
112113
}
113114

115+
await testcase.before?.call(testcase, configs)
116+
114117
const messages = linter.verify(testcase.code!, configs, testcase.filename)
115118
// Rewrite ruleId to remove the plugin prefix
116119
messages.forEach((message) => {
@@ -121,7 +124,7 @@ export function createRuleTester(options: RuleTesterInitOptions): RuleTester {
121124
// Verify errors
122125
if (testcase.errors) {
123126
if (typeof testcase.errors === 'function') {
124-
testcase.errors(messages)
127+
await testcase.errors(messages)
125128
}
126129
else if (typeof testcase.errors === 'number') {
127130
expect.soft(messages.length, 'number of error messages').toBe(testcase.errors)
@@ -170,7 +173,7 @@ export function createRuleTester(options: RuleTesterInitOptions): RuleTester {
170173
if (testcase.output === null) // null means the output should be the same as the input
171174
expect(result.output, 'output').toBe(testcase.code)
172175
else if (typeof testcase.output === 'function') // custom assertion
173-
testcase.output(result.output!, testcase.code)
176+
await testcase.output(result.output!, testcase.code)
174177

175178
else
176179
expect(result.output, 'output').toBe(testcase.output)
@@ -188,25 +191,29 @@ export function createRuleTester(options: RuleTesterInitOptions): RuleTester {
188191
expect.soft(messages, 'no errors after fix').toEqual([])
189192
}
190193

191-
testcase.onResult?.(result)
194+
await testcase.onResult?.(result)
195+
await testcase.after?.call(testcase, result)
192196

193-
return result
197+
return {
198+
testcase,
199+
result,
200+
}
194201
}
195202

196-
function valid(arg: ValidTestCase | string) {
197-
const result = each(arg)
198-
expect.soft(result.messages, 'no errors on valid cases').toEqual([])
199-
expect.soft(result.fixed, 'no need to fix for valid cases').toBeFalsy()
203+
async function valid(arg: ValidTestCase | string) {
204+
const result = await each(arg)
205+
expect.soft(result.result.messages, 'no errors on valid cases').toEqual([])
206+
expect.soft(result.result.fixed, 'no need to fix for valid cases').toBeFalsy()
200207
return result
201208
}
202209

203-
function invalid(arg: InvalidTestCase | string) {
204-
const result = each(arg)
205-
expect.soft(result.messages, 'expect errors').not.toEqual([])
210+
async function invalid(arg: InvalidTestCase | string) {
211+
const result = await each(arg)
212+
expect.soft(result.result.messages, 'expect errors').not.toEqual([])
206213
return result
207214
}
208215

209-
function run(cases: TestCasesOptions) {
216+
async function run(cases: TestCasesOptions) {
210217
describe(options.name || 'rule-to-test', () => {
211218
if (cases.valid?.length) {
212219
describe('valid', () => {
@@ -218,8 +225,8 @@ export function createRuleTester(options: RuleTesterInitOptions): RuleTester {
218225
if (_case.skip)
219226
run = it.skip
220227
run(`Valid #${index}: ${_case.description || _case.code}`, async () => {
221-
const result = valid(_case)
222-
await cases?.onResult?.(_case, result)
228+
const { testcase, result } = await valid(_case)
229+
await cases?.onResult?.(testcase, result)
223230
})
224231
},
225232
)
@@ -235,8 +242,8 @@ export function createRuleTester(options: RuleTesterInitOptions): RuleTester {
235242
if (_case.skip)
236243
run = it.skip
237244
run(`Invalid #${index}: ${_case.description || _case.code}`, async () => {
238-
const result = invalid(_case)
239-
await cases?.onResult?.(_case, result)
245+
const { testcase, result } = await invalid(_case)
246+
await cases?.onResult?.(testcase, result)
240247
})
241248
})
242249
})

src/types.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { Linter } from 'eslint'
22

3+
export type Awaitable<T> = Promise<T> | T
34
export interface ValidTestCaseBase extends CompatConfigOptions, RuleTesterBehaviorOptions {
45
name?: string
56
description?: string
@@ -8,6 +9,12 @@ export interface ValidTestCaseBase extends CompatConfigOptions, RuleTesterBehavi
89
filename?: string
910
only?: boolean
1011
skip?: boolean
12+
before?: (this: NormalizedTestCase, configs: Linter.Config[]) => Awaitable<void>
13+
after?: (this: NormalizedTestCase, result: Linter.FixReport) => Awaitable<void>
14+
15+
/**
16+
* @deprecated Use `after` instead
17+
*/
1118
onResult?: (result: Linter.FixReport) => void
1219
}
1320

@@ -29,12 +36,12 @@ export interface InvalidTestCaseBase extends ValidTestCaseBase {
2936
* If an array of strings is provided, it asserts that the error messageIds are equal to the array provided.
3037
* If an array of objects is provided, it asserts that the errors are partially equal to the objects provided.
3138
*/
32-
errors?: number | (string | TestCaseError)[] | ((errors: Linter.LintMessage[]) => void)
39+
errors?: number | (string | TestCaseError)[] | ((errors: Linter.LintMessage[]) => Awaitable<void>)
3340
/**
3441
* Assert if output is expected.
3542
* Pass `null` to assert that the output is the same as the input.
3643
*/
37-
output?: string | null | ((output: string, input: string) => void)
44+
output?: string | null | ((output: string, input: string) => Awaitable<void>)
3845
}
3946

4047
export interface NormalizedTestCase extends InvalidTestCaseBase {
@@ -70,19 +77,19 @@ export interface RuleTester {
7077
/**
7178
* Run a single test case
7279
*/
73-
each: (arg: TestCase) => TestExecutionResult
80+
each: (arg: TestCase) => Promise<{ testcase: NormalizedTestCase, result: TestExecutionResult }>
7481
/**
7582
* Run a single valid test case
7683
*/
77-
valid: (arg: ValidTestCase) => TestExecutionResult
84+
valid: (arg: ValidTestCase) => Promise<{ testcase: NormalizedTestCase, result: TestExecutionResult }>
7885
/**
7986
* Run a single invalid test case
8087
*/
81-
invalid: (arg: InvalidTestCase) => TestExecutionResult
88+
invalid: (arg: InvalidTestCase) => Promise<{ testcase: NormalizedTestCase, result: TestExecutionResult }>
8289
/**
8390
* ESLint's RuleTester style test runner, that runs multiple test cases
8491
*/
85-
run: (options: TestCasesOptions) => void
92+
run: (options: TestCasesOptions) => Promise<void>
8693
}
8794

8895
export interface RuleTesterBehaviorOptions {

0 commit comments

Comments
 (0)