Comprehensive frontend testing patterns including component tests (Jest/Vitest + RTL), visual regression (Playwright), accessibility (axe-core), and performance (Lighthouse) testing for React/Next.js applications. Use when building frontend tests, testing React components, implementing visual regression, running accessibility tests, performance testing, or when user mentions component testing, visual regression, a11y testing, React Testing Library, Jest, Vitest, Lighthouse, or frontend testing.
Inherits all available tools
Additional assets for this skill
This skill inherits all available tools. When active, it can use any tool Claude has access to.
examples/button-component-test.tsxexamples/form-accessibility.spec.tsexamples/homepage-performance.spec.tsexamples/login-page-visual.spec.tsscripts/generate-coverage-report.shscripts/init-frontend-tests.shscripts/run-accessibility-tests.shscripts/run-component-tests.shscripts/run-performance-tests.shscripts/run-visual-regression.shtemplates/accessibility.spec.tstemplates/component-test.spec.tsxtemplates/performance.spec.tstemplates/setup-tests.tstemplates/test-utils.tstemplates/visual-regression.spec.tsCRITICAL: The description field above controls when Claude auto-loads this skill.
Provides comprehensive frontend testing patterns for React/Next.js applications including:
All code examples and templates in this skill follow strict security rules:
CRITICAL: Reference @docs/security/SECURITY-RULES.md
.gitignore protection for secretsUse scripts/init-frontend-tests.sh to set up comprehensive frontend testing:
bash scripts/init-frontend-tests.sh [project-path]
This will:
Use scripts/run-component-tests.sh to execute component tests:
bash scripts/run-component-tests.sh [test-pattern] [options]
Options:
This will:
Use scripts/run-visual-regression.sh for visual testing:
bash scripts/run-visual-regression.sh [test-pattern] [update-snapshots]
This will:
Use scripts/run-accessibility-tests.sh for a11y testing:
bash scripts/run-accessibility-tests.sh [test-pattern]
This will:
Use scripts/run-performance-tests.sh for performance testing:
bash scripts/run-performance-tests.sh [url] [options]
This will:
Use scripts/generate-coverage-report.sh to aggregate coverage:
bash scripts/generate-coverage-report.sh [output-dir]
This will:
import { render, screen, userEvent } from '@testing-library/react';
import { Button } from './Button';
test('renders and handles click', async () => {
const handleClick = jest.fn();
render(<Button onClick={handleClick}>Click me</Button>);
const button = screen.getByRole('button', { name: /click me/i });
await userEvent.click(button);
expect(handleClick).toHaveBeenCalledTimes(1);
});
import { test, expect } from '@playwright/test';
test('homepage visual regression', async ({ page }) => {
await page.goto('http://localhost:3000');
await page.waitForLoadState('networkidle');
// Mask dynamic content
await page.locator('.timestamp').evaluate(el => el.style.visibility = 'hidden');
await expect(page).toHaveScreenshot('homepage.png');
});
import { test, expect } from '@playwright/test';
import { injectAxe, checkA11y } from 'axe-playwright';
test('form accessibility', async ({ page }) => {
await page.goto('http://localhost:3000/form');
await injectAxe(page);
await checkA11y(page, null, {
detailedReport: true,
detailedReportOptions: {
html: true
}
});
});
import { test } from '@playwright/test';
import { playAudit } from 'playwright-lighthouse';
test('homepage performance', async ({ page }) => {
await page.goto('http://localhost:3000');
await playAudit({
page,
thresholds: {
performance: 90,
accessibility: 90,
'best-practices': 90,
seo: 90,
'first-contentful-paint': 2000,
'largest-contentful-paint': 3000,
'cumulative-layout-shift': 0.1,
},
port: 9222,
});
});
tests/
├── unit/ # Component unit tests
│ ├── Button.spec.tsx
│ └── Form.spec.tsx
├── integration/ # Component integration tests
│ └── AuthFlow.spec.tsx
├── visual/ # Visual regression tests
│ ├── homepage.spec.ts
│ └── dashboard.spec.ts
├── a11y/ # Accessibility tests
│ ├── navigation.spec.ts
│ └── forms.spec.ts
├── performance/ # Performance tests
│ └── critical-pages.spec.ts
└── utils/ # Test utilities
├── test-utils.ts
├── mock-factories.ts
└── test-helpers.ts
- name: Run Frontend Tests
run: |
npm run test:component -- --coverage
npm run test:visual
npm run test:a11y
npm run test:performance
- name: Upload Coverage
uses: codecov/codecov-action@v3
with:
files: ./coverage/coverage-final.json
screen.debug() to print DOMscreen.logTestingPlaygroundURL() for selector help--watch for iterative debuggingtest.only to focus on single testtest('form submission', async () => {
render(<ContactForm />);
await userEvent.type(screen.getByLabelText(/name/i), 'John Doe');
await userEvent.type(screen.getByLabelText(/email/i), 'john@example.com');
await userEvent.click(screen.getByRole('button', { name: /submit/i }));
expect(await screen.findByText(/success/i)).toBeInTheDocument();
});
test('loads and displays data', async () => {
render(<UserProfile userId="123" />);
expect(screen.getByText(/loading/i)).toBeInTheDocument();
const userName = await screen.findByText(/john doe/i);
expect(userName).toBeInTheDocument();
});
test('displays error message', async () => {
server.use(
http.get('/api/user', () => {
return HttpResponse.json({ error: 'Not found' }, { status: 404 });
})
);
render(<UserProfile userId="999" />);
expect(await screen.findByText(/error/i)).toBeInTheDocument();
});
init-frontend-tests.sh to set up infrastructure