Initial commit: Telegram Management System
Some checks failed
Deploy / deploy (push) Has been cancelled
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:
305
frontend-vben/apps/web-antd/tests/e2e/menu-validation.test.ts
Normal file
305
frontend-vben/apps/web-antd/tests/e2e/menu-validation.test.ts
Normal file
@@ -0,0 +1,305 @@
|
||||
import { test, expect, Page } from '@playwright/test';
|
||||
|
||||
test.describe('菜单验证测试', () => {
|
||||
let page: Page;
|
||||
|
||||
test.beforeAll(async ({ browser }) => {
|
||||
page = await browser.newPage();
|
||||
|
||||
// 设置视口大小以确保菜单完全可见
|
||||
await page.setViewportSize({ width: 1920, height: 1080 });
|
||||
|
||||
// 访问登录页面
|
||||
await page.goto('http://localhost:5174');
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
// 登录
|
||||
await page.fill('input[placeholder="请输入用户名"]', 'admin');
|
||||
await page.fill('input[placeholder="密码"]', '111111');
|
||||
await page.click('button:has-text("登录")');
|
||||
|
||||
// 等待登录成功,跳转到主页
|
||||
await page.waitForURL(/dashboard/, { timeout: 30000 });
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
console.log('✅ 登录成功');
|
||||
});
|
||||
|
||||
test.afterAll(async () => {
|
||||
await page.close();
|
||||
});
|
||||
|
||||
test('验证菜单总数和新增菜单项', async () => {
|
||||
// 等待页面完全加载
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
// 展开所有菜单项的函数
|
||||
const expandAllMenus = async () => {
|
||||
console.log('🔍 开始展开所有菜单项...');
|
||||
|
||||
// 找到所有可展开的菜单项
|
||||
const menuItems = await page.locator('.ant-menu-submenu').all();
|
||||
console.log(`发现 ${menuItems.length} 个子菜单`);
|
||||
|
||||
for (let i = 0; i < menuItems.length; i++) {
|
||||
try {
|
||||
const menuItem = menuItems[i];
|
||||
const isOpen =
|
||||
(await menuItem.locator('.ant-menu-submenu-open').count()) > 0;
|
||||
|
||||
if (!isOpen) {
|
||||
const menuTitle = await menuItem
|
||||
.locator('.ant-menu-submenu-title')
|
||||
.first();
|
||||
const titleText = await menuTitle.textContent();
|
||||
console.log(`📂 展开菜单: ${titleText}`);
|
||||
|
||||
await menuTitle.click();
|
||||
await page.waitForTimeout(500); // 等待动画完成
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(`⚠️ 展开菜单项 ${i} 时出错:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
// 等待所有菜单展开完成
|
||||
await page.waitForTimeout(1000);
|
||||
};
|
||||
|
||||
// 展开所有菜单
|
||||
await expandAllMenus();
|
||||
|
||||
// 收集所有菜单项
|
||||
const collectAllMenuItems = async () => {
|
||||
console.log('📝 开始收集菜单项...');
|
||||
const menuItems: string[] = [];
|
||||
|
||||
// 获取所有菜单项(包含子菜单标题和叶子菜单项)
|
||||
const allMenuElements = await page
|
||||
.locator('.ant-menu-item, .ant-menu-submenu-title')
|
||||
.all();
|
||||
|
||||
for (const element of allMenuElements) {
|
||||
try {
|
||||
const text = await element.textContent();
|
||||
if (text && text.trim() && !menuItems.includes(text.trim())) {
|
||||
menuItems.push(text.trim());
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('收集菜单项时出错:', error);
|
||||
}
|
||||
}
|
||||
|
||||
return menuItems.sort();
|
||||
};
|
||||
|
||||
const allMenuItems = await collectAllMenuItems();
|
||||
|
||||
console.log('📊 所有菜单项列表:');
|
||||
allMenuItems.forEach((item, index) => {
|
||||
console.log(`${index + 1}. ${item}`);
|
||||
});
|
||||
|
||||
console.log(`\n📈 菜单总数: ${allMenuItems.length}`);
|
||||
|
||||
// 验证新增的菜单项
|
||||
const expectedNewMenus = [
|
||||
'工具箱',
|
||||
'文件上传',
|
||||
'Excel导入导出',
|
||||
'WebSocket调试',
|
||||
'帮助中心',
|
||||
'系统文档',
|
||||
'权限示例',
|
||||
];
|
||||
|
||||
console.log('\n🔍 验证新增菜单项:');
|
||||
const foundNewMenus: string[] = [];
|
||||
const missingMenus: string[] = [];
|
||||
|
||||
expectedNewMenus.forEach((menuName) => {
|
||||
const found = allMenuItems.some((item) => item.includes(menuName));
|
||||
if (found) {
|
||||
foundNewMenus.push(menuName);
|
||||
console.log(`✅ 找到: ${menuName}`);
|
||||
} else {
|
||||
missingMenus.push(menuName);
|
||||
console.log(`❌ 缺失: ${menuName}`);
|
||||
}
|
||||
});
|
||||
|
||||
// 统计结果
|
||||
console.log('\n📊 验证结果总结:');
|
||||
console.log(`菜单总数: ${allMenuItems.length}`);
|
||||
console.log(
|
||||
`新增菜单找到: ${foundNewMenus.length}/${expectedNewMenus.length}`,
|
||||
);
|
||||
console.log(`找到的新菜单: ${foundNewMenus.join(', ')}`);
|
||||
|
||||
if (missingMenus.length > 0) {
|
||||
console.log(`❌ 缺失的菜单: ${missingMenus.join(', ')}`);
|
||||
}
|
||||
|
||||
// 特别检查工具箱相关菜单
|
||||
const toolboxMenus = allMenuItems.filter(
|
||||
(item) =>
|
||||
item.includes('工具箱') ||
|
||||
item.includes('文件上传') ||
|
||||
item.includes('Excel') ||
|
||||
item.includes('WebSocket'),
|
||||
);
|
||||
|
||||
console.log('\n🛠️ 工具箱相关菜单:');
|
||||
toolboxMenus.forEach((menu) => console.log(` - ${menu}`));
|
||||
|
||||
// 特别检查帮助中心相关菜单
|
||||
const helpMenus = allMenuItems.filter(
|
||||
(item) =>
|
||||
item.includes('帮助中心') ||
|
||||
item.includes('系统文档') ||
|
||||
item.includes('权限示例'),
|
||||
);
|
||||
|
||||
console.log('\n❓ 帮助中心相关菜单:');
|
||||
helpMenus.forEach((menu) => console.log(` - ${menu}`));
|
||||
|
||||
// 断言验证
|
||||
expect(allMenuItems.length).toBeGreaterThan(49); // 应该比49个更多
|
||||
expect(foundNewMenus.length).toBeGreaterThanOrEqual(5); // 至少找到5个新菜单
|
||||
|
||||
// 截图保存
|
||||
await page.screenshot({
|
||||
path: 'test-results/screenshots/menu-validation-full.png',
|
||||
fullPage: true,
|
||||
});
|
||||
|
||||
console.log('✅ 菜单验证测试完成');
|
||||
});
|
||||
|
||||
test('详细菜单结构分析', async () => {
|
||||
// 等待页面加载
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
console.log('🔍 开始详细菜单结构分析...');
|
||||
|
||||
// 获取菜单结构
|
||||
const menuStructure = await page.evaluate(() => {
|
||||
const getMenuStructure = (element: Element, level = 0): any => {
|
||||
const result: any = {};
|
||||
|
||||
if (element.classList.contains('ant-menu-submenu')) {
|
||||
const titleElement = element.querySelector('.ant-menu-submenu-title');
|
||||
const title = titleElement?.textContent?.trim() || '';
|
||||
|
||||
result.title = title;
|
||||
result.type = 'submenu';
|
||||
result.level = level;
|
||||
result.children = [];
|
||||
|
||||
const childMenu = element.querySelector('.ant-menu-sub');
|
||||
if (childMenu) {
|
||||
const childItems = childMenu.children;
|
||||
for (let i = 0; i < childItems.length; i++) {
|
||||
const child = childItems[i];
|
||||
if (
|
||||
child.classList.contains('ant-menu-item') ||
|
||||
child.classList.contains('ant-menu-submenu')
|
||||
) {
|
||||
result.children.push(getMenuStructure(child, level + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (element.classList.contains('ant-menu-item')) {
|
||||
const title = element.textContent?.trim() || '';
|
||||
result.title = title;
|
||||
result.type = 'item';
|
||||
result.level = level;
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
const menuContainer = document.querySelector('.ant-menu-root');
|
||||
const structure: any[] = [];
|
||||
|
||||
if (menuContainer) {
|
||||
const topLevelItems = menuContainer.children;
|
||||
for (let i = 0; i < topLevelItems.length; i++) {
|
||||
const item = topLevelItems[i];
|
||||
if (
|
||||
item.classList.contains('ant-menu-item') ||
|
||||
item.classList.contains('ant-menu-submenu')
|
||||
) {
|
||||
structure.push(getMenuStructure(item));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return structure;
|
||||
});
|
||||
|
||||
// 打印菜单结构
|
||||
const printMenuStructure = (items: any[], indent = '') => {
|
||||
items.forEach((item) => {
|
||||
console.log(
|
||||
`${indent}${item.type === 'submenu' ? '📁' : '📄'} ${item.title}`,
|
||||
);
|
||||
if (item.children && item.children.length > 0) {
|
||||
printMenuStructure(item.children, indent + ' ');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
console.log('\n📋 完整菜单结构:');
|
||||
printMenuStructure(menuStructure);
|
||||
|
||||
// 统计菜单数量
|
||||
const countMenuItems = (items: any[]): number => {
|
||||
let count = 0;
|
||||
items.forEach((item) => {
|
||||
count += 1;
|
||||
if (item.children && item.children.length > 0) {
|
||||
count += countMenuItems(item.children);
|
||||
}
|
||||
});
|
||||
return count;
|
||||
};
|
||||
|
||||
const totalMenuCount = countMenuItems(menuStructure);
|
||||
console.log(`\n📊 菜单结构统计: ${totalMenuCount} 个菜单项`);
|
||||
|
||||
// 验证特定菜单的存在
|
||||
const findMenuInStructure = (
|
||||
items: any[],
|
||||
searchTitle: string,
|
||||
): boolean => {
|
||||
return items.some((item) => {
|
||||
if (item.title.includes(searchTitle)) {
|
||||
return true;
|
||||
}
|
||||
if (item.children && item.children.length > 0) {
|
||||
return findMenuInStructure(item.children, searchTitle);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
const menuChecks = [
|
||||
'工具箱',
|
||||
'文件上传',
|
||||
'Excel导入导出',
|
||||
'WebSocket调试',
|
||||
'帮助中心',
|
||||
'系统文档',
|
||||
'权限示例',
|
||||
];
|
||||
|
||||
console.log('\n🔍 菜单存在性检查:');
|
||||
menuChecks.forEach((menuName) => {
|
||||
const exists = findMenuInStructure(menuStructure, menuName);
|
||||
console.log(
|
||||
`${exists ? '✅' : '❌'} ${menuName}: ${exists ? '存在' : '不存在'}`,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user