Set up comprehensive frontend testing infrastructure

- Install Jest for unit testing with React Testing Library
- Install Playwright for end-to-end testing
- Configure Jest with proper TypeScript support and module mapping
- Create test setup files and utilities for both unit and e2e tests

Components:
* Jest configuration with coverage thresholds
* Playwright configuration with browser automation
* Unit tests for LoginForm, AuthContext, and useSocketIO hook
* E2E tests for authentication, dashboard, and agents workflows
* GitHub Actions workflow for automated testing
* Mock data and API utilities for consistent testing
* Test documentation with best practices

Testing features:
- Unit tests with 70% coverage threshold
- E2E tests with API mocking and user journey testing
- CI/CD integration for automated test runs
- Cross-browser testing support with Playwright
- Authentication system testing end-to-end

🚀 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
anthonyrawlins
2025-07-11 14:06:34 +10:00
parent c6d69695a8
commit aacb45156b
6109 changed files with 777927 additions and 1 deletions

View File

@@ -0,0 +1,65 @@
import '@testing-library/jest-dom';
// Mock environment variables
process.env.VITE_API_BASE_URL = 'http://localhost:8087';
// Mock IntersectionObserver
global.IntersectionObserver = jest.fn().mockImplementation((callback) => ({
observe: jest.fn(),
unobserve: jest.fn(),
disconnect: jest.fn(),
trigger: (entries: any) => callback(entries),
}));
// Mock ResizeObserver
global.ResizeObserver = jest.fn().mockImplementation(() => ({
observe: jest.fn(),
unobserve: jest.fn(),
disconnect: jest.fn(),
}));
// Mock matchMedia
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: jest.fn().mockImplementation((query) => ({
matches: false,
media: query,
onchange: null,
addListener: jest.fn(), // deprecated
removeListener: jest.fn(), // deprecated
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
})),
});
// Mock scrollTo
global.scrollTo = jest.fn();
// Mock console methods to reduce noise in tests
const originalConsoleError = console.error;
const originalConsoleWarn = console.warn;
console.error = (...args: any[]) => {
// Suppress specific React warnings in tests
const message = args[0];
if (
typeof message === 'string' &&
(message.includes('Warning: ReactDOM.render is deprecated') ||
message.includes('Warning: validateDOMNesting'))
) {
return;
}
originalConsoleError.apply(console, args);
};
console.warn = (...args: any[]) => {
const message = args[0];
if (
typeof message === 'string' &&
message.includes('componentWillReceiveProps has been renamed')
) {
return;
}
originalConsoleWarn.apply(console, args);
};

120
frontend/src/test/utils.tsx Normal file
View File

@@ -0,0 +1,120 @@
import { ReactElement } from 'react';
import { render, RenderOptions } from '@testing-library/react';
import { BrowserRouter } from 'react-router-dom';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { AuthProvider } from '../contexts/AuthContext';
// Create a custom render function that includes providers
const AllProviders = ({ children }: { children: React.ReactNode }) => {
const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: false,
gcTime: 0,
},
},
});
return (
<QueryClientProvider client={queryClient}>
<BrowserRouter>
<AuthProvider>
{children}
</AuthProvider>
</BrowserRouter>
</QueryClientProvider>
);
};
const customRender = (
ui: ReactElement,
options?: Omit<RenderOptions, 'wrapper'>
) => render(ui, { wrapper: AllProviders, ...options });
export * from '@testing-library/react';
export { customRender as render };
// Mock data helpers
export const mockUser = {
id: '123e4567-e89b-12d3-a456-426614174000',
username: 'testuser',
email: 'test@example.com',
full_name: 'Test User',
name: 'Test User',
role: 'user',
is_active: true,
is_superuser: false,
is_verified: true,
};
export const mockAgent = {
id: 'test-agent-1',
name: 'Test Agent',
endpoint: 'http://localhost:11434',
model: 'llama3.1:8b',
specialty: 'general_ai',
max_concurrent: 2,
current_tasks: 0,
agent_type: 'ollama',
status: 'online',
last_heartbeat: Date.now(),
};
export const mockTask = {
id: 'task_1735589200_0',
title: 'Test Task',
description: 'A test task for unit testing',
type: 'general_ai',
priority: 3,
status: 'pending',
created_at: Date.now(),
context: {
objective: 'Test objective',
requirements: ['Test requirement 1'],
},
};
export const mockWorkflow = {
id: 'workflow_1735589200',
name: 'Test Workflow',
description: 'A test workflow',
steps: [
{
name: 'Step 1',
type: 'general_ai',
agent_type: 'general_ai',
inputs: { prompt: 'Test prompt' },
outputs: ['result'],
},
],
created_at: Date.now(),
status: 'pending',
};
// Mock API responses
export const mockApiResponses = {
auth: {
login: {
access_token: 'mock-jwt-token',
token_type: 'bearer',
user: mockUser,
},
me: mockUser,
},
agents: {
list: [mockAgent],
detail: mockAgent,
},
tasks: {
list: [mockTask],
detail: mockTask,
},
workflows: {
list: [mockWorkflow],
detail: mockWorkflow,
},
};
// Async utility for waiting for elements
export const waitFor = (ms: number) =>
new Promise((resolve) => setTimeout(resolve, ms));