library-picturebook-activity/java-frontend/tests/specs/platform-admin-full-flow.spec.ts
En b805f456a6 feat: 完善后端基础架构和登录功能
- 添加 Lombok 配置支持
- 完善枚举类和常量定义
- 新增工具类(TraceId、限流、OSS 等)
- 添加切面(日志、限流、TraceId)
- 更新数据库索引规范(应用层防重)
- 登录页面样式优化
- 前后端项目文档补充
2026-03-31 13:58:28 +08:00

281 lines
10 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 获取菜单树测试完成 ===');
});
});