import { test, expect } from '@playwright/test'; /** * 超管端登录到菜单页面完整流程测试 */ test.describe('超管端完整登录流程测试', () => { const BASE_URL = 'http://localhost:3005'; const API_BASE = 'http://localhost:8580'; test('超管端完整登录流程 - 从登录页到菜单显示', async ({ page }) => { console.log('=== 开始超管端完整登录流程测试 ==='); // 设置控制台监听 page.on('console', msg => { const text = msg.text(); // 输出所有路由、认证、菜单相关的日志 if (text.includes('路由') || text.includes('login') || text.includes('menu') || text.includes('auth') || text.includes('fetch') || text.includes('route') || text.includes('router') || text.includes('菜单') || text.includes('路由')) { console.log('浏览器控制台:', msg.type(), text); } }); // 1. 访问平台超管登录页 console.log('步骤 1: 访问平台超管登录页'); await page.goto(`${BASE_URL}/platform/login`); await page.waitForLoadState('networkidle'); // 验证当前 URL expect(page.url()).toContain('/platform/login'); console.log('✓ 成功访问登录页:', page.url()); // 2. 验证登录页面元素存在 console.log('步骤 2: 验证登录页面元素'); // 检查用户名输入框 const usernameInput = page.locator('input[placeholder="请输入用户名"]'); await expect(usernameInput).toBeVisible(); console.log('✓ 用户名输入框可见'); // 检查密码输入框 const passwordInput = page.locator('input[placeholder="请输入密码"]'); await expect(passwordInput).toBeVisible(); console.log('✓ 密码输入框可见'); // 检查登录按钮 const loginButton = page.locator('button[type="submit"]'); await expect(loginButton).toBeVisible(); console.log('✓ 登录按钮可见'); // 3. 填写登录表单 console.log('步骤 3: 填写登录表单'); await usernameInput.fill('admin'); await passwordInput.fill('admin123'); console.log('✓ 已填写用户名和密码'); // 4. 点击登录按钮 console.log('步骤 4: 点击登录按钮'); const [response] = await Promise.all([ page.waitForResponse(res => res.url().includes('/api/auth/login')), loginButton.click() ]); console.log('✓ 登录请求已发送,响应状态:', response.status()); // 5. 等待登录响应 console.log('步骤 5: 等待登录响应'); const responseData = await response.json(); expect(responseData.code).toBe(200); expect(responseData.data.token).toBeTruthy(); console.log('✓ 登录成功,Token 已获取'); // 6. 验证 Token 已存储到 Cookie console.log('步骤 6: 验证 Token 存储'); await page.waitForTimeout(1000); const token = await page.evaluate(() => document.cookie.includes('token=')); const tenantCode = await page.evaluate(() => { const path = window.location.pathname; const match = path.match(/^\/([^/]+)/); return match ? match[1] : null; }); expect(token).toBeTruthy(); expect(tenantCode).toBe('platform'); console.log('✓ Token 已存储到 Cookie'); console.log(` - Cookie 包含 token: ${token}`); console.log(` - 租户编码:${tenantCode}`); // 等待登录完成后的任何异步操作 await page.waitForTimeout(2000); // 检查 authStore.menus 的值 console.log('步骤 6.5: 检查 authStore.menus'); const menusLength = await page.evaluate(() => { // 尝试从 window 对象获取 store 信息 return (window as any).__AUTH_STORE?.menus?.length || 'unknown'; }); console.log('✓ authStore.menus 长度:', menusLength); // 直接检查 store 的 menus const directMenusCheck = await page.evaluate(() => { // 尝试直接访问 store 实例 const stores = (window as any).__VUE_STORES__; if (stores && stores.auth) { return { menusLength: stores.auth.menus?.length || 0, menus: stores.auth.menus }; } return null; }); console.log('✓ 直接检查 store:', directMenusCheck ? `menus=${directMenusCheck.menusLength}` : '无法访问'); // 7. 验证页面跳转到工作台/首页 console.log('步骤 7: 验证页面跳转'); await page.waitForLoadState('networkidle'); // 等待菜单加载 console.log('等待菜单加载...'); await page.waitForTimeout(5000); const currentUrl = page.url(); console.log('✓ 当前 URL:', currentUrl); // 检查是否有 404 const pageContent = await page.content(); if (pageContent.includes('404') || pageContent.includes('抱歉')) { console.log('⚠️ 页面显示 404 错误'); } expect(currentUrl).toContain('/platform'); console.log('✓ 页面已跳转到平台超管端'); // 检查控制台错误 console.log('步骤 7.5: 检查控制台错误'); const errors: string[] = []; page.on('console', msg => { if (msg.type() === 'error') { errors.push(msg.text()); console.log('浏览器控制台错误:', msg.text()); } }); // 8. 验证侧边栏菜单存在 console.log('步骤 8: 验证侧边栏菜单'); await page.waitForTimeout(3000); // 查找侧边栏 const sidebar = page.locator('[class*="sidebar"], [class*="menu"], .ant-menu, [role="navigation"]'); const sidebarVisible = await sidebar.count() > 0; console.log('✓ 侧边栏元素计数:', await sidebar.count()); // 9. 验证菜单项 console.log('步骤 9: 验证菜单项'); const menuItems = page.locator('.ant-menu-item, .ant-menu-submenu, [role="menuitem"], [class*="menu-item"]'); const menuCount = await menuItems.count(); console.log('✓ 菜单项数量:', menuCount); // 10. 验证工作台/仪表盘内容 console.log('步骤 10: 验证工作台内容'); const mainContent = page.locator('main, [class*="content"], [class*="layout"]'); const mainVisible = await mainContent.count() > 0; console.log('✓ 主内容区存在:', mainVisible); // 11. 检查常见菜单项 console.log('步骤 11: 检查常见菜单项文本'); const pageText = await page.content(); const commonMenus = ['用户管理', '角色管理', '权限管理', '菜单管理', '租户管理']; const foundMenus = []; for (const menu of commonMenus) { if (pageText.includes(menu)) { foundMenus.push(menu); console.log(`✓ 找到菜单项:${menu}`); } } console.log('✓ 共找到', foundMenus.length, '个常见菜单项'); // 12. 截图保存 console.log('步骤 12: 截图保存'); await page.screenshot({ path: 'test-results/platform-admin-dashboard.png', fullPage: true }); console.log('✓ 已保存截图到 test-results/platform-admin-dashboard.png'); // 13. 验证用户信息显示 console.log('步骤 13: 验证用户信息显示'); const userInfoText = await page.evaluate(() => { return document.body.innerText; }); // 检查是否包含管理员相关文本(系统管理员或活动列表页面内容) expect(userInfoText).toContain('系统管理员'); console.log('✓ 页面显示用户角色:系统管理员'); console.log('=== 超管端完整登录流程测试通过 ==='); }); test('超管端菜单导航测试', async ({ page }) => { console.log('=== 开始菜单导航测试 ==='); // 先登录 await page.goto(`${BASE_URL}/platform/login`); await page.fill('input[placeholder="请输入用户名"]', 'admin'); await page.fill('input[placeholder="请输入密码"]', 'admin123'); await page.click('button[type="submit"]'); await page.waitForLoadState('networkidle'); await page.waitForTimeout(3000); // 获取所有菜单项 const menuItems = page.locator('.ant-menu-item, .ant-menu-submenu-title'); const menuCount = await menuItems.count(); console.log('✓ 菜单项数量:', menuCount); // 遍历并点击每个菜单项(如果可点击) for (let i = 0; i < Math.min(menuCount, 5); i++) { const menuItem = menuItems.nth(i); const menuItemText = await menuItem.textContent(); console.log(`尝试点击菜单项 ${i + 1}: ${menuItemText?.trim()}`); try { await menuItem.click(); await page.waitForLoadState('networkidle'); await page.waitForTimeout(1000); console.log(`✓ 菜单项 ${i + 1} 点击成功`); } catch (e) { console.log(`○ 菜单项 ${i + 1} 不可点击或需要展开`); } } // 截图 await page.screenshot({ path: 'test-results/platform-admin-menu-navigation.png' }); console.log('✓ 已保存菜单导航截图'); console.log('=== 菜单导航测试完成 ==='); }); test('API 测试 - 获取菜单树', async ({ request }) => { console.log('=== 开始 API 获取菜单树测试 ==='); // 先登录获取 Token const loginResponse = await request.post(`${API_BASE}/api/auth/login`, { data: { username: 'admin', password: 'admin123', tenantCode: 'platform' } }); const loginData = await loginResponse.json(); const token = loginData.data.token; console.log('✓ 登录成功,Token 已获取'); // 获取菜单(注意:后端接口是 /menu/user-menus 或 /menu) const menuResponse = await request.get(`${API_BASE}/api/menu/user-menus`, { headers: { Authorization: `Bearer ${token}` } }); const menuData = await menuResponse.json(); console.log('✓ 菜单 API 响应:', menuData.code === 200 ? '成功' : '失败'); // API 测试 - 打印菜单详细信息 if (menuData.code === 200 && menuData.data) { console.log('✓ 菜单数量:', menuData.data.length); expect(Array.isArray(menuData.data)).toBeTruthy(); // 打印菜单详细信息 const printMenuDetail = (menu: any, level = 0) => { const indent = ' '.repeat(level); console.log(` ${indent}- ${menu.name} (path: ${menu.path}, component: ${menu.component}, id: ${menu.id})`); if (menu.children && menu.children.length > 0) { menu.children.forEach((child: any) => printMenuDetail(child, level + 1)); } }; console.log('✓ 菜单详细结构:'); menuData.data.forEach((menu: any) => printMenuDetail(menu)); } console.log('=== API 获取菜单树测试完成 ==='); }); });