import { Page } from '@playwright/test'; export interface MockConfig { enabled: boolean; mode: 'full' | 'partial' | 'none'; mockPaths: string[]; delay: number; logCalls: boolean; validateResponses: boolean; dataSource: 'memory' | 'file' | 'database'; } export interface MockStatus { enabled: boolean; mode: string; activeMocks: string[]; callCount: number; } export interface MockData { users?: any[]; roles?: any[]; menus?: any[]; operationLogs?: any[]; auth?: any; } export class MockManager { private config: MockConfig; private mockData: Map = new Map(); private callHistory: Array<{ timestamp: number; url: string; method: string; response: any }> = []; constructor(config: MockConfig) { this.config = config; } enableMock(): void { this.config.enabled = true; console.log('✅ Mock已启用'); } disableMock(): void { this.config.enabled = false; console.log('❌ Mock已禁用'); } configureMock(config: Partial): void { this.config = { ...this.config, ...config }; console.log('⚙️ Mock配置已更新:', this.config); } resetMockData(): void { this.mockData.clear(); this.callHistory = []; console.log('🔄 Mock数据已重置'); } presetTestData(data: MockData): void { Object.entries(data).forEach(([key, value]) => { this.mockData.set(key, value); }); console.log('📦 测试数据已预设:', Object.keys(data)); } clearPresets(): void { this.mockData.clear(); console.log('🗑️ 预设数据已清除'); } getMockStatus(): MockStatus { return { enabled: this.config.enabled, mode: this.config.mode, activeMocks: Array.from(this.mockData.keys()), callCount: this.callHistory.length }; } getMockData(key: string): any { return this.mockData.get(key); } recordCall(url: string, method: string, response: any): void { if (this.config.logCalls) { this.callHistory.push({ timestamp: Date.now(), url, method, response }); } } getCallHistory(): Array<{ timestamp: number; url: string; method: string; response: any }> { return this.callHistory; } async interceptAPIRequest(page: Page): Promise { if (!this.config.enabled) { return; } await page.route('**/sys/**', async (route) => { const request = route.request(); const url = request.url(); const method = request.method(); const shouldMock = this.shouldMockRequest(url, method); if (shouldMock) { const mockResponse = await this.getMockResponse(url, method, request.postData()); if (mockResponse) { this.recordCall(url, method, mockResponse); await route.fulfill({ status: mockResponse.status || 200, contentType: 'application/json', body: JSON.stringify(mockResponse.body) }); console.log(`🎭 Mock响应: ${method} ${url}`); return; } } await route.continue(); }); } private shouldMockRequest(url: string, method: string): boolean { if (!this.config.enabled) { return false; } if (this.config.mode === 'full') { return true; } if (this.config.mode === 'partial') { return this.config.mockPaths.some(path => url.includes(path)); } return false; } private async getMockResponse(url: string, method: string, postData?: string): Promise { if (this.config.delay > 0) { await new Promise(resolve => setTimeout(resolve, this.config.delay)); } if (url.includes('/sys/auth/login') && method === 'POST') { const credentials = JSON.parse(postData || '{}'); if (credentials.username === 'admin' && credentials.password === 'admin123') { return { status: 200, body: { code: '200', message: '登录成功', data: { token: 'mock-token-123456', refreshToken: 'mock-refresh-token-789012', user: { id: 1, username: 'admin', email: 'admin@example.com', phone: '13800138000', status: 'active', createBy: 'system', updateBy: 'admin', createdAt: '2024-01-01T00:00:00.000Z', updatedAt: '2024-01-01T00:00:00.000Z' }, permissions: ['dashboard:view', 'user:read', 'user:write', 'role:read', 'role:write', 'menu:read', 'menu:write', 'operationLog:read'] } } }; } else { return { status: 200, body: { code: '401', message: '用户名或密码错误', data: null } }; } } if (url.includes('/sys/auth/logout') && method === 'POST') { return { status: 200, body: { code: '200', message: '登出成功', data: null } }; } if (url.includes('/sys/auth/refresh') && method === 'POST') { return { status: 200, body: { code: '200', message: '刷新成功', data: { token: 'new-mock-token-123456', refreshToken: 'new-mock-refresh-token-789012' } } }; } if (url.includes('/sys/user') && method === 'GET') { const users = this.mockData.get('users') || []; return { status: 200, body: { code: '200', message: '查询成功', data: { records: users, total: users.length, current: 1, size: 10 } } }; } if (url.includes('/sys/user') && method === 'POST') { const newUser = JSON.parse(postData || '{}'); return { status: 200, body: { code: '200', message: '创建成功', data: { ...newUser, id: Math.floor(Math.random() * 10000) + 1, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() } } }; } if (url.includes('/sys/user/') && method === 'PUT') { const updatedUser = JSON.parse(postData || '{}'); return { status: 200, body: { code: '200', message: '更新成功', data: { ...updatedUser, updatedAt: new Date().toISOString() } } }; } if (url.includes('/sys/user/') && method === 'DELETE') { return { status: 200, body: { code: '200', message: '删除成功', data: null } }; } if (url.includes('/sys/role') && method === 'GET') { const roles = this.mockData.get('roles') || []; return { status: 200, body: { code: '200', message: '查询成功', data: { records: roles, total: roles.length, current: 1, size: 10 } } }; } if (url.includes('/sys/role') && method === 'POST') { const newRole = JSON.parse(postData || '{}'); return { status: 200, body: { code: '200', message: '创建成功', data: { ...newRole, id: Math.floor(Math.random() * 1000) + 1, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() } } }; } if (url.includes('/sys/role/') && method === 'PUT') { const updatedRole = JSON.parse(postData || '{}'); return { status: 200, body: { code: '200', message: '更新成功', data: { ...updatedRole, updatedAt: new Date().toISOString() } } }; } if (url.includes('/sys/role/') && method === 'DELETE') { return { status: 200, body: { code: '200', message: '删除成功', data: null } }; } if (url.includes('/sys/menu') && method === 'GET') { const menus = this.mockData.get('menus') || []; return { status: 200, body: { code: '200', message: '查询成功', data: menus } }; } if (url.includes('/sys/menu/user/') && method === 'GET') { const userIdMatch = url.match(/\/sys\/menu\/user\/(\d+)/); const userId = userIdMatch ? parseInt(userIdMatch[1]) : 1; const userMenus = [ { id: 1, code: 'dashboard', name: '仪表盘', path: '/dashboard', icon: 'DashboardOutlined', parentId: 0, sortOrder: 1, status: 'active', createdAt: '2024-01-01T00:00:00.000Z', updatedAt: '2024-01-01T00:00:00.000Z', children: [] }, { id: 2, code: 'user', name: '用户管理', path: '/users', icon: 'UserOutlined', parentId: 0, sortOrder: 2, status: 'active', createdAt: '2024-01-01T00:00:00.000Z', updatedAt: '2024-01-01T00:00:00.000Z', children: [] }, { id: 3, code: 'role', name: '角色管理', path: '/roles', icon: 'TeamOutlined', parentId: 0, sortOrder: 3, status: 'active', createdAt: '2024-01-01T00:00:00.000Z', updatedAt: '2024-01-01T00:00:00.000Z', children: [] }, { id: 4, code: 'menu', name: '菜单管理', path: '/menus', icon: 'MenuOutlined', parentId: 0, sortOrder: 4, status: 'active', createdAt: '2024-01-01T00:00:00.000Z', updatedAt: '2024-01-01T00:00:00.000Z', children: [] }, { id: 5, code: 'permission', name: '权限管理', path: '/permissions', icon: 'SafetyOutlined', parentId: 0, sortOrder: 5, status: 'active', createdAt: '2024-01-01T00:00:00.000Z', updatedAt: '2024-01-01T00:00:00.000Z', children: [] }, { id: 6, code: 'operationLog', name: '操作日志', path: '/operation-logs', icon: 'FileTextOutlined', parentId: 0, sortOrder: 6, status: 'active', createdAt: '2024-01-01T00:00:00.000Z', updatedAt: '2024-01-01T00:00:00.000Z', children: [] } ]; return { status: 200, body: { code: '200', message: '查询成功', data: userMenus } }; } if (url.includes('/sys/menu') && method === 'POST') { const newMenu = JSON.parse(postData || '{}'); return { status: 200, body: { code: '200', message: '创建成功', data: { ...newMenu, id: Math.floor(Math.random() * 1000) + 1, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() } } }; } if (url.includes('/sys/menu/') && method === 'PUT') { const updatedMenu = JSON.parse(postData || '{}'); return { status: 200, body: { code: '200', message: '更新成功', data: { ...updatedMenu, updatedAt: new Date().toISOString() } } }; } if (url.includes('/sys/menu/') && method === 'DELETE') { return { status: 200, body: { code: '200', message: '删除成功', data: null } }; } if (url.includes('/sys/operationLog') && method === 'GET') { const operationLogs = this.mockData.get('operationLogs') || []; return { status: 200, body: { code: '200', message: '查询成功', data: { records: operationLogs, total: operationLogs.length, current: 1, size: 10 } } }; } return null; } }