This document provides guidelines for testing components in this library.
We use Jest as our test runner and React Testing Library for rendering and interacting with React components.
To run all tests:
npm testTo run tests in watch mode:
npm run test:watchTo run tests with coverage:
npm test -- --coverageTests should follow the Arrange-Act-Assert pattern:
- Arrange - Set up the component with props
- Act - Render the component and interact with it
- Assert - Check that the component behaves as expected
import React from 'react';
import { render, screen } from '@testing-library/react';
import Button from './Button';
describe('Button Component', () => {
test('renders children correctly', () => {
// Arrange
const children = 'Click Me';
// Act
render(<Button>{children}</Button>);
// Assert
expect(screen.getByText(children)).toBeInTheDocument();
});
test('handles click events', () => {
// Arrange
const handleClick = jest.fn();
// Act
render(<Button onClick={handleClick}>Click Me</Button>);
const button = screen.getByRole('button');
button.click();
// Assert
expect(handleClick).toHaveBeenCalledTimes(1);
});
});test('applies correct CSS classes', () => {
render(<Button variant="primary">Test</Button>);
const button = screen.getByRole('button');
expect(button).toHaveClass('bg-blue-500');
expect(button).toHaveClass('text-white');
});test('renders with custom className', () => {
render(<Button className="custom-class">Test</Button>);
const button = screen.getByRole('button');
expect(button).toHaveClass('custom-class');
});test('has correct aria-label', () => {
render(<Button aria-label="Submit form">Submit</Button>);
const button = screen.getByRole('button');
expect(button).toHaveAttribute('aria-label', 'Submit form');
});test('updates when props change', () => {
const { rerender } = render(<Button disabled>Test</Button>);
let button = screen.getByRole('button');
expect(button).toBeDisabled();
rerender(<Button disabled={false}>Test</Button>);
button = screen.getByRole('button');
expect(button).not.toBeDisabled();
});For components that use external dependencies (like charts or icons), mock them:
// Mock chart.js
jest.mock('chart.js', () => ({
Chart: () => null,
ChartJS: {
register: jest.fn(),
},
// ... other mocks
}));test('handles async operations', async () => {
render(<AsyncComponent />);
expect(screen.getByText('Loading...')).toBeInTheDocument();
// Wait for async operation to complete
await screen.findByText('Data loaded');
expect(screen.queryByText('Loading...')).not.toBeInTheDocument();
});We aim for at least 80% test coverage for all components. Run npm test -- --coverage to see current coverage.
- Test behavior, not implementation - Focus on what the component does rather than how it does it
- Use semantic queries - Prefer
getByRole,getByLabelText, etc. overgetByTestId - Keep tests isolated - Each test should be able to run independently
- Name tests clearly - Use descriptive test names that explain what is being tested
- Test edge cases - Include tests for error states, empty states, and boundary conditions
- Mock external dependencies - Keep tests fast and reliable by mocking external services
- Test accessibility - Ensure components are accessible by testing ARIA attributes and keyboard navigation
toBeInTheDocument()- Check if an element existstoHaveClass()- Check CSS classestoBeDisabled()/not.toBeDisabled()- Check disabled statetoHaveAttribute()- Check HTML attributestoHaveBeenCalledTimes()- Check function call counttoHaveBeenCalledWith()- Check function arguments
Tests are automatically run on every pull request to ensure code quality and prevent regressions.