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>
306 lines
9.0 KiB
TypeScript
306 lines
9.0 KiB
TypeScript
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 ? '存在' : '不存在'}`,
|
||
);
|
||
});
|
||
});
|
||
});
|