Tooling to test ESLint rules with comprehensive TypeScript support and advanced testing capabilities
—
Comprehensive test case definition system with support for valid and invalid scenarios, autofix testing, and suggestion validation.
Container for organizing valid and invalid test cases for rule testing.
/**
* Container for test cases passed to the run method
*/
interface RunTests<MessageIds extends string, Options extends readonly unknown[]> {
/** Array of valid test cases that should not trigger rule violations */
readonly valid: readonly (string | ValidTestCase<Options>)[];
/** Array of invalid test cases that should trigger rule violations */
readonly invalid: readonly InvalidTestCase<MessageIds, Options>[];
}Usage Examples:
import { RuleTester } from "@typescript-eslint/rule-tester";
const ruleTester = new RuleTester();
ruleTester.run("my-rule", myRule, {
valid: [
// Simple string cases
"const x = 1;",
"function foo() { return 'hello'; }",
// Complex cases with configuration
{
code: "interface User { name: string; }",
name: "interface declaration should be allowed",
},
],
invalid: [
{
code: "var x = 1;",
errors: [{ messageId: "noVar" }],
output: "const x = 1;",
},
],
});Configuration for test cases that should not trigger any rule violations.
/**
* Configuration for valid test cases that should not trigger rule violations
*/
interface ValidTestCase<Options extends readonly unknown[]> {
/** Code for the test case */
readonly code: string;
/** Name for the test case for better test output */
readonly name?: string;
/** The fake filename for the test case (useful for rules that check filenames) */
readonly filename?: string;
/** Options for the rule being tested */
readonly options?: Readonly<Options>;
/** Language-specific options for the test case */
readonly languageOptions?: TestLanguageOptions;
/** Settings for the test case */
readonly settings?: Readonly<SharedConfigurationSettings>;
/** Constraints that must pass in the current environment for the test to run */
readonly dependencyConstraints?: DependencyConstraint;
/** Run this case exclusively for debugging in supported test frameworks */
readonly only?: boolean;
/** Skip this case in supported test frameworks */
readonly skip?: boolean;
/** Function to execute before testing the case */
readonly before?: () => void;
/** Function to execute after testing the case regardless of its result */
readonly after?: () => void;
}Usage Examples:
const validTestCases: ValidTestCase<[{ allowUnions: boolean }]>[] = [
// Minimal test case
{
code: "const x = 1;",
},
// Test case with name and options
{
code: "type Status = 'active' | 'inactive';",
name: "union types should be allowed when option is enabled",
options: [{ allowUnions: true }],
},
// Test case with specific filename
{
code: "export default class Component {}",
filename: "Component.tsx",
languageOptions: {
parserOptions: {
ecmaFeatures: { jsx: true },
},
},
},
// Test case with environment constraints
{
code: "const promise: Promise<string> = Promise.resolve('hello');",
dependencyConstraints: {
typescript: ">=4.0.0",
},
},
// Test case with setup and teardown
{
code: "console.log('test');",
before: () => {
jest.spyOn(console, 'log').mockImplementation(() => {});
},
after: () => {
jest.restoreAllMocks();
},
},
// Debugging specific test case
{
code: "function debug() { return 'debugging'; }",
name: "debug function",
only: true, // Run only this test case
},
];Configuration for test cases that should trigger rule violations with expected errors and fixes.
/**
* Configuration for invalid test cases that should trigger rule violations
*/
interface InvalidTestCase<MessageIds extends string, Options extends readonly unknown[]>
extends ValidTestCase<Options> {
/** Constraints that must pass in the current environment for the test to run */
readonly dependencyConstraints?: DependencyConstraint;
/** Expected errors that should be reported */
readonly errors: readonly TestCaseError<MessageIds>[];
/** Expected code after autofixes are applied */
readonly output?: string | string[] | null;
}Usage Examples:
const invalidTestCases: InvalidTestCase<"noVar" | "preferConst", []>[] = [
// Basic invalid test case
{
code: "var x = 1;",
errors: [{ messageId: "noVar" }],
output: "const x = 1;",
},
// Test case with specific error location
{
code: "function foo() {\n var x = 1;\n return x;\n}",
errors: [{
messageId: "noVar",
line: 2,
column: 3,
endLine: 2,
endColumn: 6,
}],
output: "function foo() {\n const x = 1;\n return x;\n}",
},
// Test case with multiple errors
{
code: "var a = 1; var b = 2;",
errors: [
{ messageId: "noVar", line: 1, column: 1 },
{ messageId: "noVar", line: 1, column: 12 },
],
output: "const a = 1; const b = 2;",
},
// Multi-pass autofix (array of outputs)
{
code: "var a = 1; var b = a;",
errors: [
{ messageId: "noVar" },
{ messageId: "preferConst" },
],
output: [
"let a = 1; var b = a;", // First pass
"const a = 1; let b = a;", // Second pass
"const a = 1; const b = a;", // Final pass
],
},
// Test case with no autofix
{
code: "eval('var x = 1');",
errors: [{ messageId: "noEval" }],
output: null, // Explicitly no autofix expected
},
// Test case with suggestions
{
code: "function foo(x: any) { return x; }",
errors: [{
messageId: "noAny",
type: "TSAnyKeyword",
suggestions: [
{
messageId: "useUnknown",
output: "function foo(x: unknown) { return x; }",
},
{
messageId: "useGeneric",
output: "function foo<T>(x: T) { return x; }",
},
],
}],
},
];Expected error configuration for invalid test cases with detailed location and suggestion information.
/**
* Expected error configuration for invalid test cases
*/
interface TestCaseError<MessageIds extends string> {
/** Reported message ID (required) */
readonly messageId: MessageIds;
/** The 1-based line number of the reported start location */
readonly line?: number;
/** The 1-based column number of the reported start location */
readonly column?: number;
/** The 1-based line number of the reported end location */
readonly endLine?: number;
/** The 1-based column number of the reported end location */
readonly endColumn?: number;
/** The type of the reported AST node */
readonly type?: AST_NODE_TYPES | AST_TOKEN_TYPES;
/** The data used to fill the message template */
readonly data?: ReportDescriptorMessageData;
/** Expected suggestions for fixing the error */
readonly suggestions?: readonly SuggestionOutput<MessageIds>[] | null;
}Usage Examples:
// Minimal error expectation
const simpleError: TestCaseError<"noVar"> = {
messageId: "noVar",
};
// Detailed error expectation with location
const detailedError: TestCaseError<"noVar"> = {
messageId: "noVar",
line: 1,
column: 1,
endLine: 1,
endColumn: 4,
type: "VariableDeclaration",
};
// Error with template data
const errorWithData: TestCaseError<"unexpectedType"> = {
messageId: "unexpectedType",
data: {
expected: "string",
actual: "number",
},
};
// Error with suggestions
const errorWithSuggestions: TestCaseError<"noAny"> = {
messageId: "noAny",
suggestions: [
{
messageId: "useUnknown",
output: "function foo(x: unknown) { return x; }",
},
{
messageId: "useGeneric",
output: "function foo<T>(x: T) { return x; }",
data: { genericName: "T" },
},
],
};Expected suggestion configuration for rule suggestions with their expected outputs.
/**
* Expected suggestion configuration for rule suggestions
*/
interface SuggestionOutput<MessageIds extends string> {
/** Suggestion message ID (required) */
readonly messageId: MessageIds;
/** Expected output after applying the suggestion (required) */
readonly output: string;
/** The data used to fill the message template */
readonly data?: ReportDescriptorMessageData;
}Usage Examples:
const suggestions: SuggestionOutput<"useUnknown" | "useGeneric">[] = [
// Simple suggestion
{
messageId: "useUnknown",
output: "function foo(x: unknown) { return x; }",
},
// Suggestion with template data
{
messageId: "useGeneric",
output: "function foo<T>(x: T) { return x; }",
data: {
genericName: "T",
paramName: "x",
},
},
];/**
* Language-specific options for individual test cases
*/
interface TestLanguageOptions {
/** The absolute path for the parser */
readonly parser?: Readonly<Parser.LooseParserModule>;
/** Options for the parser */
readonly parserOptions?: Readonly<ParserOptions>;
/** The additional global variables */
readonly globals?: Readonly<Linter.GlobalsConfig>;
/** Environments for the test case */
readonly env?: Readonly<Linter.EnvironmentConfig>;
}Usage Examples:
// TypeScript with JSX support
const tsxLanguageOptions: TestLanguageOptions = {
parserOptions: {
ecmaVersion: 2022,
sourceType: "module",
ecmaFeatures: {
jsx: true,
},
project: "./tsconfig.json",
},
};
// Browser environment with globals
const browserLanguageOptions: TestLanguageOptions = {
env: {
browser: true,
es2022: true,
},
globals: {
window: "readonly",
document: "readonly",
console: "readonly",
},
};
// Node.js environment
const nodeLanguageOptions: TestLanguageOptions = {
env: {
node: true,
es2022: true,
},
globals: {
process: "readonly",
Buffer: "readonly",
},
};Install with Tessl CLI
npx tessl i tessl/npm-typescript-eslint--rule-tester