/** * WebSocket 实时通信功能端到端测试 * 测试实时消息推送、状态同步、连接管理等功能 */ import { test, expect, Page } from '@playwright/test'; const TEST_CONFIG = { baseURL: 'http://localhost:5173', wsURL: 'ws://localhost:3001/ws', timeout: 30000, adminUser: { username: 'admin', password: '111111', }, }; test.describe('WebSocket 连接管理测试', () => { test.beforeEach(async ({ page }) => { test.setTimeout(TEST_CONFIG.timeout); await page.goto(TEST_CONFIG.baseURL); await loginAsAdmin(page); }); test('WebSocket连接应该自动建立', async ({ page }) => { // 等待WebSocket连接建立 await page.waitForTimeout(2000); // 检查连接状态指示器 const connectionStatus = page.locator( '[data-testid="ws-connection-status"]', ); await expect(connectionStatus).toBeVisible(); await expect(connectionStatus).toContainText(/已连接|Connected/); // 检查连接状态图标 const statusIcon = page.locator('[data-testid="ws-status-icon"]'); await expect(statusIcon).toHaveClass(/connected|success/); }); test('WebSocket断线重连应该正常工作', async ({ page }) => { // 等待连接建立 await page.waitForTimeout(2000); // 模拟网络中断(通过开发者工具) await page.evaluate(() => { // 关闭WebSocket连接 if (window.wsConnection) { window.wsConnection.close(); } }); // 检查连接状态变为断开 const connectionStatus = page.locator( '[data-testid="ws-connection-status"]', ); await expect(connectionStatus).toContainText(/连接中断|Disconnected/, { timeout: 5000, }); // 等待自动重连 await page.waitForTimeout(5000); // 检查是否重新连接 await expect(connectionStatus).toContainText(/已连接|Connected/, { timeout: 10000, }); }); test('手动重连功能应该正常工作', async ({ page }) => { // 模拟连接断开 await page.evaluate(() => { if (window.wsConnection) { window.wsConnection.close(); } }); // 等待状态更新 await page.waitForTimeout(2000); // 点击手动重连按钮 const reconnectButton = page.locator('[data-testid="ws-reconnect-button"]'); await reconnectButton.click(); // 检查重连状态 const connectionStatus = page.locator( '[data-testid="ws-connection-status"]', ); await expect(connectionStatus).toContainText(/连接中|Connecting/); // 等待重连完成 await expect(connectionStatus).toContainText(/已连接|Connected/, { timeout: 10000, }); }); test('连接心跳机制应该正常工作', async ({ page }) => { // 监听WebSocket消息 const heartbeatMessages: string[] = []; await page.evaluateHandle(() => { return new Promise((resolve) => { const originalSend = WebSocket.prototype.send; WebSocket.prototype.send = function (data) { if (data.includes('heartbeat') || data.includes('ping')) { window.heartbeatSent = true; } return originalSend.call(this, data); }; setTimeout(resolve, 5000); }); }); // 检查是否发送了心跳消息 const heartbeatSent = await page.evaluate(() => window.heartbeatSent); expect(heartbeatSent).toBe(true); }); }); test.describe('实时消息推送测试', () => { test.beforeEach(async ({ page }) => { await page.goto(TEST_CONFIG.baseURL); await loginAsAdmin(page); // 等待WebSocket连接建立 await page.waitForTimeout(2000); }); test('系统通知应该实时接收', async ({ page }) => { // 监听通知消息 const notifications: any[] = []; await page.exposeFunction('onNotification', (data: any) => { notifications.push(data); }); // 注册通知监听器 await page.evaluate(() => { if (window.wsConnection) { window.wsConnection.addEventListener('message', (event) => { const data = JSON.parse(event.data); if (data.type === 'notification') { window.onNotification(data); } }); } }); // 模拟发送系统通知 await page.evaluate(() => { // 这里应该触发一个会产生系统通知的操作 // 例如:创建一个新的任务或发送一条消息 }); // 等待通知到达 await page.waitForTimeout(3000); // 检查通知是否显示 const notificationElement = page.locator('.ant-notification-notice'); if ((await notificationElement.count()) > 0) { await expect(notificationElement).toBeVisible(); } }); test('消息状态更新应该实时同步', async ({ page }) => { // 导航到私信群发页面 await page.click('[data-testid="menu-private-message"]'); await page.click('[data-testid="menu-send-record"]'); await page.waitForURL(/\/private-message\/record/); // 记录初始状态 const initialStats = await page .locator('[data-testid="sending-count"]') .textContent(); // 模拟消息状态变更(通过WebSocket) await page.evaluate(() => { if (window.wsConnection) { // 模拟接收状态更新消息 const mockMessage = { type: 'message_status_update', data: { taskId: 'test-task-123', messageId: 'msg-456', status: 'sent', timestamp: Date.now(), }, }; const event = new MessageEvent('message', { data: JSON.stringify(mockMessage), }); window.wsConnection.dispatchEvent(event); } }); // 等待状态更新 await page.waitForTimeout(2000); // 检查统计数据是否更新 const updatedStats = await page .locator('[data-testid="sending-count"]') .textContent(); // 至少UI应该是响应的 await expect(page.locator('[data-testid="sending-count"]')).toBeVisible(); }); test('用户在线状态应该实时更新', async ({ page }) => { // 导航到用户列表页面 await page.click('[data-testid="menu-account"]'); await page.click('[data-testid="menu-account-list"]'); await page.waitForURL(/\/account\/list/); // 检查用户在线状态指示器 const onlineIndicators = page.locator('[data-testid="user-online-status"]'); if ((await onlineIndicators.count()) > 0) { // 模拟用户状态变更 await page.evaluate(() => { if (window.wsConnection) { const mockMessage = { type: 'user_status_update', data: { userId: 'user-123', status: 'online', timestamp: Date.now(), }, }; const event = new MessageEvent('message', { data: JSON.stringify(mockMessage), }); window.wsConnection.dispatchEvent(event); } }); // 等待状态更新 await page.waitForTimeout(1000); // 检查状态指示器 await expect(onlineIndicators.first()).toBeVisible(); } }); test('任务进度应该实时更新', async ({ page }) => { // 导航到任务列表页面 await page.click('[data-testid="menu-private-message"]'); await page.click('[data-testid="menu-send-task"]'); await page.waitForURL(/\/private-message\/task/); // 找到一个进行中的任务 const taskProgress = page.locator('[data-testid="task-progress"]').first(); if ((await taskProgress.count()) > 0) { // 记录初始进度 const initialProgress = await taskProgress.getAttribute('data-percent'); // 模拟进度更新 await page.evaluate(() => { if (window.wsConnection) { const mockMessage = { type: 'task_progress_update', data: { taskId: 'task-123', progress: 75, completed: 750, total: 1000, timestamp: Date.now(), }, }; const event = new MessageEvent('message', { data: JSON.stringify(mockMessage), }); window.wsConnection.dispatchEvent(event); } }); // 等待进度更新 await page.waitForTimeout(2000); // 检查进度条是否更新 await expect(taskProgress).toBeVisible(); } }); }); test.describe('实时监控功能测试', () => { test.beforeEach(async ({ page }) => { await page.goto(TEST_CONFIG.baseURL); await loginAsAdmin(page); // 导航到实时监控页面 await page.click('[data-testid="menu-private-message"]'); await page.click('[data-testid="menu-monitor"]'); await page.waitForURL(/\/private-message\/monitor/); }); test('实时性能指标应该正常更新', async ({ page }) => { // 检查性能指标卡片 const performanceMetrics = [ '[data-testid="cpu-usage"]', '[data-testid="memory-usage"]', '[data-testid="active-connections"]', '[data-testid="message-rate"]', ]; for (const metric of performanceMetrics) { await expect(page.locator(metric)).toBeVisible(); } // 记录初始值 const initialCpu = await page .locator('[data-testid="cpu-usage"]') .textContent(); // 等待数据更新 await page.waitForTimeout(5000); // 检查数据是否可能发生变化 const currentCpu = await page .locator('[data-testid="cpu-usage"]') .textContent(); // 至少UI应该是响应的 await expect(page.locator('[data-testid="cpu-usage"]')).toBeVisible(); }); test('实时日志应该正常显示', async ({ page }) => { // 检查日志显示区域 await expect(page.locator('.realtime-logs')).toBeVisible(); // 检查日志条目 const logEntries = page.locator('.log-entry'); // 如果有日志条目,检查其结构 if ((await logEntries.count()) > 0) { const firstLog = logEntries.first(); await expect(firstLog.locator('.log-timestamp')).toBeVisible(); await expect(firstLog.locator('.log-level')).toBeVisible(); await expect(firstLog.locator('.log-message')).toBeVisible(); } // 模拟新日志产生 await page.evaluate(() => { if (window.wsConnection) { const mockLogMessage = { type: 'log_message', data: { level: 'info', message: '测试日志消息', timestamp: Date.now(), source: 'test-component', }, }; const event = new MessageEvent('message', { data: JSON.stringify(mockLogMessage), }); window.wsConnection.dispatchEvent(event); } }); // 等待新日志显示 await page.waitForTimeout(1000); // 检查是否有新的日志条目 await expect(page.locator('.log-entry')).toHaveCount({ min: 1 }); }); test('告警信息应该实时显示', async ({ page }) => { // 检查告警区域 const alertsSection = page.locator('.alerts-section'); await expect(alertsSection).toBeVisible(); // 模拟告警消息 await page.evaluate(() => { if (window.wsConnection) { const mockAlert = { type: 'alert', data: { level: 'warning', title: '发送频率过高', message: '检测到发送频率超过限制,请注意调整', timestamp: Date.now(), id: 'alert-' + Date.now(), }, }; const event = new MessageEvent('message', { data: JSON.stringify(mockAlert), }); window.wsConnection.dispatchEvent(event); } }); // 等待告警显示 await page.waitForTimeout(2000); // 检查告警是否显示 const alertItems = page.locator('.alert-item'); if ((await alertItems.count()) > 0) { await expect(alertItems.first()).toBeVisible(); await expect(alertItems.first().locator('.alert-title')).toContainText( '发送频率过高', ); } }); test('实时图表应该正常更新', async ({ page }) => { // 检查实时图表容器 const chartContainer = page.locator('.realtime-chart .echarts-container'); await expect(chartContainer).toBeVisible(); // 等待图表渲染 await page.waitForTimeout(3000); // 检查图表是否有数据 const chartCanvas = chartContainer.locator('canvas').first(); await expect(chartCanvas).toBeVisible(); // 模拟新的性能数据 await page.evaluate(() => { if (window.wsConnection) { const mockPerformanceData = { type: 'performance_data', data: { timestamp: Date.now(), cpu: Math.random() * 100, memory: Math.random() * 100, connections: Math.floor(Math.random() * 1000), messageRate: Math.floor(Math.random() * 500), }, }; const event = new MessageEvent('message', { data: JSON.stringify(mockPerformanceData), }); window.wsConnection.dispatchEvent(event); } }); // 等待图表更新 await page.waitForTimeout(2000); // 图表应该仍然可见且响应 await expect(chartCanvas).toBeVisible(); }); }); test.describe('多标签页同步测试', () => { test('多标签页状态应该保持同步', async ({ context }) => { // 创建两个标签页 const page1 = await context.newPage(); const page2 = await context.newPage(); // 在两个标签页都登录 await page1.goto(TEST_CONFIG.baseURL); await loginAsAdmin(page1); await page2.goto(TEST_CONFIG.baseURL); await loginAsAdmin(page2); // 等待WebSocket连接建立 await Promise.all([page1.waitForTimeout(2000), page2.waitForTimeout(2000)]); // 在第一个标签页触发一个操作 await page1.click('[data-testid="menu-private-message"]'); await page1.click('[data-testid="menu-send-task"]'); // 模拟在第一个标签页创建任务 await page1.evaluate(() => { if (window.wsConnection) { const mockTaskCreated = { type: 'task_created', data: { taskId: 'new-task-123', name: '新创建的测试任务', timestamp: Date.now(), }, }; const event = new MessageEvent('message', { data: JSON.stringify(mockTaskCreated), }); window.wsConnection.dispatchEvent(event); } }); // 切换到第二个标签页的相同页面 await page2.click('[data-testid="menu-private-message"]'); await page2.click('[data-testid="menu-send-task"]'); // 等待数据同步 await page2.waitForTimeout(3000); // 检查两个标签页的数据是否一致 const page1TaskCount = await page1.locator('.task-item').count(); const page2TaskCount = await page2.locator('.task-item').count(); // 至少UI应该是同步的 expect(Math.abs(page1TaskCount - page2TaskCount)).toBeLessThanOrEqual(1); await page1.close(); await page2.close(); }); }); // 辅助函数:管理员登录 async function loginAsAdmin(page: Page) { await page.fill( '[data-testid="username-input"]', TEST_CONFIG.adminUser.username, ); await page.fill( '[data-testid="password-input"]', TEST_CONFIG.adminUser.password, ); await page.click('[data-testid="login-button"]'); await page.waitForURL(/\/dashboard/, { timeout: 10000 }); }