jest

Use when implementing or expanding ESLint rule test suites.

SKILL.md

GitHub

Given an ESLint rule implementation (e.g., src/rules/my-rule.ts), implement or update a comprehensive Jest test suite in src/tests/ (e.g., src/tests/my-rule.test.ts). The suite must achieve high code coverage (90% minimum, 100% preferred) and pass all checks.

Purpose

Ensure every rule ships with a thorough, repeatable test suite that prevents false positives, catches regressions, and documents expected behavior.

Why the System Was Built

Reliable rule tests keep 100+ custom rules stable across refactors and TypeScript upgrades by codifying expected diagnostics, auto-fixes, and edge cases.

Definitions

  • RuleTester: Harness that runs lint cases against a rule and asserts reported errors/fixes.
  • Valid case: Code that must NOT trigger the rule (guards against false positives).
  • Invalid case: Code that MUST trigger the rule with expected messageId (prevents false negatives and verifies fixes).
  • Coverage target: 90% minimum, 100% preferred across rule logic and fixers.

Core Components

  • ruleTesterTs, ruleTesterJsx, ruleTesterJson from src/utils/ruleTester.ts for TS, JSX/React, and JSON rules.
  • Jest with ts-jest for executing suites.
  • Auto-fix assertions via output in invalid cases when the rule is fixable.

Directory Structure

  • Tests live in src/tests/, mirroring rule filenames (src/rules/foo.ts → src/tests/foo.test.ts).
  • RuleTester utilities: src/utils/ruleTester.ts.
  • Coverage output: coverage/.

Setup and Execution

  • Run a single suite: npx jest src/tests/<rule>.test.ts
  • Run everything related to a change: npm run test:related [-- <file|flag>...]
  • Run all 355 suites: CI's test-report.yml, which runs npm run test:ci and is the only surface that runs them. The PreToolUse Bash guard denies npm test locally and publishes npm run test:related in its place: seventeen suites take 8 to 22 minutes each, and an ungoverned local run fills the 31 GB box this repo's maintainer loop shares with two others.

Relatedness, and why a rule change runs only its own suite

jest --findRelatedTests is TRANSITIVE, and jest computes the import graph with a regex over source text: only a LITERAL module specifier creates an edge. src/index.ts imports every rule, so a file spelling require('..') or from '../index' is related to all 195 of them, and every such file makes one rule change select every corpus guard along with it.

src/utils/loadPlugin.ts is therefore the one way to load the plugin. It resolves the index through a computed path, which the extractor cannot see, so the runtime module and its registry semantics are identical while the edge is absent. src/tests/plugin-edge-isolation.test.ts is the ratchet: it runs jest's own dependencyExtractor over every file under src/ and fails on any literal specifier resolving to the index, on a literal inside the helper itself, and on any rule whose own suite does not import it directly. Write loadPlugin(), never the literal.

A change to src/index.ts therefore relates to nothing, so npm run test:related appends the pinned cheap registry guards in scripts/registry-guard-suites.ts when the registry is among the changed files.

Test Case Patterns

import { ruleTesterTs, ruleTesterJsx, ruleTesterJson } from '../utils/ruleTester';
import { myRule } from '../rules/my-rule';

// TypeScript rules
ruleTesterTs.run('my-rule', myRule, { valid: [], invalid: [] });

// React/JSX rules
ruleTesterJsx.run('my-rule', myRule, { valid: [], invalid: [] });

// JSON rules
ruleTesterJson.run('my-rule', myRule, { valid: [], invalid: [] });

✅ Valid Cases (false-positive prevention)

  • Good patterns that must NOT report.
  • Include similar-but-correct code and ignored paths.
valid: [
  'const x = 1;',
  { code: 'const x = 1;', filename: 'src/utils/math.ts', options: [{ allow: true }] },
]

❌ Invalid Cases (false-negative prevention)

  • Violations that MUST report with the right messageId.
  • Provide output when verifying auto-fix.
invalid: [
  {
    code: 'const x = var;',
    errors: [{ messageId: 'unexpectedVar' }],
    output: 'const x = fixedVar;',
  },
]

Usage Instructions

  1. Analyze the rule (targets, visitors, edge cases).
  2. Select the correct tester variant.
  3. Draft cases: 10+ valid and 10+ invalid, covering nested and tricky syntax.
  4. Run npx jest src/tests/<rule>.test.ts.
  5. Refine until coverage ≥90% and all assertions pass.
  6. Keep fixes minimal and focused on requested behavior.

How It Makes Developers' Lives Easier

  • Prevents regressions when refactoring rules or upgrading TypeScript/ESLint.
  • Documents expected diagnostics and fixes for future contributors.
  • Speeds debugging by localizing failures to a single rule suite.

Critical Insights for Maintainers

  • Test the intent: messageId should match the rule's purpose.
  • Use filenames when rule behavior depends on file type/path.
  • Always assert output for fixable rules to guard against unsafe fixes.