Skip to content

Commit b29c468

Browse files
authored
feat: add error message id generic type support (#14)
1 parent 66fed32 commit b29c468

File tree

4 files changed

+32
-31
lines changed

4 files changed

+32
-31
lines changed

src/rule-tester.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { isUsingTypeScriptParser, normalizeCaseError, normalizeTestCase } from '
1515
import { getAjvInstance, getRuleOptionsSchema } from './vendor/ajv'
1616
import { applyFixes } from './vendor/fixer'
1717

18-
export function createRuleTester<RuleOptions = any>(options: RuleTesterInitOptions): RuleTester<RuleOptions> {
18+
export function createRuleTester<RuleOptions = any, MessageId extends string = string>(options: RuleTesterInitOptions): RuleTester<RuleOptions, MessageId> {
1919
const languageOptions = deepMerge(
2020
options.languageOptions ?? {
2121
parser: options.parser,
@@ -48,7 +48,7 @@ export function createRuleTester<RuleOptions = any>(options: RuleTesterInitOptio
4848
...options.defaultFilenames,
4949
}
5050

51-
async function each(c: TestCase<RuleOptions>) {
51+
async function each(c: TestCase<RuleOptions, MessageId>) {
5252
const testcase = normalizeTestCase(c, languageOptions, defaultFilenames)
5353

5454
const {
@@ -207,13 +207,13 @@ export function createRuleTester<RuleOptions = any>(options: RuleTesterInitOptio
207207
return result
208208
}
209209

210-
async function invalid(arg: InvalidTestCase<RuleOptions> | string) {
210+
async function invalid(arg: InvalidTestCase<RuleOptions, MessageId> | string) {
211211
const result = await each(arg)
212212
expect.soft(result.result.messages, 'expect errors').not.toEqual([])
213213
return result
214214
}
215215

216-
async function run(cases: TestCasesOptions<RuleOptions>) {
216+
async function run(cases: TestCasesOptions<RuleOptions, MessageId>) {
217217
describe(options.name || 'rule-to-test', () => {
218218
if (cases.valid?.length) {
219219
describe('valid', () => {

src/run.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,21 @@ import { createRuleTester } from './rule-tester'
44
/**
55
* Shortcut to run test cases for a rule
66
*/
7-
export function run<RuleOptions = any>(options: TestCasesOptions<RuleOptions> & RuleTesterInitOptions) {
8-
const tester = createRuleTester<RuleOptions>(options)
7+
export function run<RuleOptions = any, MessageId extends string = string>(options: TestCasesOptions<RuleOptions, MessageId> & RuleTesterInitOptions) {
8+
const tester = createRuleTester<RuleOptions, MessageId>(options)
99
return tester.run(options)
1010
}
1111

1212
/**
1313
* Shortcut to run test cases for a rule in classic style
1414
*/
15-
export function runClassic<RuleOptions = any>(
15+
export function runClassic<RuleOptions = any, MessageId extends string = string>(
1616
ruleName: string,
1717
rule: RuleModule,
18-
cases: TestCasesOptions<RuleOptions>,
18+
cases: TestCasesOptions<RuleOptions, MessageId>,
1919
options?: RuleTesterInitOptions,
2020
) {
21-
const tester = createRuleTester<RuleOptions>({
21+
const tester = createRuleTester<RuleOptions, MessageId>({
2222
rule,
2323
name: ruleName,
2424
...options,

src/types.ts

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

33
export type Awaitable<T> = Promise<T> | T
4-
export interface ValidTestCaseBase<RuleOptions = any> extends CompatConfigOptions, RuleTesterBehaviorOptions {
4+
export interface ValidTestCaseBase<RuleOptions = any, MessageId extends string = string> extends CompatConfigOptions, RuleTesterBehaviorOptions {
55
name?: string
66
description?: string
77
code: string
88
options?: RuleOptions
99
filename?: string
1010
only?: boolean
1111
skip?: boolean
12-
before?: (this: NormalizedTestCase, configs: Linter.Config[]) => Awaitable<void>
13-
after?: (this: NormalizedTestCase, result: Linter.FixReport) => Awaitable<void>
12+
before?: (this: NormalizedTestCase<RuleOptions, MessageId>, configs: Linter.Config[]) => Awaitable<void>
13+
after?: (this: NormalizedTestCase<RuleOptions, MessageId>, result: Linter.FixReport) => Awaitable<void>
1414

1515
/**
1616
* @deprecated Use `after` instead
1717
*/
1818
onResult?: (result: Linter.FixReport) => void
1919
}
2020

21-
export interface TestCaseError extends Partial<Linter.LintMessage> {
21+
export type TestCaseError<MessageId extends string = string> = Partial<Linter.LintMessage> & {
2222
/**
2323
* Data for interpolate the error message
2424
*/
@@ -27,32 +27,33 @@ export interface TestCaseError extends Partial<Linter.LintMessage> {
2727
* Alias to `nodeType`
2828
*/
2929
type?: string
30+
messageId?: MessageId
3031
}
3132

32-
export interface InvalidTestCaseBase<RuleOptions = any> extends ValidTestCaseBase<RuleOptions> {
33+
export interface InvalidTestCaseBase<RuleOptions = any, MessageId extends string = string> extends ValidTestCaseBase<RuleOptions, MessageId> {
3334
/**
3435
* Expected errors.
3536
* If a number is provided, it asserts that the number of errors is equal to the number provided.
3637
* If an array of strings is provided, it asserts that the error messageIds are equal to the array provided.
3738
* If an array of objects is provided, it asserts that the errors are partially equal to the objects provided.
3839
*/
39-
errors?: number | (string | TestCaseError)[] | ((errors: Linter.LintMessage[]) => Awaitable<void>)
40+
errors?: number | (MessageId | TestCaseError<MessageId>)[] | ((errors: Linter.LintMessage[]) => Awaitable<void>)
4041
/**
4142
* Assert if output is expected.
4243
* Pass `null` to assert that the output is the same as the input.
4344
*/
4445
output?: string | null | ((output: string, input: string) => Awaitable<void>)
4546
}
4647

47-
export interface NormalizedTestCase extends InvalidTestCaseBase {
48+
export interface NormalizedTestCase<RuleOptions = any, MessageId extends string = string> extends InvalidTestCaseBase<RuleOptions, MessageId> {
4849
type: 'valid' | 'invalid'
4950
code: string
5051
}
5152

52-
export type InvalidTestCase<RuleOptions = any> = InvalidTestCaseBase<RuleOptions> | string
53+
export type InvalidTestCase<RuleOptions = any, MessageId extends string = string> = InvalidTestCaseBase<RuleOptions, MessageId> | string
5354
export type ValidTestCase<RuleOptions = any> = ValidTestCaseBase<RuleOptions> | string
5455

55-
export type TestCase<RuleOptions = any> = ValidTestCase<RuleOptions> | InvalidTestCase<RuleOptions>
56+
export type TestCase<RuleOptions = any, MessageId extends string = string> = ValidTestCase<RuleOptions> | InvalidTestCase<RuleOptions, MessageId>
5657

5758
export interface TestExecutionResult extends Linter.FixReport {
5859
/**
@@ -73,23 +74,23 @@ export interface CompatConfigOptions {
7374

7475
export type RuleModule = any // to allow any rule module
7576

76-
export interface RuleTester<RuleOptions = any> {
77+
export interface RuleTester<RuleOptions = any, MessageId extends string = string> {
7778
/**
7879
* Run a single test case
7980
*/
80-
each: (arg: TestCase<RuleOptions>) => Promise<{ testcase: NormalizedTestCase, result: TestExecutionResult }>
81+
each: (arg: TestCase<RuleOptions, MessageId>) => Promise<{ testcase: NormalizedTestCase<RuleOptions, MessageId>, result: TestExecutionResult }>
8182
/**
8283
* Run a single valid test case
8384
*/
84-
valid: (arg: ValidTestCase<RuleOptions>) => Promise<{ testcase: NormalizedTestCase, result: TestExecutionResult }>
85+
valid: (arg: ValidTestCase<RuleOptions>) => Promise<{ testcase: NormalizedTestCase<RuleOptions, MessageId>, result: TestExecutionResult }>
8586
/**
8687
* Run a single invalid test case
8788
*/
88-
invalid: (arg: InvalidTestCase<RuleOptions>) => Promise<{ testcase: NormalizedTestCase, result: TestExecutionResult }>
89+
invalid: (arg: InvalidTestCase<RuleOptions, MessageId>) => Promise<{ testcase: NormalizedTestCase<RuleOptions, MessageId>, result: TestExecutionResult }>
8990
/**
9091
* ESLint's RuleTester style test runner, that runs multiple test cases
9192
*/
92-
run: (options: TestCasesOptions<RuleOptions>) => Promise<void>
93+
run: (options: TestCasesOptions<RuleOptions, MessageId>) => Promise<void>
9394
}
9495

9596
export interface RuleTesterBehaviorOptions {
@@ -137,11 +138,11 @@ export interface RuleTesterInitOptions extends CompatConfigOptions, RuleTesterBe
137138
defaultFilenames?: Partial<DefaultFilenames>
138139
}
139140

140-
export interface TestCasesOptions<RuleOptions = any> {
141+
export interface TestCasesOptions<RuleOptions = any, MessageId extends string = string> {
141142
valid?: (ValidTestCase<RuleOptions> | string)[]
142-
invalid?: (InvalidTestCase<RuleOptions> | string)[]
143+
invalid?: (InvalidTestCase<RuleOptions, MessageId> | string)[]
143144
/**
144145
* Callback to be called after each test case
145146
*/
146-
onResult?: (_case: NormalizedTestCase, result: Linter.FixReport) => void | Promise<void>
147+
onResult?: (_case: NormalizedTestCase<RuleOptions, MessageId>, result: Linter.FixReport) => void | Promise<void>
147148
}

src/utils.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,16 @@ import { interpolate } from './vendor/interpolate'
77

88
export { unindent as $, unindent }
99

10-
export function normalizeTestCase(
11-
c: TestCase,
10+
export function normalizeTestCase<RuleOptions = any, MessageId extends string = string>(
11+
c: TestCase<RuleOptions, MessageId>,
1212
languageOptions: Linter.Config['languageOptions'],
1313
defaultFilenames: DefaultFilenames,
1414
type?: 'valid' | 'invalid',
15-
): NormalizedTestCase {
15+
): NormalizedTestCase<RuleOptions, MessageId> {
1616
const obj = typeof c === 'string'
1717
? { code: c }
1818
: { ...c }
19-
const normalized = obj as NormalizedTestCase
19+
const normalized = obj as NormalizedTestCase<RuleOptions, MessageId>
2020
normalized.type ||= type || (('errors' in obj || 'output' in obj) ? 'invalid' : 'valid')
2121

2222
const merged: Linter.Config['languageOptions'] = {
@@ -47,7 +47,7 @@ export function normalizeTestCase(
4747
return normalized
4848
}
4949

50-
export function normalizeCaseError(error: TestCaseError | string, rule?: RuleModule): Partial<Linter.LintMessage> {
50+
export function normalizeCaseError<MessageId extends string = string>(error: TestCaseError<MessageId> | MessageId, rule?: RuleModule): Partial<Linter.LintMessage> {
5151
if (typeof error === 'string')
5252
return { messageId: error }
5353
const clone = { ...error }

0 commit comments

Comments
 (0)