Initial commit: Telegram Management System
Some checks failed
Deploy / deploy (push) Has been cancelled

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>
This commit is contained in:
你的用户名
2025-11-04 15:37:50 +08:00
commit 237c7802e5
3674 changed files with 525172 additions and 0 deletions

View File

@@ -0,0 +1,531 @@
/**
* 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 });
}