eslint-vitest-rule-tester

raw JSON →
3.1.0 verified Sat Apr 25 auth: no javascript

ESLint rule tester integration with Vitest, providing a modern testing experience for ESLint rules. Current stable version is 3.1.0, supporting ESLint v9.10+ and Vitest v1-v4. Key differentiators: built-in snapshot support for outputs and errors, async hooks, onResult callback for custom assertions, and TypeScript types included. Released actively by the antfu-collective, used by ESLint Stylistic and other popular plugins. No need for Vitest globals.

error Cannot find module 'eslint-vitest-rule-tester'
cause Package not installed or not in node_modules.
fix
Run 'npm install -D eslint-vitest-rule-tester'.
error export 'run' (imported as 'run') was not found in 'eslint-vitest-rule-tester'
cause Using wrong import syntax (named vs default) or typo.
fix
Use 'import { run } from 'eslint-vitest-rule-tester''.
error RuleTester is not a constructor
cause Trying to instantiate RuleTester from this package; it exports functions, not classes.
fix
Use 'run()' or 'createRuleTester()' instead.
breaking Requires ESLint v9.10+ (v3.0.0+)
fix Upgrade ESLint to v9.10 or higher.
breaking Dropped @types/eslint dependency (v2.3.0+)
fix If you relied on @types/eslint types, install them separately.
gotcha ESM-only package; CommonJS require() may fail.
fix Use dynamic import() or switch to ESM in your project.
deprecated Vitest v4 is allowed since v3.0.0
fix Upgrade to v3.0.0+ for Vitest v4 support.
gotcha Async hooks are not supported before v2.0.0
fix Upgrade to v2.0.0+ to use async output/errors/after/before.
npm install eslint-vitest-rule-tester
yarn add eslint-vitest-rule-tester
pnpm add eslint-vitest-rule-tester

Demonstrates the run() function with a custom rule, snapshotting output and errors.

import { run } from 'eslint-vitest-rule-tester';
import { expect } from 'vitest';

// Define a simple ESLint rule (example)
const myRule = {
  meta: { fixable: 'code', messages: { noLet: 'Use const instead of let.' } },
  create(context) {
    return {
      VariableDeclaration(node) {
        if (node.kind === 'let') {
          context.report({
            node,
            messageId: 'noLet',
            fix(fixer) {
              return fixer.replaceTextRange(
                [node.range[0], node.range[0] + 3],
                'const'
              );
            }
          });
        }
      }
    };
  }
};

run({
  name: 'no-let',
  rule: myRule,
  parserOptions: { ecmaVersion: 2020, sourceType: 'module' },
  valid: [
    'const foo = 1'
  ],
  invalid: [
    {
      code: 'let foo = 1',
      output(output) {
        expect(output).toMatchInlineSnapshot('"const foo = 1;"');
      },
      errors(errors) {
        expect(errors).toHaveLength(1);
        expect(errors[0].messageId).toBe('noLet');
      }
    }
  ]
});