.agents/skills/frontend-testing/SKILL.md
Activate this skill when:
| Technology | Version | Purpose |
|---|---|---|
| Jest | 30.x | Test runner and assertion framework |
| ts-jest | 29.x | TypeScript transform for Jest |
| React Testing Library | 16.x | Component rendering and DOM queries |
| @testing-library/user-event | 14.x | Realistic user interaction simulation |
| @testing-library/jest-dom | 6.x | Extended DOM matchers |
| jsdom | (via jest-environment-jsdom 30.x) | Browser environment simulation |
| React | 19.x | UI framework |
| TypeScript | 5.4 | Type safety |
| Zustand | 4.x | State management |
| React Router DOM | 6.x | Client-side routing |
| @tanstack/react-query | 5.x | Server state management |
| Axios | 1.x | HTTP client |
src/frontend/jest.config.jssrc/frontend/jest.setup.js (globals/mocks) and src/frontend/src/setupTests.ts (DOM matchers, ResizeObserver, IntersectionObserver, matchMedia)@/ maps to <rootDir>/src/src/**/__tests__/**/*.{test,spec}.{ts,tsx} and src/**/*.{test,spec}.{ts,tsx}transform-import-meta.js handles import.meta for Jest compatibilityjest.setup.js): @radix-ui/react-form, react-markdown, lucide-react/dynamicIconImports, @/components/common/genericIconComponent, @/icons/BotMessageSquare, @/stores/darkStore, localStorage, sessionStorage, crypto# Run all tests
npm test
# Run a specific test file
npm test -- path/to/file.test.tsx
# Run tests matching a pattern
npm test -- --testPathPattern="alertStore"
# Run tests in watch mode
npm run test:watch
# Run tests with coverage
npm run test:coverage
# Run a single test file with coverage
npm test -- --coverage --collectCoverageFrom='src/path/to/source.ts' path/to/__tests__/source.test.ts
Test files follow one of two patterns:
Dedicated __tests__ directory (preferred for components and modules):
src/components/core/my-component/
├── my-component.tsx
└── __tests__/
└── my-component.test.tsx
Co-located test file (acceptable for utilities and simple modules):
src/utils/
├── myUtil.ts
└── myUtil.test.ts
Naming convention: ComponentName.test.tsx for components, hook-name.test.ts for hooks, util-name.test.ts for utilities.
Do NOT use .spec.tsx -- while technically matched, the project convention is .test.tsx.
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import MyComponent from "../MyComponent";
// Mock dependencies (use jest.mock, NOT vi.mock)
jest.mock("@/controllers/API/api", () => ({
get: jest.fn(),
post: jest.fn(),
}));
describe("MyComponent", () => {
beforeEach(() => {
jest.clearAllMocks();
});
describe("rendering", () => {
it("should render the component with default props", () => {
// Arrange
render(<MyComponent />);
// Act - (none for render test)
// Assert
expect(screen.getByRole("button", { name: /submit/i })).toBeInTheDocument();
});
});
describe("user interactions", () => {
it("should call onSubmit when the form is submitted", async () => {
// Arrange
const user = userEvent.setup();
const onSubmit = jest.fn();
render(<MyComponent onSubmit={onSubmit} />);
// Act
await user.click(screen.getByRole("button", { name: /submit/i }));
// Assert
expect(onSubmit).toHaveBeenCalledTimes(1);
});
});
});
When testing a directory with multiple files, follow this order:
Within a single test file, order test cases from simplest to most complex:
Every test should have a clear three-phase structure. Use blank lines to separate each phase for readability.
Test the component from the user's perspective. Query by role, label, text, or data-testid -- never by CSS class, internal state variable, or implementation detail.
Each it() block should verify exactly one behavior. If you need to write "and" in the test name, split it into two tests.
Use descriptive names that explain the expected behavior:
"should disable the submit button when the form is invalid""button test" or "test 1"Format: "should [expected behavior] when [condition]"
For every component, cover at minimum:
Happy path tests alone are NOT enough. They only confirm the code works when everything is perfect. Real bugs hide in the cracks. You MUST write tests that actively TRY TO BREAK the code:
Unexpected inputs:
null, undefined, "", [], {}, 0, -1, NaN, InfinityBoundary values:
Malformed data:
{ data: null } instead of { data: [] }Error states:
What should NOT happen:
Rapid/concurrent actions:
Write tests based on REQUIREMENTS, not on what the source code does. This is how you catch bugs where the code diverges from expected behavior.
When a test fails: first ask if the CODE is wrong, not the test. Do NOT silently change a failing assertion to match the current code without understanding WHY.
Per source file:
Run coverage for a specific file:
npm test -- --coverage --collectCoverageFrom='src/path/to/file.ts' src/path/to/__tests__/file.test.ts
jest.fn(), jest.mock(), jest.spyOn(), jest.mocked() -- never vi.* equivalents.@/components/ui/ -- render them as-is.jest.setup.js before mocking: Many modules are already globally mocked (darkStore, genericIconComponent, react-markdown, radix-form, etc.). Do not re-mock them.@testing-library/user-event over fireEvent for user interactions.act() when testing Zustand stores or React state changes.beforeEach(() => jest.clearAllMocks()) and afterEach for timers.| Pattern | Problem | How to Detect |
|---|---|---|
| The Liar | Test passes but doesn't verify the behavior it claims to test | Assertions don't match the test name |
| The Mirror | Test reads source code and asserts exactly what the code does — finds zero bugs | Test would never fail even if logic changes |
| The Giant | 50+ lines of setup, multiple acts, dozens of assertions | Should be 5+ separate tests |
| The Mockery | So many mocks that the test only tests the mock setup | Count mocks — if > 3 deep, rethink |
| The Inspector | Coupled to implementation details, breaks on any refactor | Tests internal state instead of behavior |
| The Chain Gang | Tests depend on execution order or share mutable state | Tests fail when run in isolation |
| The Flaky | Sometimes passes, sometimes fails with no code changes | Non-deterministic assertions or timing issues |