jest
Use when implementing or expanding ESLint rule test suites.
SKILL.md.claude/skills/jest/SKILL.md
GitHubGiven 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,ruleTesterJsonfromsrc/utils/ruleTester.tsfor TS, JSX/React, and JSON rules.- Jest with
ts-jestfor executing suites. - Auto-fix assertions via
outputin 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 runsnpm run test:ciand is the only surface that runs them. ThePreToolUseBash guard deniesnpm testlocally and publishesnpm run test:relatedin 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
outputwhen verifying auto-fix.
invalid: [
{
code: 'const x = var;',
errors: [{ messageId: 'unexpectedVar' }],
output: 'const x = fixedVar;',
},
]
Usage Instructions
- Analyze the rule (targets, visitors, edge cases).
- Select the correct tester variant.
- Draft cases: 10+ valid and 10+ invalid, covering nested and tricky syntax.
- Run
npx jest src/tests/<rule>.test.ts. - Refine until coverage ≥90% and all assertions pass.
- 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:
messageIdshould match the rule's purpose. - Use filenames when rule behavior depends on file type/path.
- Always assert
outputfor fixable rules to guard against unsafe fixes.