Files
你的用户名 237c7802e5
Some checks failed
Deploy / deploy (push) Has been cancelled
Initial commit: Telegram Management System
Full-stack web application for Telegram management
- Frontend: Vue 3 + Vben Admin
- Backend: NestJS
- Features: User management, group broadcast, statistics

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-04 15:37:50 +08:00

532 lines
15 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 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 });
}