7.1 KiB
7.1 KiB
Frontend Testing Guide
This guide explains how to test the React frontend components and pages.
Testing Setup
The frontend uses Vitest (recommended) or Jest for testing React components. To set up testing:
cd frontend
npm install --save-dev @testing-library/react @testing-library/jest-dom @testing-library/user-event vitest @vitest/ui jsdom
Add to package.json:
{
"scripts": {
"test": "vitest",
"test:ui": "vitest --ui",
"test:coverage": "vitest --coverage"
}
}
Testing Strategy
Component Testing
Test individual components in isolation:
// frontend/src/components/__tests__/StatusIndicator.test.tsx
import { render, screen } from '@testing-library/react';
import { describe, it, expect } from 'vitest';
import StatusIndicator from '../StatusIndicator';
describe('StatusIndicator', () => {
it('renders connected status correctly', () => {
render(<StatusIndicator status="connected" label="WebSocket" />);
expect(screen.getByText('WebSocket')).toBeInTheDocument();
});
it('shows correct color for error status', () => {
render(<StatusIndicator status="error" label="Error" />);
const chip = screen.getByText('Error');
expect(chip).toHaveClass('MuiChip-colorError');
});
});
Page Testing
Test complete page workflows:
// frontend/src/pages/__tests__/StrategiesPage.test.tsx
import { render, screen, waitFor } from '@testing-library/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { describe, it, expect, vi } from 'vitest';
import StrategiesPage from '../StrategiesPage';
import * as strategiesApi from '../../api/strategies';
vi.mock('../../api/strategies');
describe('StrategiesPage', () => {
const queryClient = new QueryClient({
defaultOptions: { queries: { retry: false } }
});
it('displays list of strategies', async () => {
const mockStrategies = [
{
id: 1,
name: 'Test Strategy',
strategy_type: 'rsi',
enabled: false,
paper_trading: true,
// ... other fields
}
];
vi.mocked(strategiesApi.strategiesApi.listStrategies).mockResolvedValue(mockStrategies);
render(
<QueryClientProvider client={queryClient}>
<StrategiesPage />
</QueryClientProvider>
);
await waitFor(() => {
expect(screen.getByText('Test Strategy')).toBeInTheDocument();
});
});
it('shows create strategy button', () => {
render(
<QueryClientProvider client={queryClient}>
<StrategiesPage />
</QueryClientProvider>
);
expect(screen.getByText('Create Strategy')).toBeInTheDocument();
});
});
API Integration Testing
Test API client functions:
// frontend/src/api/__tests__/strategies.test.ts
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { strategiesApi } from '../strategies';
import { apiClient } from '../client';
vi.mock('../client');
describe('strategiesApi', () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('lists strategies', async () => {
const mockStrategies = [{ id: 1, name: 'Test' }];
vi.mocked(apiClient.get).mockResolvedValue({ data: mockStrategies });
const result = await strategiesApi.listStrategies();
expect(result).toEqual(mockStrategies);
expect(apiClient.get).toHaveBeenCalledWith('/api/strategies/');
});
it('creates strategy', async () => {
const newStrategy = { name: 'New', strategy_type: 'rsi' };
const created = { id: 1, ...newStrategy };
vi.mocked(apiClient.post).mockResolvedValue({ data: created });
const result = await strategiesApi.createStrategy(newStrategy);
expect(result).toEqual(created);
});
});
Hook Testing
Test custom React hooks:
// frontend/src/hooks/__tests__/useWebSocket.test.ts
import { renderHook, waitFor } from '@testing-library/react';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { useWebSocket } from '../useWebSocket';
// Mock WebSocket
global.WebSocket = vi.fn(() => ({
addEventListener: vi.fn(),
removeEventListener: vi.fn(),
send: vi.fn(),
close: vi.fn(),
readyState: WebSocket.OPEN,
})) as any;
describe('useWebSocket', () => {
it('connects to WebSocket', async () => {
const { result } = renderHook(() => useWebSocket('ws://localhost:8000/ws/'));
await waitFor(() => {
expect(result.current.isConnected).toBe(true);
});
});
it('handles messages', async () => {
const { result } = renderHook(() => useWebSocket('ws://localhost:8000/ws/'));
// Simulate message
const ws = (global.WebSocket as any).mock.results[0].value;
const messageEvent = new MessageEvent('message', {
data: JSON.stringify({ type: 'order_update', order_id: 1 })
});
ws.onmessage(messageEvent);
await waitFor(() => {
expect(result.current.lastMessage).toBeTruthy();
});
});
});
Test Coverage
Components to Test
-
Pages:
StrategiesPage- CRUD operations, start/stopTradingPage- Order placement, position closingDashboardPage- AutoPilot controls, system healthPortfolioPage- Position management, chartsBacktestPage- Backtest execution, resultsSettingsPage- All settings tabs
-
Components:
StrategyDialog- Form validation, parameter configurationOrderForm- Order type handling, validationPositionCard- Position display, close functionalityStatusIndicator- Status displaySystemHealth- Health status aggregationDataFreshness- Timestamp calculationsOperationsPanel- Operation display
-
Hooks:
useWebSocket- Connection, messages, subscriptionsuseRealtimeData- Query invalidation
-
Contexts:
SnackbarContext- Notification displayWebSocketProvider- WebSocket management
Running Tests
# Run all tests
npm test
# Run tests in watch mode
npm test -- --watch
# Run tests with UI
npm run test:ui
# Generate coverage report
npm run test:coverage
Best Practices
- Mock External Dependencies: Mock API calls, WebSocket connections
- Test User Interactions: Use
@testing-library/user-eventfor clicks, typing - Test Error States: Verify error handling and display
- Test Loading States: Ensure loading indicators appear
- Test Accessibility: Use
@testing-library/jest-dommatchers - Keep Tests Fast: Mock expensive operations
- Test Edge Cases: Empty states, error conditions, boundary values
Example Test Suite Structure
frontend/src/
├── components/
│ ├── __tests__/
│ │ ├── StatusIndicator.test.tsx
│ │ ├── StrategyDialog.test.tsx
│ │ └── ...
│ └── ...
├── pages/
│ ├── __tests__/
│ │ ├── StrategiesPage.test.tsx
│ │ ├── TradingPage.test.tsx
│ │ └── ...
│ └── ...
├── hooks/
│ ├── __tests__/
│ │ ├── useWebSocket.test.ts
│ │ └── ...
│ └── ...
└── api/
├── __tests__/
│ ├── strategies.test.ts
│ └── ...
└── ...