import { test, expect, Page } from '@playwright/test'; // 测试数据 const TEST_USERS = { admin: { username: 'admin', password: 'admin123', expectedRole: '超级管理员' }, testUser: { username: 'test_user', password: 'test123', expectedRole: '测试普通用户' }, testAdmin: { username: 'test_admin', password: 'test123', expectedRole: '测试管理员' } }; const BASE_URL = 'http://localhost:3003'; const API_BASE_URL = 'http://localhost:8084'; // 测试辅助函数 async function login(page: Page, username: string, password: string) { await page.goto(`${BASE_URL}/login`); await page.fill('input[placeholder="请输入用户名"]', username); await page.fill('input[placeholder="请输入密码"]', password); await page.click('button:has-text("登录")'); await page.waitForURL(`${BASE_URL}/dashboard`); } async function waitForAPIResponse(page: Page, urlPattern: string) { return page.waitForResponse(response => response.url().includes(urlPattern) && response.status() === 200 ); } // TC-001: 完整登录流程 test.describe('TC-001: 完整登录流程', () => { test('管理员登录成功并验证登录日志', async ({ page }) => { await login(page, TEST_USERS.admin.username, TEST_USERS.admin.password); // 验证登录成功,跳转到首页 await expect(page).toHaveURL(`${BASE_URL}/dashboard`); // 验证Dashboard页面加载成功 await expect(page.locator('text=用户总数')).toBeVisible(); await expect(page.locator('text=角色总数')).toBeVisible(); // 验证登录日志记录 const loginLogResponse = await page.evaluate(async (apiBaseUrl) => { const response = await fetch(`${apiBaseUrl}/api/auth/login-logs`); return response.json(); }, API_BASE_URL); expect(loginLogResponse.data).toBeDefined(); expect(loginLogResponse.data.length).toBeGreaterThan(0); // 验证最新的登录日志 const latestLog = loginLogResponse.data[0]; expect(latestLog.username).toBe(TEST_USERS.admin.username); expect(latestLog.browser).toContain('Chrome'); expect(latestLog.os).toContain('Mac OS X'); }); test('普通用户登录成功', async ({ page }) => { await login(page, TEST_USERS.testUser.username, TEST_USERS.testUser.password); await expect(page).toHaveURL(`${BASE_URL}/dashboard`); await expect(page.locator('text=用户总数')).toBeVisible(); }); test('登录失败 - 错误密码', async ({ page }) => { await page.goto(`${BASE_URL}/login`); await page.fill('input[placeholder="请输入用户名"]', TEST_USERS.admin.username); await page.fill('input[placeholder="请输入密码"]', 'wrongpassword'); await page.click('button:has-text("登录")'); await expect(page.locator('text=用户名或密码错误')).toBeVisible(); }); test('登录失败 - 空用户名', async ({ page }) => { await page.goto(`${BASE_URL}/login`); await page.fill('input[placeholder="请输入密码"]', TEST_USERS.admin.password); await page.click('button:has-text("登录")'); await expect(page.locator('text=请输入用户名')).toBeVisible(); }); test('登录失败 - 空密码', async ({ page }) => { await page.goto(`${BASE_URL}/login`); await page.fill('input[placeholder="请输入用户名"]', TEST_USERS.admin.username); await page.click('button:has-text("登录")'); await expect(page.locator('text=请输入密码')).toBeVisible(); }); }); // TC-002: 角色管理完整流程 test.describe('TC-002: 角色管理完整流程', () => { test.beforeEach(async ({ page }) => { await login(page, TEST_USERS.admin.username, TEST_USERS.admin.password); }); test('查看角色列表 - 验证字段映射', async ({ page }) => { await page.click('text=系统管理'); await page.click('text=角色管理'); // 等待角色列表加载 await page.waitForSelector('table'); // 验证角色列表显示正确的字段 await expect(page.locator('th:has-text("角色名称")')).toBeVisible(); await expect(page.locator('th:has-text("角色标识")')).toBeVisible(); await expect(page.locator('th:has-text("显示顺序")')).toBeVisible(); await expect(page.locator('th:has-text("状态")')).toBeVisible(); // 验证角色数据 const roles = await page.evaluate(async () => { const response = await fetch(`${API_BASE_URL}/api/roles`); const data = await response.json(); return data.data; }); expect(roles).toBeDefined(); expect(roles.length).toBeGreaterThan(0); // 验证字段映射正确性 const firstRole = roles[0]; expect(firstRole.roleName).toBeDefined(); expect(firstRole.roleKey).toBeDefined(); expect(firstRole.roleSort).toBeDefined(); expect(firstRole.status).toBeDefined(); // 验证不包含旧字段 expect(firstRole.name).toBeUndefined(); expect(firstRole.code).toBeUndefined(); expect(firstRole.description).toBeUndefined(); }); test('创建新角色', async ({ page }) => { await page.click('text=系统管理'); await page.click('text=角色管理'); // 点击新建按钮 await page.click('button:has-text("新建")'); // 填写角色信息 const newRoleName = `测试角色_${Date.now()}`; await page.fill('input[placeholder="角色名称"]', newRoleName); await page.fill('input[placeholder="角色标识"]', `test_role_${Date.now()}`); await page.fill('input[placeholder="显示顺序"]', '10'); // 提交表单 await page.click('button:has-text("确定")'); // 验证创建成功 await expect(page.locator('text=创建成功')).toBeVisible(); // 验证角色出现在列表中 await expect(page.locator(`text=${newRoleName}`)).toBeVisible(); }); test('编辑角色', async ({ page }) => { await page.click('text=系统管理'); await page.click('text=角色管理'); // 等待列表加载 await page.waitForSelector('table'); // 点击第一个编辑按钮 const editButtons = await page.locator('button:has-text("编辑")').all(); await editButtons[0].click(); // 修改角色名称 const updatedRoleName = `更新角色_${Date.now()}`; await page.fill('input[placeholder="角色名称"]', updatedRoleName); // 提交修改 await page.click('button:has-text("确定")'); // 验证更新成功 await expect(page.locator('text=更新成功')).toBeVisible(); await expect(page.locator(`text=${updatedRoleName}`)).toBeVisible(); }); test('删除角色', async ({ page }) => { await page.click('text=系统管理'); await page.click('text=角色管理'); // 等待列表加载 await page.waitForSelector('table'); // 点击删除按钮 const deleteButtons = await page.locator('button:has-text("删除")').all(); page.on('dialog', dialog => dialog.accept()); await deleteButtons[0].click(); // 验证删除成功 await expect(page.locator('text=删除成功')).toBeVisible(); }); }); // TC-003: 菜单管理数据验证 test.describe('TC-003: 菜单管理数据验证', () => { test.beforeEach(async ({ page }) => { await login(page, TEST_USERS.admin.username, TEST_USERS.admin.password); }); test('查看菜单树结构', async ({ page }) => { await page.click('text=系统管理'); await page.click('text=菜单管理'); // 等待菜单树加载 await page.waitForSelector('.el-tree'); // 验证一级菜单 await expect(page.locator('text=系统管理')).toBeVisible(); await expect(page.locator('text=审计日志')).toBeVisible(); await expect(page.locator('text=系统监控')).toBeVisible(); // 验证二级菜单 await expect(page.locator('text=用户管理')).toBeVisible(); await expect(page.locator('text=角色管理')).toBeVisible(); await expect(page.locator('text=菜单管理')).toBeVisible(); await expect(page.locator('text=登录日志')).toBeVisible(); }); test('验证菜单字段映射', async ({ page }) => { // 直接调用API验证字段 const menus = await page.evaluate(async () => { const response = await fetch(`${API_BASE_URL}/api/menus`); const data = await response.json(); return data.data; }); expect(menus).toBeDefined(); expect(menus.length).toBeGreaterThan(0); // 验证字段映射正确性 const firstMenu = menus[0]; expect(firstMenu.menuName).toBeDefined(); expect(firstMenu.menuType).toBeDefined(); expect(firstMenu.orderNum).toBeDefined(); expect(firstMenu.component).toBeDefined(); expect(firstMenu.perms).toBeDefined(); }); test('空数据处理', async ({ page }) => { // 模拟空数据场景 await page.evaluate(async () => { // 清空菜单数据(仅用于测试) await fetch(`${API_BASE_URL}/api/menus/clear`, { method: 'DELETE' }); }); await page.click('text=系统管理'); await page.click('text=菜单管理'); // 验证显示空状态提示 await expect(page.locator('text=暂无数据')).toBeVisible(); }); }); // TC-004: 前后端字段映射一致性 test.describe('TC-004: 前后端字段映射一致性', () => { test('验证角色API字段映射', async ({ page }) => { const roles = await page.evaluate(async () => { const response = await fetch(`${API_BASE_URL}/api/roles`); const data = await response.json(); return data.data; }); roles.forEach((role: any) => { expect(role.roleName).toBeDefined(); expect(role.roleKey).toBeDefined(); expect(role.roleSort).toBeDefined(); expect(role.status).toBeDefined(); // 验证不包含旧字段 expect(role.name).toBeUndefined(); expect(role.code).toBeUndefined(); expect(role.description).toBeUndefined(); }); }); test('验证菜单API字段映射', async ({ page }) => { const menus = await page.evaluate(async () => { const response = await fetch(`${API_BASE_URL}/api/menus`); const data = await response.json(); return data.data; }); menus.forEach((menu: any) => { expect(menu.menuName).toBeDefined(); expect(menu.menuType).toBeDefined(); expect(menu.orderNum).toBeDefined(); expect(menu.component).toBeDefined(); expect(menu.perms).toBeDefined(); }); }); test('验证用户API字段映射', async ({ page }) => { const users = await page.evaluate(async () => { const response = await fetch(`${API_BASE_URL}/api/users`); const data = await response.json(); return data.data; }); users.forEach((user: any) => { expect(user.username).toBeDefined(); expect(user.email).toBeDefined(); expect(user.status).toBeDefined(); }); }); }); // TC-005: RBAC权限验证 test.describe('TC-005: RBAC权限验证', () => { test('管理员访问所有功能', async ({ page }) => { await login(page, TEST_USERS.admin.username, TEST_USERS.admin.password); // 验证管理员能访问所有菜单 await expect(page.locator('text=系统管理')).toBeVisible(); await expect(page.locator('text=审计日志')).toBeVisible(); await expect(page.locator('text=系统监控')).toBeVisible(); // 尝试访问各个功能 await page.click('text=系统管理'); await page.click('text=用户管理'); await expect(page).toHaveURL(`${BASE_URL}/system/user`); await page.click('text=系统管理'); await page.click('text=角色管理'); await expect(page).toHaveURL(`${BASE_URL}/system/role`); await page.click('text=审计日志'); await page.click('text=登录日志'); await expect(page).toHaveURL(`${BASE_URL}/audit/loginlog`); }); test('普通用户权限限制', async ({ page }) => { await login(page, TEST_USERS.testUser.username, TEST_USERS.testUser.password); // 验证普通用户只能看到授权的菜单 await expect(page.locator('text=系统管理')).toBeVisible(); await expect(page.locator('text=用户管理')).toBeVisible(); // 尝试访问未授权功能 await page.goto(`${BASE_URL}/system/role`); // 验证被拒绝访问 await expect(page.locator('text=权限不足')).toBeVisible(); }); test('未授权访问返回403', async ({ page }) => { // 直接调用未授权API const response = await page.evaluate(async () => { try { const response = await fetch(`${API_BASE_URL}/api/roles`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer invalid_token' }, body: JSON.stringify({ roleName: 'Test Role', roleKey: 'test_role', roleSort: 1 }) }); return { status: response.status }; } catch (error) { return { status: 0 }; } }); expect(response.status).toBe(403); }); }); // TC-006: 空数据处理 test.describe('TC-006: 空数据处理', () => { test('角色列表空状态', async ({ page }) => { await login(page, TEST_USERS.admin.username, TEST_USERS.admin.password); // 清空角色数据 await page.evaluate(async () => { await fetch(`${API_BASE_URL}/api/roles/clear`, { method: 'DELETE' }); }); await page.click('text=系统管理'); await page.click('text=角色管理'); // 验证空状态提示 await expect(page.locator('text=暂无角色数据')).toBeVisible(); }); test('菜单列表空状态', async ({ page }) => { await login(page, TEST_USERS.admin.username, TEST_USERS.admin.password); // 清空菜单数据 await page.evaluate(async () => { await fetch(`${API_BASE_URL}/api/menus/clear`, { method: 'DELETE' }); }); await page.click('text=系统管理'); await page.click('text=菜单管理'); // 验证空状态提示 await expect(page.locator('text=暂无菜单数据')).toBeVisible(); }); }); // TC-007: 异常输入处理 test.describe('TC-007: 异常输入处理', () => { test('创建角色 - 重复的roleKey', async ({ page }) => { await login(page, TEST_USERS.admin.username, TEST_USERS.admin.password); await page.click('text=系统管理'); await page.click('text=角色管理'); await page.click('button:has-text("新建")'); // 使用已存在的roleKey await page.fill('input[placeholder="角色名称"]', '重复角色'); await page.fill('input[placeholder="角色标识"]', 'admin'); // 已存在的roleKey await page.fill('input[placeholder="显示顺序"]', '10'); await page.click('button:has-text("确定")'); // 验证错误提示 await expect(page.locator('text=角色标识已存在')).toBeVisible(); }); test('创建菜单 - 无效的menuType', async ({ page }) => { await login(page, TEST_USERS.admin.username, TEST_USERS.admin.password); await page.click('text=系统管理'); await page.click('text=菜单管理'); await page.click('button:has-text("新建")'); // 选择无效的菜单类型 await page.fill('input[placeholder="菜单名称"]', '测试菜单'); await page.selectOption('select[placeholder="菜单类型"]', 'X'); // 无效值 await page.click('button:has-text("确定")'); // 验证表单验证 await expect(page.locator('text=请选择有效的菜单类型')).toBeVisible(); }); test('超长字符串输入', async ({ page }) => { await login(page, TEST_USERS.admin.username, TEST_USERS.admin.password); await page.click('text=系统管理'); await page.click('text=角色管理'); await page.click('button:has-text("新建")'); // 输入超长字符串 const longString = 'A'.repeat(1000); await page.fill('input[placeholder="角色名称"]', longString); await page.click('button:has-text("确定")'); // 验证长度限制 await expect(page.locator('text=角色名称长度不能超过50个字符')).toBeVisible(); }); }); // TC-008: 并发操作测试 test.describe('TC-008: 并发操作测试', () => { test('多用户同时编辑角色', async ({ browser }) => { const context1 = await browser.newContext(); const context2 = await browser.newContext(); const page1 = await context1.newPage(); const page2 = await context2.newPage(); // 用户1登录并开始编辑 await login(page1, TEST_USERS.admin.username, TEST_USERS.admin.password); await page1.click('text=系统管理'); await page1.click('text=角色管理'); const editButtons1 = await page1.locator('button:has-text("编辑")').all(); await editButtons1[0].click(); // 用户2登录并尝试编辑同一角色 await login(page2, TEST_USERS.testAdmin.username, TEST_USERS.testAdmin.password); await page2.click('text=系统管理'); await page2.click('text=角色管理'); const editButtons2 = await page2.locator('button:has-text("编辑")').all(); await editButtons2[0].click(); // 用户1提交修改 await page1.fill('input[placeholder="角色名称"]', '并发测试角色1'); await page1.click('button:has-text("确定")'); await expect(page1.locator('text=更新成功')).toBeVisible(); // 用户2提交修改 await page2.fill('input[placeholder="角色名称"]', '并发测试角色2'); await page2.click('button:has-text("确定")'); // 验证系统处理并发请求 const updateSuccess = page2.locator('text=更新成功'); const dataModified = page2.locator('text=数据已被修改'); await Promise.race([ updateSuccess.waitFor({ state: 'visible' }), dataModified.waitFor({ state: 'visible' }) ]); await context1.close(); await context2.close(); }); });