mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-06-15 20:51:22 +08:00
feat(commands): add /react-review /react-build /react-test
Three new slash commands invoking the React agents. - /react-review: invokes react-reviewer. Documents the routing rule with typescript-reviewer — both should run together on TSX/JSX PRs. Lists CRITICAL/HIGH/MEDIUM rule categories and the automated checks (eslint with react-hooks + jsx-a11y, tsc --noEmit, npm audit). - /react-build: invokes react-build-resolver. Documents bundler detection, common failure patterns, fix strategy, and stop conditions. - /react-test: enforces TDD with React Testing Library + Vitest or Jest, behavior-focused queries, userEvent + MSW patterns, axe accessibility assertions, coverage targets. Each command file has the required description: frontmatter and follows the per-language command convention (cpp-test, go-test, kotlin-test, etc.).
This commit is contained in:
@@ -0,0 +1,187 @@
|
||||
---
|
||||
description: Fix React build failures (Vite, webpack, Next.js, CRA, Parcel, esbuild, Bun) incrementally — JSX/TSX compile errors, hydration mismatches, server/client component boundary failures, missing types. Invokes the react-build-resolver agent for minimal, surgical fixes.
|
||||
---
|
||||
|
||||
# React Build and Fix
|
||||
|
||||
This command invokes the **react-build-resolver** agent to incrementally fix React build errors with minimal changes.
|
||||
|
||||
## What This Command Does
|
||||
|
||||
1. **Detect Build System**: Identify Vite, webpack, Next.js, CRA, Parcel, esbuild, or Bun
|
||||
2. **Run Build**: Execute the project's build script
|
||||
3. **Parse Errors**: Group by layer (TypeScript / bundler config / runtime / hydration)
|
||||
4. **Fix Incrementally**: One error at a time, re-running build after each change
|
||||
5. **Report Summary**: Show what was fixed and what remains
|
||||
|
||||
## When to Use
|
||||
|
||||
Use `/react-build` when:
|
||||
|
||||
- `npm run build` (or pnpm/yarn/bun equivalent) fails
|
||||
- JSX/TSX compile errors after a TypeScript or React upgrade
|
||||
- Next.js hydration mismatch errors at runtime
|
||||
- Server/Client Component boundary errors in App Router
|
||||
- After installing or upgrading `react`, `react-dom`, `@types/react`, or a bundler
|
||||
- Missing types or "module not found" errors involving React
|
||||
|
||||
## Scope
|
||||
|
||||
This command owns **React build/bundler/runtime hydration** failures. For pure TypeScript type errors with no React involvement, use `/build-fix` (generic) instead.
|
||||
|
||||
## Diagnostic Commands Run
|
||||
|
||||
```bash
|
||||
# Project build script (preferred)
|
||||
npm run build --if-present
|
||||
pnpm build 2>/dev/null
|
||||
yarn build 2>/dev/null
|
||||
bun run build 2>/dev/null
|
||||
|
||||
# Standalone typecheck
|
||||
npm run typecheck --if-present
|
||||
tsc --noEmit -p tsconfig.json
|
||||
|
||||
# Bundler-specific fallback
|
||||
next build # Next.js
|
||||
vite build # Vite
|
||||
react-scripts build # CRA
|
||||
webpack --mode=production # webpack
|
||||
parcel build src/index.html # Parcel
|
||||
bun build ./src/index.tsx --outdir=dist
|
||||
```
|
||||
|
||||
## Example Session
|
||||
|
||||
```text
|
||||
User: /react-build
|
||||
|
||||
Agent:
|
||||
# React Build Resolution
|
||||
|
||||
## Build System Detected
|
||||
Vite (vite.config.ts present, @vitejs/plugin-react in deps)
|
||||
|
||||
## Initial Diagnostics
|
||||
|
||||
```bash
|
||||
$ npm run build
|
||||
> tsc -b && vite build
|
||||
|
||||
src/components/UserCard.tsx:1:8 - error TS6133: 'React' is declared but its value is never read.
|
||||
src/components/Modal.tsx:12:15 - error TS7016: Could not find a declaration file for module 'react-portal'.
|
||||
src/pages/Home.tsx:42:5 - error: 'useState' is not defined
|
||||
```
|
||||
|
||||
Errors found: 3
|
||||
|
||||
## Fix 1: Old JSX transform leftover
|
||||
|
||||
File: src/components/UserCard.tsx:1
|
||||
Cause: `tsconfig.json` already uses `"jsx": "react-jsx"`; the explicit `import React` is unused.
|
||||
|
||||
```tsx
|
||||
// Removed
|
||||
- import React from 'react';
|
||||
```
|
||||
|
||||
```bash
|
||||
$ npm run build
|
||||
# 2 errors remaining
|
||||
```
|
||||
|
||||
## Fix 2: Missing types
|
||||
|
||||
File: src/components/Modal.tsx
|
||||
Cause: `@types/react-portal` not installed.
|
||||
|
||||
```bash
|
||||
$ npm i -D @types/react-portal
|
||||
added 1 package
|
||||
```
|
||||
|
||||
```bash
|
||||
$ npm run build
|
||||
# 1 error remaining
|
||||
```
|
||||
|
||||
## Fix 3: Missing hook import
|
||||
|
||||
File: src/pages/Home.tsx
|
||||
Cause: `useState` referenced but not imported.
|
||||
|
||||
```tsx
|
||||
- import { useEffect } from "react";
|
||||
+ import { useEffect, useState } from "react";
|
||||
```
|
||||
|
||||
```bash
|
||||
$ npm run build
|
||||
# Build successful!
|
||||
```
|
||||
|
||||
## Final Verification
|
||||
|
||||
```bash
|
||||
$ npm run build
|
||||
✓ built in 2.34s
|
||||
|
||||
$ npm test
|
||||
✓ 47 tests passed
|
||||
```
|
||||
|
||||
## Summary
|
||||
|
||||
| Metric | Count |
|
||||
|--------|-------|
|
||||
| Build errors fixed | 3 |
|
||||
| Files modified | 2 |
|
||||
| Dependencies added | 1 (@types/react-portal) |
|
||||
| Remaining issues | 0 |
|
||||
|
||||
Build Status: PASS: SUCCESS
|
||||
```
|
||||
|
||||
## Common Errors Fixed
|
||||
|
||||
| Error | Typical Fix |
|
||||
|---|---|
|
||||
| `'React' is not defined` | Set `"jsx": "react-jsx"` in tsconfig (React 17+) |
|
||||
| Missing `@types/react` | `npm i -D @types/react @types/react-dom` |
|
||||
| `Unexpected token '<'` | Add `@vitejs/plugin-react` / `babel-loader` |
|
||||
| `You're importing a component that needs useState` (Next.js) | Add `"use client"` or move hook to a Client Component child |
|
||||
| `Module not found: Can't resolve 'fs'` (Next.js) | Remove `fs` import or move logic into Server Component / API route |
|
||||
| `Hydration failed because the initial UI does not match` | Move `Date.now()`/`Math.random()`/`window.*` to `useEffect` |
|
||||
| `Invalid hook call` | Multiple React copies — dedupe via `resolutions`/`overrides` |
|
||||
| `Element type is invalid` | Default vs named import mismatch |
|
||||
|
||||
## Fix Strategy
|
||||
|
||||
1. **Compile errors first** — code must build
|
||||
2. **Hydration errors second** — affects production correctness
|
||||
3. **Bundler config third** — restore plugin/loader correctness
|
||||
4. **One fix at a time** — verify each change
|
||||
5. **Minimal changes** — never `// @ts-ignore` without explanation
|
||||
6. **Re-run after each fix** — surface new errors immediately
|
||||
|
||||
## Stop Conditions
|
||||
|
||||
The agent will stop and report if:
|
||||
|
||||
- Same error persists after 3 attempts
|
||||
- Fix introduces more errors than it resolves
|
||||
- Requires architectural change beyond build resolution (e.g., redesigning the RSC boundary)
|
||||
- Bundler version no longer supports the installed React major
|
||||
|
||||
## Related Commands
|
||||
|
||||
- `/react-test` — run tests after the build is green
|
||||
- `/react-review` — review code quality after the build succeeds
|
||||
- `/build-fix` — generic build fixer (non-React)
|
||||
- `verification-loop` skill — full verification loop
|
||||
|
||||
## Related
|
||||
|
||||
- Agent: `agents/react-build-resolver.md`
|
||||
- Skills: `skills/react-patterns/`, `skills/frontend-patterns/`
|
||||
- Rules: `rules/react/coding-style.md`, `rules/react/patterns.md`
|
||||
@@ -0,0 +1,170 @@
|
||||
---
|
||||
description: Comprehensive React/JSX code review for hook correctness, render performance, server/client component boundaries, accessibility, and React-specific security. Invokes the react-reviewer agent (and typescript-reviewer alongside on TSX/JSX changes).
|
||||
---
|
||||
|
||||
# React Code Review
|
||||
|
||||
This command invokes the **react-reviewer** agent for React-specific code review. For pull requests touching `.tsx`/`.jsx` files, both `react-reviewer` and `typescript-reviewer` should run — each owns a distinct lane.
|
||||
|
||||
## What This Command Does
|
||||
|
||||
1. **Identify React Changes**: Find modified `.tsx`/`.jsx` files (and React-containing `.ts`/`.js` files) via `git diff`
|
||||
2. **Run Lint**: Execute `eslint` with `eslint-plugin-react-hooks` and `eslint-plugin-jsx-a11y`
|
||||
3. **Typecheck**: Run `tsc --noEmit` or the project's canonical typecheck command
|
||||
4. **Review React Lanes Only**: Hook rules, RSC boundaries, accessibility, render performance, React-specific security
|
||||
5. **Generate Report**: Categorize issues by severity (CRITICAL / HIGH / MEDIUM)
|
||||
|
||||
## When to Use
|
||||
|
||||
Use `/react-review` when:
|
||||
|
||||
- A PR or commit touches `.tsx`/`.jsx` files
|
||||
- After writing or modifying React components, custom hooks, or pages
|
||||
- Before merging React code
|
||||
- Auditing accessibility on UI components
|
||||
- Reviewing a new hook for rules-of-hooks and dependency correctness
|
||||
- Auditing a Next.js App Router server/client component boundary
|
||||
|
||||
For pure `.ts`/`.js` changes with no React imports, use `/code-review` (general) or invoke `typescript-reviewer` directly.
|
||||
|
||||
## Scope vs `/code-review` and TypeScript Review
|
||||
|
||||
| Tool | Scope |
|
||||
|---|---|
|
||||
| `react-reviewer` (this command) | Hooks rules, JSX, RSC, a11y, React-specific security, render perf |
|
||||
| `typescript-reviewer` | Generic TS/JS — `any` abuse, async correctness, Node security |
|
||||
| `security-reviewer` | Project-wide security audit |
|
||||
| `/code-review` | Generic uncommitted-changes or PR review |
|
||||
|
||||
On a TSX/JSX PR, invoke both `react-reviewer` and `typescript-reviewer`. Findings from each are non-overlapping by design.
|
||||
|
||||
## Review Categories
|
||||
|
||||
### CRITICAL (Must Fix)
|
||||
|
||||
- `dangerouslySetInnerHTML` with unsanitized input
|
||||
- `href`/`src` with unvalidated user URLs (`javascript:`, `data:`)
|
||||
- Server Action without input validation
|
||||
- Secret in client bundle (`NEXT_PUBLIC_*`, `VITE_*`, `REACT_APP_*`)
|
||||
- `localStorage`/`sessionStorage` for session tokens
|
||||
- Conditional hook calls (violates Rules of Hooks)
|
||||
- Direct state mutation
|
||||
- Hook called outside a component or custom hook
|
||||
|
||||
### HIGH (Should Fix)
|
||||
|
||||
- Missing `useEffect`/`useMemo`/`useCallback` deps (disabled `exhaustive-deps` without justification)
|
||||
- Effect for derived state
|
||||
- Effect missing cleanup
|
||||
- Stale closures in handlers/intervals
|
||||
- Server-only imports in Client Components
|
||||
- Sensitive data leaked via props to Client Components
|
||||
- Server Actions without auth checks
|
||||
- Accessibility violations (missing labels, non-semantic interactive elements, ARIA misuse)
|
||||
- `key={index}` in dynamic lists
|
||||
- Duplicated state, useEffect chains
|
||||
|
||||
### MEDIUM (Consider)
|
||||
|
||||
- Over-memoization without measured win
|
||||
- Inline new object/function as prop to memoized child
|
||||
- Suspense at route root only (no progressive reveal)
|
||||
- Long lists without virtualization
|
||||
- High-frequency value via `useContext`
|
||||
- Roll-your-own validation in non-trivial forms
|
||||
- Prop drilling beyond 3 levels
|
||||
- Component over 200 lines
|
||||
- Class components in new code
|
||||
|
||||
## Automated Checks Run
|
||||
|
||||
```bash
|
||||
# Lint (required for any meaningful review)
|
||||
npx eslint . --ext .tsx,.jsx,.ts,.js
|
||||
|
||||
# Typecheck (skip cleanly for JS-only projects)
|
||||
npm run typecheck --if-present
|
||||
tsc --noEmit -p tsconfig.json
|
||||
|
||||
# Targeted a11y rules
|
||||
npx eslint . --rule 'jsx-a11y/alt-text: error' \
|
||||
--rule 'jsx-a11y/anchor-is-valid: error' \
|
||||
--rule 'jsx-a11y/click-events-have-key-events: error'
|
||||
|
||||
# Supply-chain
|
||||
npm audit
|
||||
```
|
||||
|
||||
If `eslint-plugin-react-hooks` or `eslint-plugin-jsx-a11y` is not configured, the review will flag the gap as a HIGH config issue and continue.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```text
|
||||
User: /react-review
|
||||
|
||||
Agent:
|
||||
# React Code Review Report
|
||||
|
||||
## Files Reviewed
|
||||
- src/components/UserCard.tsx (modified)
|
||||
- src/hooks/useUser.ts (new)
|
||||
|
||||
## Lint Results
|
||||
PASS: eslint clean
|
||||
PASS: typecheck clean
|
||||
|
||||
## Issues Found
|
||||
|
||||
[CRITICAL] Unsanitized dangerouslySetInnerHTML
|
||||
File: src/components/UserCard.tsx:42
|
||||
Issue: User-controlled bio rendered as raw HTML.
|
||||
Why: XSS via stored script tags in user input.
|
||||
Fix: Sanitize with DOMPurify or render as text:
|
||||
```tsx
|
||||
import DOMPurify from "isomorphic-dompurify";
|
||||
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(user.bio) }} />
|
||||
```
|
||||
|
||||
[HIGH] Effect cleanup missing
|
||||
File: src/hooks/useUser.ts:18
|
||||
Issue: `fetch` call without AbortController; setState on unmounted component possible.
|
||||
Fix: Add AbortController and cleanup:
|
||||
```ts
|
||||
useEffect(() => {
|
||||
const ac = new AbortController();
|
||||
fetch(`/api/users/${id}`, { signal: ac.signal })
|
||||
.then(r => r.json())
|
||||
.then(setUser);
|
||||
return () => ac.abort();
|
||||
}, [id]);
|
||||
```
|
||||
|
||||
## Summary
|
||||
- CRITICAL: 1
|
||||
- HIGH: 1
|
||||
- MEDIUM: 0
|
||||
|
||||
Recommendation: FAIL: Block merge until CRITICAL issue is fixed
|
||||
```
|
||||
|
||||
## Approval Criteria
|
||||
|
||||
| Status | Condition |
|
||||
|---|---|
|
||||
| PASS: Approve | No CRITICAL or HIGH issues |
|
||||
| WARNING: Warning | Only MEDIUM issues (merge with caution) |
|
||||
| FAIL: Block | CRITICAL or HIGH issues found |
|
||||
|
||||
## Integration with Other Commands
|
||||
|
||||
- Run `/react-build` first if the build is broken
|
||||
- Run `/react-test` to ensure component tests pass
|
||||
- Run `/react-review` before merging
|
||||
- Use `/code-review` for non-React-specific concerns on the same PR
|
||||
|
||||
## Related
|
||||
|
||||
- Agent: `agents/react-reviewer.md`
|
||||
- Companion agent: `agents/typescript-reviewer.md` (run alongside for TSX/JSX PRs)
|
||||
- Skills: `skills/react-patterns/`, `skills/react-testing/`, `skills/accessibility/`
|
||||
- Rules: `rules/react/`
|
||||
@@ -0,0 +1,265 @@
|
||||
---
|
||||
description: Enforce TDD workflow for React. Write React Testing Library tests first (behavior-focused, accessibility-first), then implement components. Detects Vitest or Jest and verifies coverage targets.
|
||||
---
|
||||
|
||||
# React TDD Command
|
||||
|
||||
This command enforces test-driven development for React using React Testing Library plus Vitest or Jest, detected at runtime.
|
||||
|
||||
## What This Command Does
|
||||
|
||||
1. **Define Component Signature**: Scaffold the component, prop type, and exports
|
||||
2. **Write Behavior Tests First**: RTL queries (role-first), `userEvent`, MSW for network — RED
|
||||
3. **Run Tests**: Verify they fail for the right reason
|
||||
4. **Implement Minimal Code**: Just enough to pass — GREEN
|
||||
5. **Refactor**: Improve while keeping tests green
|
||||
6. **Check Coverage**: Hit the targets in [rules/react/testing.md](../rules/react/testing.md)
|
||||
|
||||
## When to Use
|
||||
|
||||
Use `/react-test` when:
|
||||
|
||||
- Implementing a new React component or custom hook
|
||||
- Adding test coverage to an untested component
|
||||
- Fixing a bug (write failing test first that reproduces it)
|
||||
- Building forms, state machines, or accessibility-critical UI
|
||||
- Onboarding to RTL + Vitest/Jest workflow
|
||||
|
||||
## TDD Cycle
|
||||
|
||||
```
|
||||
RED -> Write failing test for the next behavior
|
||||
GREEN -> Implement minimal component code to pass
|
||||
REFACTOR -> Improve component, tests stay green
|
||||
REPEAT -> Next behavior
|
||||
```
|
||||
|
||||
## Runner Detection
|
||||
|
||||
```bash
|
||||
test -f vitest.config.ts -o -f vitest.config.js -o -f vite.config.ts # Vitest
|
||||
grep -l '"jest"' package.json # Jest
|
||||
```
|
||||
|
||||
Prefer Vitest for new Vite-based projects; respect Jest for existing setups.
|
||||
|
||||
## Example Session
|
||||
|
||||
```text
|
||||
User: /react-test I need a SearchInput component with debounced search
|
||||
|
||||
Agent:
|
||||
# TDD Session: SearchInput
|
||||
|
||||
## Step 1: Define Signature
|
||||
|
||||
```tsx
|
||||
// src/components/SearchInput.tsx
|
||||
type Props = {
|
||||
onSearch: (query: string) => void;
|
||||
placeholder?: string;
|
||||
debounceMs?: number;
|
||||
};
|
||||
|
||||
export function SearchInput({ onSearch, placeholder, debounceMs }: Props) {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
```
|
||||
|
||||
## Step 2: Write Behavior Tests (RED)
|
||||
|
||||
```tsx
|
||||
// src/components/SearchInput.test.tsx
|
||||
import { describe, expect, test, vi } from "vitest";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { SearchInput } from "./SearchInput";
|
||||
|
||||
describe("SearchInput", () => {
|
||||
test("renders with placeholder", () => {
|
||||
render(<SearchInput onSearch={() => {}} placeholder="Search users" />);
|
||||
expect(screen.getByPlaceholderText("Search users")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test("calls onSearch after typing", async () => {
|
||||
vi.useFakeTimers();
|
||||
const user = userEvent.setup({ advanceTimers: vi.advanceTimersByTime });
|
||||
const onSearch = vi.fn();
|
||||
render(<SearchInput onSearch={onSearch} debounceMs={300} />);
|
||||
|
||||
await user.type(screen.getByRole("textbox"), "alice");
|
||||
|
||||
expect(onSearch).not.toHaveBeenCalled(); // before debounce
|
||||
vi.advanceTimersByTime(300);
|
||||
expect(onSearch).toHaveBeenCalledWith("alice"); // after debounce
|
||||
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
test("does not call onSearch when typing pauses then continues", async () => {
|
||||
vi.useFakeTimers();
|
||||
const user = userEvent.setup({ advanceTimers: vi.advanceTimersByTime });
|
||||
const onSearch = vi.fn();
|
||||
render(<SearchInput onSearch={onSearch} debounceMs={300} />);
|
||||
|
||||
await user.type(screen.getByRole("textbox"), "ali");
|
||||
vi.advanceTimersByTime(200); // mid-debounce
|
||||
await user.type(screen.getByRole("textbox"), "ce");
|
||||
vi.advanceTimersByTime(300);
|
||||
|
||||
expect(onSearch).toHaveBeenCalledTimes(1);
|
||||
expect(onSearch).toHaveBeenCalledWith("alice");
|
||||
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
test("is keyboard reachable and accessible", () => {
|
||||
render(<SearchInput onSearch={() => {}} />);
|
||||
const input = screen.getByRole("textbox");
|
||||
input.focus();
|
||||
expect(input).toHaveFocus();
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Step 3: Run Tests — Verify FAIL
|
||||
|
||||
```bash
|
||||
$ vitest run src/components/SearchInput.test.tsx
|
||||
|
||||
× src/components/SearchInput.test.tsx (4 tests) ✘ Error: not implemented
|
||||
```
|
||||
|
||||
✓ Tests fail as expected.
|
||||
|
||||
## Step 4: Implement Minimal Code (GREEN)
|
||||
|
||||
```tsx
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
export function SearchInput({ onSearch, placeholder, debounceMs = 300 }: Props) {
|
||||
const [query, setQuery] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
const id = setTimeout(() => onSearch(query), debounceMs);
|
||||
return () => clearTimeout(id);
|
||||
}, [query, onSearch, debounceMs]);
|
||||
|
||||
return (
|
||||
<input
|
||||
type="text"
|
||||
value={query}
|
||||
placeholder={placeholder}
|
||||
onChange={(e) => setQuery(e.target.value)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Step 5: Run Tests — Verify PASS
|
||||
|
||||
```bash
|
||||
$ vitest run src/components/SearchInput.test.tsx
|
||||
|
||||
✓ src/components/SearchInput.test.tsx (4 tests) 47ms
|
||||
```
|
||||
|
||||
## Step 6: Coverage
|
||||
|
||||
```bash
|
||||
$ vitest run --coverage src/components/SearchInput.test.tsx
|
||||
|
||||
% Stmts: 100 % Branch: 100 % Funcs: 100 % Lines: 100
|
||||
```
|
||||
|
||||
## TDD Complete!
|
||||
```
|
||||
|
||||
## Test Patterns
|
||||
|
||||
### Behavior, not implementation
|
||||
|
||||
Use `getByRole`, `getByLabelText`, `getByText`. Avoid `container.querySelector` and asserting on component state.
|
||||
|
||||
### `userEvent.setup()` per test
|
||||
|
||||
```tsx
|
||||
const user = userEvent.setup();
|
||||
await user.click(screen.getByRole("button", { name: /save/i }));
|
||||
```
|
||||
|
||||
### MSW for network
|
||||
|
||||
```tsx
|
||||
beforeAll(() => server.listen({ onUnhandledRequest: "error" }));
|
||||
afterEach(() => server.resetHandlers());
|
||||
afterAll(() => server.close());
|
||||
|
||||
server.use(http.post("/api/users", () => HttpResponse.json({ id: "1" }, { status: 201 })));
|
||||
```
|
||||
|
||||
### Custom hooks
|
||||
|
||||
```tsx
|
||||
const { result } = renderHook(() => useCounter(0));
|
||||
act(() => result.current.increment());
|
||||
expect(result.current.count).toBe(1);
|
||||
```
|
||||
|
||||
### Accessibility
|
||||
|
||||
```tsx
|
||||
import { axe } from "vitest-axe";
|
||||
expect(await axe(container)).toHaveNoViolations();
|
||||
```
|
||||
|
||||
## Coverage Targets
|
||||
|
||||
| Layer | Target |
|
||||
|---|---|
|
||||
| Pure utilities | >=90% |
|
||||
| Custom hooks | >=85% |
|
||||
| Presentational components | >=80% |
|
||||
| Container components | >=70% |
|
||||
| Pages | E2E covered separately |
|
||||
|
||||
Configure in `vitest.config.ts` / `jest.config.js` to enforce thresholds in CI.
|
||||
|
||||
## Anti-Patterns to Avoid
|
||||
|
||||
- `container.querySelector(...)` — bypasses accessibility queries
|
||||
- Asserting on render count
|
||||
- Mocking `react` itself (`jest.mock("react", ...)`)
|
||||
- Mocking child components by default (mock only when child has heavy side effects)
|
||||
- Ignoring `act()` warnings — they signal real bugs
|
||||
- Snapshot tests of rendered components (brittle, rubber-stamped) — use Playwright/Cypress visual diff instead
|
||||
|
||||
## Test Commands
|
||||
|
||||
```bash
|
||||
# Vitest
|
||||
vitest # watch
|
||||
vitest run # one-shot
|
||||
vitest run --coverage # with coverage
|
||||
vitest run path/to/file.test.tsx # single file
|
||||
|
||||
# Jest
|
||||
jest --watch
|
||||
jest --coverage
|
||||
jest path/to/file.test.tsx
|
||||
|
||||
# CI mode
|
||||
CI=true vitest run --coverage
|
||||
```
|
||||
|
||||
## Related Commands
|
||||
|
||||
- `/react-build` — fix build errors before running tests
|
||||
- `/react-review` — review after implementation
|
||||
- `verification-loop` skill — full verification loop
|
||||
|
||||
## Related
|
||||
|
||||
- Skills: `skills/react-testing/`, `skills/tdd-workflow/`, `skills/accessibility/`, `skills/e2e-testing/`
|
||||
- Rules: `rules/react/testing.md`
|
||||
- Agents: `react-reviewer` (reviews test quality), `tdd-guide` (enforces TDD process)
|
||||
Reference in New Issue
Block a user