eslint-vitest-rule-tester

📁 antfu-collective/eslint-vitest-rule-tester 📅 2 days ago
1
总安装量
1
周安装量
#49409
全站排名
安装命令
npx skills add https://github.com/antfu-collective/eslint-vitest-rule-tester --skill eslint-vitest-rule-tester

Agent 安装分布

windsurf 1
amp 1
opencode 1
kimi-cli 1
codex 1

Skill 文档

Instructions

You are helping users work with eslint-vitest-rule-tester, a library that provides ESLint rule testing with Vitest integration.

Core APIs

Two testing approaches:

  1. run({ name, rule, valid, invalid, ...config }) – All-in-one object style (config directly in object)
  2. createRuleTester({ name, rule, configs }) – Returns { valid, invalid } for explicit Vitest describe/it blocks (note: configs key, not languageOptions)

Key extensions:

  • output and errors fields can be functions for custom assertions and snapshots
  • onResult hook for full result object validation
  • No globals: true required in Vitest config

When Helping Users

Identify testing style first:

  • Check if they have existing tests to match their pattern
  • Default to run() for simplicity (see Pattern 1 below)
  • Use createRuleTester() for explicit Vitest test blocks with individual it() tests (Pattern 3)

Configuration:

  • Use languageOptions.parser: tsParser for TypeScript (import from @typescript-eslint/parser)
  • Use languageOptions.parserOptions for JS options (ecmaVersion, sourceType)
  • Put shared config in tester initialization, not in every test case
  • Note: In createRuleTester, config goes under configs key, not at top level

Type safety:

  • Use satisfies TestCasesOptions['valid'] for valid test arrays
  • Use satisfies TestCasesOptions['invalid'] for invalid test arrays
  • Extract test cases as constants before passing to run() (see Pattern 1)

Snapshots:

  • Use onResult hook with toMatchSnapshot() for all invalid cases (Pattern 1)
  • Use function-based output/errors with toMatchInlineSnapshot() for inline snapshots (Pattern 2)
  • Recommend toMatchInlineSnapshot() over toMatchSnapshot() when possible for better visibility

Common patterns:

// Pattern 1: run() with extracted test cases (from top-level-function.test.ts)
import type { TestCasesOptions } from 'eslint-vitest-rule-tester'
import { run } from 'eslint-vitest-rule-tester'
import { expect } from 'vitest'
import * as tsParser from '@typescript-eslint/parser'

const valids = [
  'function foo() {}',
  // allow arrow function inside function
  'function foo() { const bar = () => {} }',
] satisfies TestCasesOptions['valid']

const invalids = [
  {
    code: 'const foo = () => {}',
    output: 'function foo () {}',
    errors: [{ messageId: 'topLevelFunctionDeclaration' }],
  },
] satisfies TestCasesOptions['invalid']

run({
  name: 'top-level-function',
  rule,
  languageOptions: { parser: tsParser },
  valid: valids,
  invalid: invalids,
  onResult(_case, result) {
    if (_case.type === 'invalid')
      expect(result.output).toMatchSnapshot()
  },
})

// Pattern 2: Function-based output/errors (from README)
run({
  name: 'rule-name',
  rule,
  invalid: [
    {
      code: 'let foo = 1',
      output(output) {
        expect(output.slice(0, 3)).toBe('let')
        expect(output).toMatchInlineSnapshot(`"const foo = 1;"`)
      },
      errors(errors) {
        expect(errors).toHaveLength(1)
        expect(errors.map(e => e.messageId))
          .toMatchInlineSnapshot(`["preferConst"]`)
      },
    },
  ],
})

// Pattern 3: Explicit test suites (from README)
import { createRuleTester } from 'eslint-vitest-rule-tester'
import { describe, it } from 'vitest'

describe('rule-name', () => {
  const { valid, invalid } = createRuleTester({
    name: 'rule-name',
    rule,
    configs: {
      languageOptions: {
        parserOptions: { ecmaVersion: 2020, sourceType: 'module' },
      },
    },
  })

  it('valid case 1', () => {
    valid('const foo = 1')
  })

  it('invalid case 1 with snapshot', async () => {
    const { result } = await invalid({
      code: 'const foo = 1',
      errors: ['error-message-id'],
    })
    expect(result.output).toMatchSnapshot()
  })
})

Troubleshooting

  • Version errors: Requires ESLint v9.10+, check package.json
  • Snapshot failures: Run vitest -u to update snapshots
  • Parser issues: Add languageOptions.parser for TypeScript/JSX
  • Type errors: Import from eslint-vitest-rule-tester, not eslint

Best Practices

  • Match existing patterns – Check existing test files in the project first
  • Extract test cases – Define valids and invalids as constants with satisfies for type safety
  • Add comments – Use inline comments to explain what each test case validates (e.g., // allow arrow function inside function)
  • Use snapshots – Prefer onResult hook for bulk snapshot testing of all invalid cases
  • Consolidate config – Put shared parser/language options at tester level, not per test case
  • TypeScript support – Import @typescript-eslint/parser as tsParser and use in languageOptions.parser
  • Error format – Use errors: [{ messageId: 'errorId' }] for structured errors, or errors: ['errorId'] for simple cases