- 添加 Lombok 配置支持 - 完善枚举类和常量定义 - 新增工具类(TraceId、限流、OSS 等) - 添加切面(日志、限流、TraceId) - 更新数据库索引规范(应用层防重) - 登录页面样式优化 - 前后端项目文档补充
281 lines
10 KiB
TypeScript
281 lines
10 KiB
TypeScript
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 获取菜单树测试完成 ===');
|
||
});
|
||
});
|