This document provides information about the testing setup and how to run tests in this project.
- Test Runner: Jest 30.x
- Testing Library: @testing-library/react 16.x
- Test Environment: jsdom (simulates browser environment)
- Coverage Provider: V8
- CI/CD: GitHub Actions
pnpm testpnpm test:watchpnpm test:coveragepnpm test path/to/test-file.test.tsxpnpm test --testNamePattern="Button"Test files should be placed next to the files they test with the .test.ts or .test.tsx extension:
components/
ui/
button.tsx
button.test.tsx ← Test file
app/
page.tsx
page.test.tsx ← Test file
lib/
utils.ts
utils.test.ts ← Test file
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { Button } from './button';
describe('Button', () => {
it('renders a button with text', () => {
render(<Button>Click me</Button>);
expect(screen.getByRole('button', { name: /click me/i })).toBeInTheDocument();
});
it('handles click events', async () => {
const handleClick = jest.fn();
const user = userEvent.setup();
render(<Button onClick={handleClick}>Click me</Button>);
await user.click(screen.getByRole('button'));
expect(handleClick).toHaveBeenCalledTimes(1);
});
});import { cn } from "./utils";
describe("cn utility function", () => {
it("merges class names correctly", () => {
const result = cn("class1", "class2");
expect(result).toBe("class1 class2");
});
});After running pnpm test:coverage, coverage reports are generated in the coverage/ directory:
- HTML Report:
coverage/index.html- Open in browser for interactive coverage report - LCOV Report:
coverage/lcov.info- For CI/CD integration - JUnit XML:
coverage/junit.xml- For CI/CD test result reporting - Clover XML:
coverage/clover.xml- Alternative coverage format
Open the HTML coverage report in your browser:
# Windows
start coverage/index.html
# macOS
open coverage/index.html
# Linux
xdg-open coverage/index.htmlThe Jest configuration is in jest.config.ts and includes:
- Test Environment: jsdom for React component testing
- Setup File:
jest.setup.ts- Configures testing-library/jest-dom and mocks - Module Name Mapper: Handles path aliases (@/components, @/lib, etc.)
- Coverage Collection: Configured to collect from app/, components/, and lib/ directories
- Reporters: Default console reporter + JUnit XML reporter for CI
The following Next.js modules are automatically mocked in jest.setup.ts:
next/image- Mocked to render as standard<img>tagnext/navigation- Mocked router hooks (useRouter, usePathname, useSearchParams)
Tests run automatically on:
- Push to
mainordevelopbranches - Pull requests to
mainordevelopbranches
The CI pipeline:
- Installs dependencies
- Runs linting (
pnpm lint) - Runs tests with coverage (
pnpm test:coverage) - Uploads coverage to Codecov (if configured)
- Uploads test results and coverage as artifacts
- Builds the Next.js application (
pnpm build)
The workflow is defined in .github/workflows/ci.yml.
To enable Codecov integration:
- Sign up at codecov.io
- Add your repository
- Add
CODECOV_TOKENto your GitHub repository secrets - Coverage will be automatically uploaded on each CI run
❌ Bad:
expect(component.state.count).toBe(1);✅ Good:
expect(screen.getByText("Count: 1")).toBeInTheDocument();Prefer queries that reflect how users interact with your app:
getByRole- Best for most elementsgetByLabelText- Good for form fieldsgetByPlaceholderText- For inputs without labelsgetByText- For non-interactive elementsgetByTestId- Last resort
Use @testing-library/user-event instead of fireEvent:
❌ Bad:
fireEvent.click(button);✅ Good:
const user = userEvent.setup();
await user.click(button);Jest automatically cleans up after each test, but if you create side effects:
afterEach(() => {
// Clean up
jest.clearAllMocks();
});it('has accessible button', () => {
render(<Button>Click me</Button>);
const button = screen.getByRole('button', { name: /click me/i });
expect(button).toBeInTheDocument();
});- Use
test.only()to run a single test during development - Use
pnpm test:watchto run only changed tests
- Check that path aliases in
jest.config.tsmatchtsconfig.json - Ensure the module is properly mocked if it's a Next.js-specific module
- Verify the file is in the
collectCoverageFrompatterns injest.config.ts - Check that the file isn't in
coveragePathIgnorePatterns