const { chromium } = require('playwright'); const fs = require('fs').promises; const path = require('path'); // 菜单项配置 const MENU_CONFIG = [ // 仪表板 { name: '仪表板', path: '/dashboard/home', selector: 'text=仪表板' }, // 账号管理 { name: 'TG账号列表', path: '/account-manage/list', selector: 'text=TG账号列表' }, { name: 'TG账号用途', path: '/account-manage/usage', selector: 'text=TG账号用途' }, { name: 'Telegram用户列表', path: '/account-manage/telegram-users', selector: 'text=Telegram用户列表' }, { name: '统一注册系统', path: '/account-manage/unified-register', selector: 'text=统一注册系统' }, { name: 'Telegram指南', path: '/account-manage/guide', selector: 'text=Telegram指南' }, { name: 'Telegram快速访问', path: '/account-manage/quick-access', selector: 'text=Telegram快速访问' }, { name: 'Telegram聊天', path: '/account-manage/telegram-chat', selector: 'text=Telegram聊天' }, { name: 'Telegram网页版', path: '/account-manage/telegram-web', selector: 'text=Telegram网页版' }, { name: 'Telegram网页版(全屏)', path: '/account-manage/telegram-full', selector: 'text=Telegram网页版(全屏)' }, // 名称管理 { name: '名字列表', path: '/name-management/firstname', selector: 'text=名字列表' }, { name: '姓氏列表', path: '/name-management/lastname', selector: 'text=姓氏列表' }, { name: '统一名称管理', path: '/name-management/unified', selector: 'text=统一名称管理' }, // 群组管理 { name: '群组列表', path: '/group-config/list', selector: 'text=群组列表' }, { name: '群组成员', path: '/group-config/members', selector: 'text=群组成员' }, { name: '群组设置', path: '/group-config/sets', selector: 'text=群组设置' }, // 消息管理 { name: '消息列表', path: '/message-management/list', selector: 'text=消息列表' }, // 营销中心 { name: '营销控制台', path: '/marketing-center/dashboard', selector: 'text=营销控制台' }, { name: '智能群发', path: '/marketing-center/smart-campaign', selector: 'text=智能群发' }, { name: '统一账号管理', path: '/marketing-center/integrated-account', selector: 'text=统一账号管理' }, { name: '账号池管理', path: '/marketing-center/account-pool', selector: 'text=账号池管理' }, { name: '风控中心', path: '/marketing-center/risk-control', selector: 'text=风控中心' }, { name: '监控中心', path: '/marketing-center/monitoring', selector: 'text=监控中心' }, // 私信群发 { name: '任务列表', path: '/direct-message/task-list', selector: 'text=任务列表' }, { name: '创建任务', path: '/direct-message/create-task', selector: 'text=创建任务' }, { name: '模板列表', path: '/direct-message/template-list', selector: 'text=模板列表' }, { name: '创建模板', path: '/direct-message/create-template', selector: 'text=创建模板' }, { name: '引擎控制', path: '/direct-message/engine-control', selector: 'text=引擎控制' }, { name: '统计分析', path: '/direct-message/statistics', selector: 'text=统计分析' }, // 群发广播 { name: '广播任务', path: '/group-broadcast/task', selector: 'text=广播任务' }, { name: '广播日志', path: '/group-broadcast/log', selector: 'text=广播日志' }, // 炒群营销 { name: '剧本列表', path: '/group-marketing/script', selector: 'text=剧本列表' }, { name: '营销项目', path: '/group-marketing/project', selector: 'text=营销项目' }, // 短信平台 { name: '短信仪表板', path: '/sms-platform/dashboard', selector: 'text=短信仪表板' }, { name: '平台管理', path: '/sms-platform/platform-list', selector: 'text=平台管理' }, { name: '发送记录', path: '/sms-platform/records', selector: 'text=发送记录' }, { name: '统计分析', path: '/sms-platform/statistics', selector: 'text=统计分析' }, { name: '价格对比', path: '/sms-platform/price-compare', selector: 'text=价格对比' }, { name: '快速操作', path: '/sms-platform/quick-actions', selector: 'text=快速操作' }, { name: '余额报警', path: '/sms-platform/balance-alert', selector: 'text=余额报警' }, // 日志管理 { name: '注册日志', path: '/log-manage/register', selector: 'text=注册日志' }, { name: '用户登录', path: '/log-manage/user-login', selector: 'text=用户登录' }, { name: '用户注册', path: '/log-manage/user-register', selector: 'text=用户注册' }, { name: '群发日志', path: '/log-manage/group-send', selector: 'text=群发日志' }, { name: '群组加入', path: '/log-manage/group-join', selector: 'text=群组加入' }, { name: 'TG登录验证码', path: '/log-manage/login-code', selector: 'text=TG登录验证码' }, { name: '拉群成员', path: '/log-manage/pull-member', selector: 'text=拉群成员' }, { name: '拉群统计', path: '/log-manage/pull-member-statistic', selector: 'text=拉群统计' }, { name: '拉群项目统计', path: '/log-manage/pull-member-project-statistic', selector: 'text=拉群项目统计' }, // 系统配置 { name: '系统参数', path: '/system-config/params', selector: 'text=系统参数' }, { name: '通用设置', path: '/system-config/base-config', selector: 'text=通用设置' }, { name: '数据中心', path: '/system-config/dc-list', selector: 'text=数据中心' } ]; class VbenMenuChecker { constructor() { this.baseUrl = 'http://localhost:5173'; this.results = []; this.screenshots = []; this.browser = null; this.context = null; this.page = null; } async init() { console.log('🚀 启动浏览器...'); this.browser = await chromium.launch({ headless: false, slowMo: 500 }); this.context = await this.browser.newContext({ viewport: { width: 1920, height: 1080 } }); this.page = await this.context.newPage(); // 监听网络请求 this.page.on('response', (response) => { const url = response.url(); if (url.includes('/api/') && !response.ok()) { console.log(`⚠️ API 请求失败: ${url} - ${response.status()}`); } }); } async login() { console.log('🔑 开始登录...'); try { await this.page.goto(this.baseUrl); await this.page.waitForLoadState('networkidle'); // 检查是否已经登录 try { if (await this.page.locator('text=仪表板').first().isVisible({ timeout: 2000 })) { console.log('✅ 已经登录'); return true; } } catch (e) { // 继续登录流程 } // 填写登录信息 await this.page.fill('input[placeholder*="用户名"]', 'admin'); await this.page.fill('input[placeholder*="密码"]', '111111'); // 点击登录按钮 await this.page.click('button:has-text("登录")'); await this.page.waitForLoadState('networkidle'); // 等待登录成功 - 等待页面跳转 await this.page.waitForTimeout(3000); // 检查是否登录成功(URL改变或出现菜单) const currentUrl = this.page.url(); const hasMenu = await this.page.locator('.ant-menu, .vben-menu, [role="menu"]').count() > 0; const hasDashboard = await this.page.locator('text=仪表板').count() > 0; if (!currentUrl.includes('/login') || hasMenu || hasDashboard) { console.log('✅ 登录成功,当前页面包含菜单组件'); } else { throw new Error('登录后未找到预期的页面元素'); } console.log('✅ 登录成功'); // 截图保存登录后状态 await this.page.screenshot({ path: `/Users/hahaha/telegram-management-system/test-screenshots/vben-after-login.png`, fullPage: true }); return true; } catch (error) { console.error('❌ 登录失败:', error.message); await this.page.screenshot({ path: `/Users/hahaha/telegram-management-system/test-screenshots/vben-login-error.png`, fullPage: true }); return false; } } async checkMenu(menuItem) { console.log(`\n📋 检查菜单: ${menuItem.name}`); const result = { name: menuItem.name, path: menuItem.path, success: false, loadTime: 0, hasData: false, error: null, dataElements: 0, apiCalls: 0, screenshot: null }; try { const startTime = Date.now(); // 监听 API 调用 let apiCallCount = 0; const apiListener = (response) => { if (response.url().includes('/api/')) { apiCallCount++; console.log(`🔄 API 调用: ${response.url()} - ${response.status()}`); } }; this.page.on('response', apiListener); // 导航到菜单页面 await this.page.goto(`${this.baseUrl}${menuItem.path}`); await this.page.waitForLoadState('networkidle'); // 等待页面内容加载 await this.page.waitForTimeout(2000); const loadTime = Date.now() - startTime; result.loadTime = loadTime; result.apiCalls = apiCallCount; // 检查页面是否有错误 const hasError = await this.page.locator('.ant-result-error, .error, .ant-empty').count() > 0; // 检查是否有数据表格或列表 const tableCount = await this.page.locator('table, .ant-table, .ant-list, .card-list').count(); const dataElements = await this.page.locator('td, li, .card-item').count(); result.dataElements = dataElements; result.hasData = dataElements > 0; result.success = !hasError && loadTime < 10000; // 截图 const screenshotPath = `/Users/hahaha/telegram-management-system/test-screenshots/vben-${menuItem.name.replace(/[\/\\]/g, '-')}.png`; await this.page.screenshot({ path: screenshotPath, fullPage: true }); result.screenshot = screenshotPath; console.log(` ✅ 加载时间: ${loadTime}ms`); console.log(` 📊 数据元素: ${dataElements}个`); console.log(` 🌐 API调用: ${apiCallCount}次`); console.log(` 📷 截图: ${screenshotPath}`); // 移除事件监听器 this.page.off('response', apiListener); } catch (error) { result.error = error.message; console.error(` ❌ 错误: ${error.message}`); // 错误截图 const errorScreenshotPath = `/Users/hahaha/telegram-management-system/test-screenshots/vben-error-${menuItem.name.replace(/[\/\\]/g, '-')}.png`; await this.page.screenshot({ path: errorScreenshotPath, fullPage: true }); result.screenshot = errorScreenshotPath; } return result; } async checkAllMenus() { console.log(`\n🎯 开始检查 ${MENU_CONFIG.length} 个菜单...\n`); for (let i = 0; i < MENU_CONFIG.length; i++) { const menuItem = MENU_CONFIG[i]; console.log(`[${i + 1}/${MENU_CONFIG.length}] 检查菜单: ${menuItem.name}`); const result = await this.checkMenu(menuItem); this.results.push(result); // 每检查5个菜单暂停一下 if ((i + 1) % 5 === 0) { console.log(`⏸️ 已检查 ${i + 1} 个菜单,暂停2秒...`); await this.page.waitForTimeout(2000); } } } async generateReport() { console.log('\n📊 生成测试报告...'); const successCount = this.results.filter(r => r.success).length; const errorCount = this.results.filter(r => r.error).length; const dataCount = this.results.filter(r => r.hasData).length; const avgLoadTime = this.results.reduce((sum, r) => sum + r.loadTime, 0) / this.results.length; const totalApiCalls = this.results.reduce((sum, r) => sum + r.apiCalls, 0); const report = { timestamp: new Date().toISOString(), summary: { total: this.results.length, success: successCount, errors: errorCount, hasData: dataCount, successRate: `${((successCount / this.results.length) * 100).toFixed(1)}%`, avgLoadTime: `${avgLoadTime.toFixed(0)}ms`, totalApiCalls }, details: this.results.map(r => ({ name: r.name, path: r.path, success: r.success, loadTime: `${r.loadTime}ms`, hasData: r.hasData, dataElements: r.dataElements, apiCalls: r.apiCalls, error: r.error, screenshot: r.screenshot })) }; // 保存报告 await fs.writeFile( '/Users/hahaha/telegram-management-system/vben-menu-test-report.json', JSON.stringify(report, null, 2) ); // 打印摘要 console.log('\n📈 测试摘要:'); console.log(` 总菜单数: ${report.summary.total}`); console.log(` 成功加载: ${report.summary.success} (${report.summary.successRate})`); console.log(` 加载错误: ${report.summary.errors}`); console.log(` 有数据显示: ${report.summary.hasData}`); console.log(` 平均加载时间: ${report.summary.avgLoadTime}`); console.log(` API调用总数: ${report.summary.totalApiCalls}`); // 打印有问题的菜单 const problemMenus = this.results.filter(r => !r.success || r.error); if (problemMenus.length > 0) { console.log('\n⚠️ 有问题的菜单:'); problemMenus.forEach(menu => { console.log(` - ${menu.name}: ${menu.error || '加载失败'}`); }); } // 打印无数据的菜单 const noDataMenus = this.results.filter(r => r.success && !r.hasData); if (noDataMenus.length > 0) { console.log('\n📭 无数据显示的菜单:'); noDataMenus.forEach(menu => { console.log(` - ${menu.name} (${menu.loadTime}ms)`); }); } console.log(`\n📁 完整报告已保存到: vben-menu-test-report.json`); return report; } async close() { if (this.browser) { await this.browser.close(); } } } async function main() { const checker = new VbenMenuChecker(); try { await checker.init(); const loginSuccess = await checker.login(); if (!loginSuccess) { console.error('❌ 登录失败,无法继续测试'); return; } await checker.checkAllMenus(); await checker.generateReport(); console.log('\n🎉 菜单检查完成!'); } catch (error) { console.error('❌ 测试过程中发生错误:', error); } finally { await checker.close(); } } // 运行测试 if (require.main === module) { main().catch(console.error); } module.exports = { VbenMenuChecker, MENU_CONFIG };