This document provides comprehensive information about testing in the Warranty Wallet application.
The Warranty Wallet frontend uses a modern testing stack to ensure code quality and reliability:
- Jest - JavaScript testing framework
- React Testing Library - Simple and complete testing utilities for React components
- Jest DOM - Custom Jest matchers for asserting on DOM elements
Tests are co-located with their components following React best practices:
src/
├── Pages/
│ └── Dashboard/
│ ├── Dashboard.js
│ └── Dashboard.test.jsx
├── Components/
│ └── Filters/
│ ├── Filters.js
│ └── Filters.test.jsx
└── setupTests.js # Global test configuration
- Test files:
ComponentName.test.jsx - Test suites:
describe('ComponentName', () => {...}) - Test cases:
test('should do something', () => {...})
# Run all tests in watch mode
npm test
# Run tests once (CI/CD mode)
npm test -- --watchAll=false
# Run with coverage report
npm test -- --coverage --watchAll=false
# Run specific test file
npm test Dashboard.test.jsx
# Run tests matching pattern
npm test -- --testNamePattern="renders"# Run tests with verbose output
npm test -- --verbose
# Update snapshots (if using snapshot testing)
npm test -- --updateSnapshot
# Run tests in specific directory
npm test -- --testPathPattern="Pages"The test suite currently covers:
Dashboard Component (Dashboard.test.jsx)
- ✅ Loading spinner display
- ✅ API data fetching and rendering
- ✅ Warranty card rendering
- ✅ Empty state handling
- ✅ Search/filter functionality
- ✅ API error handling
Filters Component (Filters.test.jsx)
- ✅ Input field rendering
- ✅ Icon display
- ✅ Search input functionality
- ✅ Category button rendering
- ✅ User interaction handling
Generate detailed coverage reports:
npm test -- --coverage --watchAll=falseCoverage reports will be generated in the coverage/ directory and include:
- Line coverage
- Function coverage
- Branch coverage
- Statement coverage
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import '@testing-library/jest-dom';
import YourComponent from './YourComponent';
describe('YourComponent', () => {
beforeEach(() => {
// Setup before each test
jest.clearAllMocks();
});
test('should render component correctly', () => {
render(<YourComponent />);
expect(screen.getByText('Expected Text')).toBeInTheDocument();
});
test('should handle user interaction', async () => {
render(<YourComponent />);
const button = screen.getByRole('button', { name: /click me/i });
fireEvent.click(button);
await waitFor(() => {
expect(screen.getByText('Result')).toBeInTheDocument();
});
});
});jest.mock('../../Config/Axios/Axios', () => ({
Axios: {
get: jest.fn(),
post: jest.fn(),
},
}));const mockUser = { userId: 101, name: 'Test User' };
render(
<UserContext.Provider value={{ user: mockUser }}>
<YourComponent />
</UserContext.Provider>
);jest.mock('antd', () => ({
message: {
useMessage: () => [jest.fn(), <div key="toast" />],
},
Spin: () => <div data-testid="loading">Loading...</div>,
}));test('renders component with props', () => {
const props = { title: 'Test Title', items: [] };
render(<Component {...props} />);
expect(screen.getByText('Test Title')).toBeInTheDocument();
});test('handles button click', () => {
const mockHandler = jest.fn();
render(<Button onClick={mockHandler} />);
fireEvent.click(screen.getByRole('button'));
expect(mockHandler).toHaveBeenCalledTimes(1);
});test('loads data from API', async () => {
const mockData = [{ id: 1, name: 'Item 1' }];
Axios.get.mockResolvedValueOnce({ data: mockData });
render(<DataComponent />);
await waitFor(() => {
expect(screen.getByText('Item 1')).toBeInTheDocument();
});
});test('handles API error gracefully', async () => {
Axios.get.mockRejectedValueOnce(new Error('Network Error'));
render(<DataComponent />);
await waitFor(() => {
expect(screen.getByText(/error/i)).toBeInTheDocument();
});
});-
Test behavior, not implementation
// ✅ Good - tests behavior expect(screen.getByText('Welcome')).toBeInTheDocument(); // ❌ Avoid - tests implementation expect(component.state.showWelcome).toBe(true);
-
Use accessible queries
// ✅ Preferred order screen.getByRole('button', { name: /submit/i }) screen.getByLabelText(/email/i) screen.getByPlaceholderText(/enter email/i) screen.getByText(/welcome/i) // ❌ Less preferred screen.getByTestId('submit-button')
-
Mock external dependencies
- Always mock API calls
- Mock third-party libraries
- Mock complex React contexts
-
Clean up after tests
beforeEach(() => { jest.clearAllMocks(); });
- Don't test implementation details
- Don't test third-party library code
- Don't write tests that duplicate functionality
- Don't forget to handle async operations properly
// Solution: Use waitFor for async operations
await waitFor(() => {
expect(screen.getByText('Dynamic Content')).toBeInTheDocument();
});// Ensure mocks are placed before imports
jest.mock('./module', () => ({...}));
import Component from './Component';// Increase timeout for slow operations
test('slow operation', async () => {
// test code
}, 10000); // 10 second timeout// Print current DOM structure
screen.debug();
// Print specific element
screen.debug(screen.getByRole('button'));
// Use logRoles to see available roles
import { logRoles } from '@testing-library/dom';
logRoles(container);name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: '16'
- run: cd frontend && npm ci
- run: cd frontend && npm test -- --coverage --watchAll=false- React Testing Library Documentation
- Jest Documentation
- Common Testing Patterns
- Testing Best Practices
When contributing new features:
- Write tests for new components
- Update existing tests when modifying components
- Ensure all tests pass before submitting PR
- Maintain test coverage above 80%
- Follow existing testing patterns and conventions
For questions about testing, please check the existing test files or reach out to the development team.