|
| 1 | +# ESLint Plugin Testing Library - Copilot Instructions |
| 2 | + |
| 3 | +## Repository Overview |
| 4 | + |
| 5 | +This is an ESLint plugin that enforces best practices for Testing Library (DOM, React, Vue, Angular, Svelte, Marko). The plugin provides 30 ESLint rules to catch common mistakes when writing tests with Testing Library frameworks. |
| 6 | + |
| 7 | +**Language**: TypeScript |
| 8 | +**Package Manager**: pnpm (version specified in `package.json` `packageManager` field) |
| 9 | +**Node.js**: Version specified in `.nvmrc` (supported versions in `package.json` `engines.node`) |
| 10 | +**Build Tool**: tsdown (TypeScript bundler using Rolldown) |
| 11 | +**Test Framework**: Vitest |
| 12 | +**Code Coverage**: 90% threshold (branches, functions, lines, statements) |
| 13 | + |
| 14 | +## Prerequisites |
| 15 | + |
| 16 | +**CRITICAL**: This project uses **pnpm**, not npm or yarn. Always use `pnpm` commands. |
| 17 | + |
| 18 | +1. Enable pnpm via corepack: `corepack enable pnpm` (uses version from `package.json` `packageManager` field) |
| 19 | +2. Node.js version is specified in `.nvmrc` |
| 20 | +3. Install dependencies: `pnpm install` (this MUST be run first) |
| 21 | + |
| 22 | +## Build and Validation Workflow |
| 23 | + |
| 24 | +**ALWAYS run in this exact order when making changes:** |
| 25 | + |
| 26 | +### 1. Install Dependencies (First Time Only) |
| 27 | + |
| 28 | +```bash |
| 29 | +pnpm install |
| 30 | +``` |
| 31 | + |
| 32 | +This installs all dependencies and sets up git hooks via husky. |
| 33 | + |
| 34 | +### 2. Development Workflow |
| 35 | + |
| 36 | +```bash |
| 37 | +# Lint your code (auto-fix available) |
| 38 | +pnpm run lint # Check for issues |
| 39 | +pnpm run lint:fix # Auto-fix issues |
| 40 | + |
| 41 | +# Type check (no auto-fix) |
| 42 | +pnpm run type-check |
| 43 | + |
| 44 | +# Format code |
| 45 | +pnpm run format # Auto-format |
| 46 | +pnpm run format:check # Check formatting |
| 47 | + |
| 48 | +# Run tests |
| 49 | +pnpm run test # Run all tests |
| 50 | +pnpm run test:watch # Watch mode |
| 51 | +pnpm run test:ci # With coverage (required for CI) |
| 52 | +``` |
| 53 | + |
| 54 | +### 3. Build |
| 55 | + |
| 56 | +```bash |
| 57 | +pnpm run build |
| 58 | +``` |
| 59 | + |
| 60 | +**Time**: ~2 seconds. Builds both ESM (`dist/index.mjs`) and CJS (`dist/index.cjs`) formats. |
| 61 | +**Output**: `dist/` directory (gitignored) |
| 62 | + |
| 63 | +### 4. Full Validation (Run Before Committing) |
| 64 | + |
| 65 | +```bash |
| 66 | +pnpm run validate-gen-all |
| 67 | +``` |
| 68 | + |
| 69 | +**Time**: ~10 seconds. Validates that generated configs and docs are up to date. |
| 70 | +This runs: |
| 71 | + |
| 72 | +- `validate-gen:configs` - Checks configs are generated correctly |
| 73 | +- `validate-gen:docs` - Checks documentation is up to date |
| 74 | + |
| 75 | +### 5. Bundle Check |
| 76 | + |
| 77 | +```bash |
| 78 | +pnpm run bundle-check |
| 79 | +``` |
| 80 | + |
| 81 | +**Time**: ~3 seconds. Validates package exports with publint. |
| 82 | + |
| 83 | +## Testing |
| 84 | + |
| 85 | +**Test Location**: `tests/` directory mirrors `src/` structure |
| 86 | +**Test Files**: 34 test files with thousands of tests |
| 87 | +**Test Time**: ~40 seconds for full suite |
| 88 | + |
| 89 | +```bash |
| 90 | +pnpm run test # Run all tests once |
| 91 | +pnpm run test:watch # Watch mode for development |
| 92 | +pnpm run test:ci # CI mode with coverage and JUnit report |
| 93 | +pnpm run test:ui # Open Vitest UI |
| 94 | +``` |
| 95 | + |
| 96 | +**Coverage thresholds** (90% required): branches, functions, lines, statements |
| 97 | + |
| 98 | +## Project Structure |
| 99 | + |
| 100 | +### Root Files |
| 101 | + |
| 102 | +- `package.json` - Dependencies and scripts |
| 103 | +- `tsconfig.json` - TypeScript configuration (strict mode, ES2023 target) |
| 104 | +- `eslint.config.js` - Flat ESLint config (ESLint v9) |
| 105 | +- `vitest.config.ts` - Test configuration |
| 106 | +- `prettier.config.js` - Prettier config (tabs, single quotes) |
| 107 | +- `.nvmrc` - Node.js version (22) |
| 108 | +- `pnpm-lock.yaml` - Lock file (DO NOT modify manually) |
| 109 | + |
| 110 | +### Source Structure (`src/`) |
| 111 | + |
| 112 | +- `rules/` - 30 ESLint rule implementations (TypeScript) |
| 113 | +- `configs/` - 7 preset configurations (angular, dom, marko, react, svelte, vue, index) |
| 114 | +- `create-testing-library-rule/` - Custom rule creator with helpers |
| 115 | +- `utils/` - Shared utilities for detecting Testing Library patterns |
| 116 | +- `node-utils/` - AST node utilities |
| 117 | +- `index.ts` - Main plugin export |
| 118 | + |
| 119 | +### Test Structure (`tests/`) |
| 120 | + |
| 121 | +- `tests/rules/` - Rule tests (one file per rule) |
| 122 | +- `tests/utils/` - Utility tests |
| 123 | +- `tests/create-testing-library-rule.test.ts` - Rule creator tests |
| 124 | +- `tests/fake-rule.ts` - Test fixture for rule creator |
| 125 | + |
| 126 | +### Documentation (`docs/`) |
| 127 | + |
| 128 | +- `docs/rules/` - Markdown documentation for each rule |
| 129 | +- `docs/migration-guides/` - Version migration guides |
| 130 | + |
| 131 | +## CI/CD Pipeline |
| 132 | + |
| 133 | +### GitHub Actions Workflows |
| 134 | + |
| 135 | +**Main CI** (`.github/workflows/ci.yml`): |
| 136 | + |
| 137 | +- Runs on: pull requests, merge groups |
| 138 | +- Calls: `verifications.yml` workflow |
| 139 | + |
| 140 | +**Verifications** (`.github/workflows/verifications.yml`): |
| 141 | + |
| 142 | +- **Code Validation Matrix**: lint, type-check, format:check, validate-gen-all, bundle-check |
| 143 | +- **Test Matrix**: Node.js (18, 20, 21, 22, 23) × ESLint (8, 9) = 10 combinations |
| 144 | +- **Timeout**: 3 minutes per test job |
| 145 | +- **Coverage**: Uploads to Codecov with OIDC auth |
| 146 | + |
| 147 | +**All validation steps run in parallel** to speed up CI. |
| 148 | + |
| 149 | +## Git Hooks (via Husky) |
| 150 | + |
| 151 | +### Pre-commit Hook |
| 152 | + |
| 153 | +Runs `lint-staged` which: |
| 154 | + |
| 155 | +1. Auto-formats all files with Prettier |
| 156 | +2. Runs ESLint with `--fix` on changed `.js`, `.ts`, `.mjs`, `.mts` files |
| 157 | + |
| 158 | +### Commit-msg Hook |
| 159 | + |
| 160 | +Validates commit messages follow [Conventional Commits](https://www.conventionalcommits.org/): |
| 161 | + |
| 162 | +- Format: `type(scope): subject` |
| 163 | +- Types: feat, fix, docs, style, refactor, test, chore, etc. |
| 164 | +- Example: `feat(no-await-sync-queries): add new rule` |
| 165 | + |
| 166 | +**If commit fails**, fix the commit message format. |
| 167 | + |
| 168 | +## Common Workflows |
| 169 | + |
| 170 | +### Adding a New Rule |
| 171 | + |
| 172 | +1. Create three files: |
| 173 | + - `src/rules/rule-name.ts` - Rule implementation |
| 174 | + - `tests/rules/rule-name.test.ts` - Tests |
| 175 | + - `docs/rules/rule-name.md` - Documentation |
| 176 | + |
| 177 | +2. Use `createTestingLibraryRule` (NOT TSESLint's default creator): |
| 178 | + |
| 179 | + ```typescript |
| 180 | + import { createTestingLibraryRule } from '../create-testing-library-rule'; |
| 181 | + |
| 182 | + export default createTestingLibraryRule({ |
| 183 | + name: 'rule-name', |
| 184 | + meta: { |
| 185 | + /* ... */ |
| 186 | + }, |
| 187 | + defaultOptions: [], |
| 188 | + create(context, _, helpers) { |
| 189 | + // Use helpers.isQuery(), helpers.isAsyncUtil(), etc. |
| 190 | + }, |
| 191 | + }); |
| 192 | + ``` |
| 193 | + |
| 194 | +3. Register in `src/rules/index.ts` |
| 195 | + |
| 196 | +4. Generate documentation: `pnpm run generate:docs` |
| 197 | + |
| 198 | +5. Run tests: `pnpm run test` |
| 199 | + |
| 200 | +### Updating Existing Rules |
| 201 | + |
| 202 | +1. Modify rule implementation in `src/rules/` |
| 203 | +2. Add tests to `tests/rules/` |
| 204 | +3. Update documentation in `docs/rules/` if needed |
| 205 | +4. Run `pnpm run validate-gen-all` to ensure docs are synced |
| 206 | +5. Run `pnpm run test` to verify |
| 207 | + |
| 208 | +### Before Committing |
| 209 | + |
| 210 | +**ALWAYS run these commands**: |
| 211 | + |
| 212 | +```bash |
| 213 | +pnpm run lint:fix # Fix linting issues |
| 214 | +pnpm run type-check # Verify types |
| 215 | +pnpm run test # Run tests |
| 216 | +pnpm run validate-gen-all # Validate generated files |
| 217 | +``` |
| 218 | + |
| 219 | +Git hooks will catch formatting issues, but manual validation is faster. |
| 220 | + |
| 221 | +## Important Notes |
| 222 | + |
| 223 | +### Package Manager |
| 224 | + |
| 225 | +- **ONLY use pnpm**, never npm or yarn |
| 226 | +- Commands: `pnpm install`, `pnpm run <script>`, `pnpm add <package>` |
| 227 | + |
| 228 | +### Generated Files |
| 229 | + |
| 230 | +**DO NOT manually edit** these files: |
| 231 | + |
| 232 | +- `src/configs/*.ts` (except index.ts) - Auto-generated from `tools/generate-configs` |
| 233 | +- Supported rules table in `README.md` - Auto-generated by `eslint-doc-generator` |
| 234 | +- `dist/` directory - Build output |
| 235 | + |
| 236 | +To update generated files: |
| 237 | + |
| 238 | +```bash |
| 239 | +pnpm run generate:configs # Regenerate config files |
| 240 | +pnpm run generate:docs # Regenerate README table |
| 241 | +``` |
| 242 | + |
| 243 | +### TypeScript |
| 244 | + |
| 245 | +- **Strict mode enabled**: All type errors must be resolved |
| 246 | +- **Target**: ES2023 |
| 247 | +- **Module**: preserve (implies Bundler resolution) |
| 248 | +- **noEmit**: true (tsdown handles builds) |
| 249 | + |
| 250 | +### Testing Best Practices |
| 251 | + |
| 252 | +- Write realistic test scenarios with actual Testing Library imports |
| 253 | +- Use `only: true` for focused testing during development (remove before committing) |
| 254 | +- Check `line`, `column`, `messageId`, and `data` in invalid test cases |
| 255 | +- Test with and without Shared Settings where applicable |
| 256 | + |
| 257 | +### Build Artifacts to Ignore |
| 258 | + |
| 259 | +The `.gitignore` already handles these, but be aware: |
| 260 | + |
| 261 | +- `dist/` - Build output |
| 262 | +- `coverage/` - Test coverage reports |
| 263 | +- `test-report.junit.xml` - Test results |
| 264 | +- `.eslintcache` - ESLint cache |
| 265 | +- `node_modules/` - Dependencies |
| 266 | + |
| 267 | +## Troubleshooting |
| 268 | + |
| 269 | +### "pnpm: command not found" |
| 270 | + |
| 271 | +Enable pnpm via corepack: `corepack enable pnpm` |
| 272 | + |
| 273 | +### Build Failures |
| 274 | + |
| 275 | +1. Clean and rebuild: `rm -rf dist && pnpm run build` |
| 276 | +2. Reinstall dependencies: `rm -rf node_modules && pnpm install` |
| 277 | + |
| 278 | +### Test Failures |
| 279 | + |
| 280 | +- Run specific test: Add `only: true` to the test case |
| 281 | +- Check coverage: `pnpm run test:ci` |
| 282 | +- View in browser: `pnpm run test:ui` |
| 283 | + |
| 284 | +### Validation Failures |
| 285 | + |
| 286 | +If `validate-gen-all` fails: |
| 287 | + |
| 288 | +1. Run `pnpm run generate:configs` to regenerate configs |
| 289 | +2. Run `pnpm run generate:docs` to regenerate docs |
| 290 | +3. Commit the changes |
| 291 | + |
| 292 | +### Type Check Failures |
| 293 | + |
| 294 | +- Use `pnpm run type-check` to see all errors |
| 295 | +- Fix errors in source files (not `.d.ts` files) |
| 296 | +- Strict mode is enabled; null checks are required |
| 297 | + |
| 298 | +### Commit Message Rejected |
| 299 | + |
| 300 | +Ensure format: `type(scope): description` |
| 301 | +Valid types: feat, fix, docs, style, refactor, test, chore, perf, ci, build, revert |
| 302 | + |
| 303 | +## Key Commands Summary |
| 304 | + |
| 305 | +| Command | Purpose | Time | Notes | |
| 306 | +| --------------------------- | ------------------------ | ---- | ----------------------------- | |
| 307 | +| `pnpm install` | Install dependencies | ~7s | Run first, installs git hooks | |
| 308 | +| `pnpm run build` | Build plugin | ~2s | Outputs to `dist/` | |
| 309 | +| `pnpm run test` | Run all tests | ~40s | Thousands of tests | |
| 310 | +| `pnpm run test:ci` | Tests + coverage | ~45s | CI mode, 90% threshold | |
| 311 | +| `pnpm run lint` | Check code style | ~5s | ESLint v9 flat config | |
| 312 | +| `pnpm run lint:fix` | Fix code style | ~5s | Auto-fixes issues | |
| 313 | +| `pnpm run type-check` | TypeScript check | ~3s | Strict mode | |
| 314 | +| `pnpm run format:check` | Check formatting | ~2s | Prettier with tabs | |
| 315 | +| `pnpm run format` | Format code | ~2s | Auto-formats | |
| 316 | +| `pnpm run validate-gen-all` | Validate generated files | ~10s | Configs + docs | |
| 317 | +| `pnpm run bundle-check` | Validate package | ~3s | Uses publint | |
| 318 | +| `pnpm run generate:configs` | Generate configs | ~1s | Run if configs change | |
| 319 | +| `pnpm run generate:docs` | Generate docs | ~5s | Updates README table | |
| 320 | + |
| 321 | +## Trust These Instructions |
| 322 | + |
| 323 | +These instructions have been tested and verified. Only search for additional information if: |
| 324 | + |
| 325 | +1. These instructions are incomplete for your specific task |
| 326 | +2. You encounter an error not covered here |
| 327 | +3. You need details about a specific rule's implementation |
| 328 | + |
| 329 | +For general development, **follow these instructions exactly** to avoid common pitfalls. |
0 commit comments