TypeScript Component Testing Skill
Metadata (Tier 1)
Keywords: jest, vitest, testing-library, react, component test, render
File Patterns: *.test.tsx, *.spec.ts, *.test.js
Modes: testing_frontend
Instructions (Tier 2)
React Testing Library Philosophy
Core Principle: Test from user's perspective, not implementation
Query Priority
getByRole(accessible roles)getByLabelText(form elements)getByPlaceholderTextgetByTextgetByTestId(last resort)
Basic Test Pattern
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
test('submits login form', async () => {
const handleSubmit = jest.fn();
render(<LoginForm />);
await userEvent.type(screen.getByLabelText(/email/i), 'user@example.com');
await userEvent.type(screen.getByLabelText(/password/i), 'password123');
await userEvent.click(screen.getByRole('button', { name: /log in/i }));
expect(handleSubmit).toHaveBeenCalledWith({
email: 'user@example.com',
password: 'password123'
});
});
Async Testing
test('loads and displays data', async () => {
render(<UserProfile userId="123" />);
// Wait for data to load
expect(await screen.findByText('John Doe')).toBeInTheDocument();
});
// Or with waitFor
await waitFor(() => {
expect(screen.getByText('John Doe')).toBeInTheDocument();
});
User Interactions (userEvent)
// ✅ Preferred: userEvent (realistic)
await userEvent.click(button);
await userEvent.type(input, 'text');
await userEvent.selectOptions(select, ['option1']);
// ❌ Avoid: fireEvent (low-level)
fireEvent.click(button);
Anti-Patterns
❌ Testing implementation details (state, methods) ❌ Using container.querySelector ❌ Not waiting for async updates ❌ Manually wrapping in act()