# 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: ```bash 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`: ```json { "scripts": { "test": "vitest", "test:ui": "vitest --ui", "test:coverage": "vitest --coverage" } } ``` ## Testing Strategy ### Component Testing Test individual components in isolation: ```typescript // 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(); expect(screen.getByText('WebSocket')).toBeInTheDocument(); }); it('shows correct color for error status', () => { render(); const chip = screen.getByText('Error'); expect(chip).toHaveClass('MuiChip-colorError'); }); }); ``` ### Page Testing Test complete page workflows: ```typescript // 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( ); await waitFor(() => { expect(screen.getByText('Test Strategy')).toBeInTheDocument(); }); }); it('shows create strategy button', () => { render( ); expect(screen.getByText('Create Strategy')).toBeInTheDocument(); }); }); ``` ### API Integration Testing Test API client functions: ```typescript // 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: ```typescript // 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 1. **Pages**: - `StrategiesPage` - CRUD operations, start/stop - `TradingPage` - Order placement, position closing - `DashboardPage` - AutoPilot controls, system health - `PortfolioPage` - Position management, charts - `BacktestPage` - Backtest execution, results - `SettingsPage` - All settings tabs 2. **Components**: - `StrategyDialog` - Form validation, parameter configuration - `OrderForm` - Order type handling, validation - `PositionCard` - Position display, close functionality - `StatusIndicator` - Status display - `SystemHealth` - Health status aggregation - `DataFreshness` - Timestamp calculations - `OperationsPanel` - Operation display 3. **Hooks**: - `useWebSocket` - Connection, messages, subscriptions - `useRealtimeData` - Query invalidation 4. **Contexts**: - `SnackbarContext` - Notification display - `WebSocketProvider` - WebSocket management ## Running Tests ```bash # 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 1. **Mock External Dependencies**: Mock API calls, WebSocket connections 2. **Test User Interactions**: Use `@testing-library/user-event` for clicks, typing 3. **Test Error States**: Verify error handling and display 4. **Test Loading States**: Ensure loading indicators appear 5. **Test Accessibility**: Use `@testing-library/jest-dom` matchers 6. **Keep Tests Fast**: Mock expensive operations 7. **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 │ └── ... └── ... ```