test: E2E 测试用例更新与新增

- 更新 Page Object 模型适配新字段名
- 新增 UAT 测试套件与 journey 测试用例
- 优化测试辅助工具与数据工厂
- 更新 playwright 认证状态
This commit is contained in:
张翔
2026-05-06 14:17:51 +08:00
parent 0b246b3e24
commit bd21e2d1f7
47 changed files with 1764 additions and 1226 deletions
@@ -21,20 +21,20 @@ test.describe('管理员完整工作流', () => {
await test.step('点击创建角色按钮', async () => {
await page.locator('button:has-text("新增角色")').click();
await page.waitForSelector('.el-dialog', { state: 'visible', timeout: 5000 });
await page.waitForSelector('.ant-modal', { state: 'visible', timeout: 5000 });
});
await test.step('填写角色信息', async () => {
const dialog = page.locator('.el-dialog');
const dialog = page.locator('.ant-modal');
await dialog.locator('input').first().fill(roleName);
await dialog.locator('input').nth(1).fill(roleKey);
await dialog.locator('.el-input-number .el-input__inner').fill('99');
await dialog.locator('.ant-input-number .ant-input__inner').fill('99');
});
await test.step('提交表单', async () => {
await page.locator('.el-dialog button:has-text("确定")').click();
await page.waitForSelector('.el-dialog', { state: 'hidden', timeout: 10000 });
await expect(page.locator('.el-message--success')).toBeVisible({ timeout: 5000 });
await page.locator('.ant-modal button:has-text("确定")').click();
await page.waitForSelector('.ant-modal', { state: 'hidden', timeout: 10000 });
await expect(page.locator('.ant-message-success')).toBeVisible({ timeout: 5000 });
});
});
@@ -51,11 +51,11 @@ test.describe('管理员完整工作流', () => {
await test.step('点击创建用户按钮', async () => {
await page.locator('button:has-text("新增用户")').click();
await page.waitForSelector('.el-dialog', { state: 'visible', timeout: 5000 });
await page.waitForSelector('.ant-modal', { state: 'visible', timeout: 5000 });
});
await test.step('填写用户信息', async () => {
const dialog = page.locator('.el-dialog');
const dialog = page.locator('.ant-modal');
await dialog.locator('input').first().fill(username);
await dialog.locator('input[type="password"]').fill('Test@123');
await dialog.locator('input').nth(2).fill(`测试用户${timestamp}`);
@@ -64,9 +64,9 @@ test.describe('管理员完整工作流', () => {
});
await test.step('提交表单', async () => {
await page.locator('.el-dialog button:has-text("确定")').click();
await page.waitForSelector('.el-dialog', { state: 'hidden', timeout: 10000 });
await expect(page.locator('.el-message--success')).toBeVisible({ timeout: 5000 });
await page.locator('.ant-modal button:has-text("确定")').click();
await page.waitForSelector('.ant-modal', { state: 'hidden', timeout: 10000 });
await expect(page.locator('.ant-message-success')).toBeVisible({ timeout: 5000 });
});
await test.step('搜索新创建的用户', async () => {
@@ -85,13 +85,13 @@ test.describe('管理员完整工作流', () => {
await expect(userRow).toBeVisible({ timeout: 10000 });
await userRow.locator('button:has-text("分配角色")').click();
await page.waitForSelector('.el-dialog:has-text("分配角色")', { state: 'visible', timeout: 5000 });
await page.waitForSelector('.ant-modal:has-text("分配角色")', { state: 'visible', timeout: 5000 });
const transfer = page.locator('.el-transfer');
const leftPanel = transfer.locator('.el-transfer-panel').first();
const rightPanel = transfer.locator('.el-transfer-panel').last();
const transfer = page.locator('.ant-transfer');
const leftPanel = transfer.locator('.ant-transfer-list').first();
const rightPanel = transfer.locator('.ant-transfer-list').last();
const rightPanelItems = await rightPanel.locator('.el-checkbox').all();
const rightPanelItems = await rightPanel.locator('.ant-checkbox').all();
let hasSuperAdminRole = false;
for (const item of rightPanelItems) {
@@ -103,7 +103,7 @@ test.describe('管理员完整工作流', () => {
}
if (!hasSuperAdminRole) {
const leftPanelItems = await leftPanel.locator('.el-checkbox').all();
const leftPanelItems = await leftPanel.locator('.ant-checkbox').all();
let superAdminCheckbox = null;
for (const item of leftPanelItems) {
@@ -121,7 +121,7 @@ test.describe('管理员完整工作流', () => {
await page.waitForTimeout(500);
}
const moveToRightButton = transfer.locator('.el-transfer__buttons button').nth(1);
const moveToRightButton = transfer.locator('.ant-transfer-operation button').nth(1);
if (await moveToRightButton.isEnabled()) {
await moveToRightButton.click();
await page.waitForTimeout(500);
@@ -129,9 +129,9 @@ test.describe('管理员完整工作流', () => {
}
}
await page.locator('.el-dialog:has-text("分配角色") button:has-text("确定")').click();
await page.waitForSelector('.el-dialog:has-text("分配角色")', { state: 'hidden', timeout: 10000 });
await expect(page.locator('.el-message--success').last()).toBeVisible({ timeout: 5000 });
await page.locator('.ant-modal:has-text("分配角色") button:has-text("确定")').click();
await page.waitForSelector('.ant-modal:has-text("分配角色")', { state: 'hidden', timeout: 10000 });
await expect(page.locator('.ant-message-success').last()).toBeVisible({ timeout: 5000 });
});
});
@@ -140,7 +140,7 @@ test.describe('管理员完整工作流', () => {
await page.goto('/dashboard');
await page.waitForLoadState('networkidle');
const avatarButton = page.locator('.el-avatar').first();
const avatarButton = page.locator('.ant-avatar').first();
await avatarButton.click({ timeout: 10000 });
await page.waitForTimeout(500);
@@ -166,7 +166,7 @@ test.describe('管理员完整工作流', () => {
await page.goto('/dashboard');
await page.waitForLoadState('networkidle');
const avatarButton = page.locator('.el-avatar').first();
const avatarButton = page.locator('.ant-avatar').first();
if (await avatarButton.isVisible()) {
await avatarButton.click();
await page.waitForTimeout(500);
@@ -187,7 +187,7 @@ test.describe('管理员完整工作流', () => {
await page.waitForTimeout(1000);
await page.locator('button:has-text("删除")').first().click();
await page.locator('button:has-text("确定")').click();
await expect(page.locator('.el-message--success')).toBeVisible({ timeout: 5000 });
await expect(page.locator('.ant-message-success')).toBeVisible({ timeout: 5000 });
});
await test.step('删除测试角色', async () => {
@@ -197,7 +197,7 @@ test.describe('管理员完整工作流', () => {
await page.waitForTimeout(1000);
await page.locator('button:has-text("删除")').first().click();
await page.locator('button:has-text("确定")').click();
await expect(page.locator('.el-message--success')).toBeVisible({ timeout: 5000 });
await expect(page.locator('.ant-message-success')).toBeVisible({ timeout: 5000 });
});
});
});
@@ -0,0 +1,82 @@
import { test, expect } from '@playwright/test';
import { LoginPage } from '../pages/LoginPage';
import { OperationLogPage } from '../pages/OperationLogPage';
import { LoginLogPage } from '../pages/LoginLogPage';
import { ExceptionLogPage } from '../pages/ExceptionLogPage';
test.describe('User Journey: 审计日志查看', () => {
test.describe.configure({ mode: 'serial' });
test('UJ-08: 操作日志查看与搜索', async ({ page }) => {
const loginPage = new LoginPage(page);
const opLogPage = new OperationLogPage(page);
await test.step('登录', async () => {
await loginPage.goto();
await loginPage.login('admin', 'Test@123');
});
await test.step('导航到操作日志', async () => {
await opLogPage.goto();
});
await test.step('验证日志表格加载', async () => {
await expect(opLogPage.table).toBeVisible({ timeout: 15000 });
});
await test.step('搜索日志', async () => {
await opLogPage.searchByKeyword('admin');
await page.waitForLoadState('networkidle');
});
await test.step('刷新日志', async () => {
await opLogPage.reload();
});
});
test('UJ-09: 登录日志查看', async ({ page }) => {
const loginPage = new LoginPage(page);
const loginLogPage = new LoginLogPage(page);
await test.step('登录', async () => {
await loginPage.goto();
await loginPage.login('admin', 'Test@123');
});
await test.step('导航到登录日志', async () => {
await loginLogPage.goto();
});
await test.step('验证日志表格加载', async () => {
await expect(loginLogPage.table).toBeVisible({ timeout: 15000 });
});
await test.step('搜索日志', async () => {
await loginLogPage.searchByKeyword('admin');
await page.waitForLoadState('networkidle');
});
});
test('UJ-10: 异常日志查看', async ({ page }) => {
const loginPage = new LoginPage(page);
const exLogPage = new ExceptionLogPage(page);
await test.step('登录', async () => {
await loginPage.goto();
await loginPage.login('admin', 'Test@123');
});
await test.step('导航到异常日志', async () => {
await exLogPage.goto();
});
await test.step('验证日志表格加载', async () => {
await expect(exLogPage.table).toBeVisible({ timeout: 15000 });
});
await test.step('搜索日志', async () => {
await exLogPage.search('error');
await page.waitForLoadState('networkidle');
});
});
});
@@ -24,17 +24,17 @@ test.describe('审计工作流', () => {
await page.locator('text=审计日志').click();
await page.waitForTimeout(1000);
await page.locator('.el-menu-item:has-text("操作日志")').click();
await page.locator('.ant-menu-item:has-text("操作日志")').click();
await page.waitForLoadState('networkidle');
await page.waitForTimeout(1000);
await expect(page).toHaveURL(/.*oplog/, { timeout: 10000 });
await expect(page.locator('.el-table')).toBeVisible({ timeout: 10000 });
await expect(page.locator('.ant-table')).toBeVisible({ timeout: 10000 });
});
await test.step('验证操作日志记录', async () => {
await page.waitForTimeout(2000);
const logContent = await page.locator('.el-table').textContent();
const logContent = await page.locator('.ant-table').textContent();
expect(logContent).toMatch(/用户管理|角色管理|菜单管理/);
});
});
@@ -47,7 +47,7 @@ test.describe('审计工作流', () => {
await page.locator('text=审计日志').click();
await page.waitForTimeout(1000);
await page.locator('.el-menu-item:has-text("登录日志")').click();
await page.locator('.ant-menu-item:has-text("登录日志")').click();
await page.waitForLoadState('networkidle');
await page.waitForTimeout(1000);
@@ -55,8 +55,8 @@ test.describe('审计工作流', () => {
});
await test.step('验证登录日志显示', async () => {
await expect(page.locator('.el-table')).toBeVisible({ timeout: 10000 });
const logContent = await page.locator('.el-table').textContent();
await expect(page.locator('.ant-table')).toBeVisible({ timeout: 10000 });
const logContent = await page.locator('.ant-table').textContent();
expect(logContent).toBeTruthy();
expect(logContent.length).toBeGreaterThan(0);
});
@@ -70,24 +70,24 @@ test.describe('审计工作流', () => {
await page.locator('text=审计日志').click();
await page.waitForTimeout(1000);
await page.locator('.el-menu-item:has-text("操作日志")').click();
await page.locator('.ant-menu-item:has-text("操作日志")').click();
await page.waitForLoadState('networkidle');
await page.waitForTimeout(1000);
await expect(page.locator('.el-table')).toBeVisible({ timeout: 10000 });
await expect(page.locator('.ant-table')).toBeVisible({ timeout: 10000 });
});
await test.step('按模块筛选', async () => {
const moduleSelect = page.locator('.el-select:has-text("模块")');
const moduleSelect = page.locator('.ant-select:has-text("模块")');
if (await moduleSelect.isVisible()) {
await moduleSelect.click();
await page.locator('.el-select-dropdown__item:has-text("用户管理")').click();
await page.locator('.ant-select-item:has-text("用户管理")').click();
await page.waitForTimeout(1000);
}
});
await test.step('按时间范围筛选', async () => {
const dateRangePicker = page.locator('.el-date-editor');
const dateRangePicker = page.locator('.ant-picker');
if (await dateRangePicker.isVisible()) {
await dateRangePicker.click();
await page.waitForTimeout(500);
@@ -0,0 +1,80 @@
import { test, expect } from '@playwright/test';
import { LoginPage } from '../pages/LoginPage';
import { SystemConfigPage } from '../pages/SystemConfigPage';
import { DictionaryManagementPage } from '../pages/DictionaryManagementPage';
test.describe('User Journey: 系统配置与字典管理', () => {
test.describe.configure({ mode: 'serial' });
const timestamp = Date.now();
test('UJ-04: 系统配置 CRUD', async ({ page }) => {
const loginPage = new LoginPage(page);
const configPage = new SystemConfigPage(page);
await test.step('登录', async () => {
await loginPage.goto();
await loginPage.login('admin', 'Test@123');
});
await test.step('导航到系统配置', async () => {
await configPage.goto();
});
await test.step('创建配置', async () => {
await configPage.addConfig(
`E2E配置_${timestamp}`,
`e2e.config.key_${timestamp}`,
'e2e_test_value',
'test',
'E2E测试配置'
);
});
await test.step('验证配置已创建', async () => {
const exists = await configPage.containsText(`e2e.config.key_${timestamp}`);
expect(exists).toBe(true);
});
await test.step('编辑配置值', async () => {
await configPage.editConfig(`e2e.config.key_${timestamp}`, 'updated_value');
});
await test.step('删除配置', async () => {
await configPage.deleteConfig(`e2e.config.key_${timestamp}`);
});
});
test('UJ-05: 字典类型与数据 CRUD', async ({ page }) => {
const loginPage = new LoginPage(page);
const dictPage = new DictionaryManagementPage(page);
const dictName = `E2E字典_${timestamp}`;
const dictType = `e2e_dict_${timestamp}`;
await test.step('登录', async () => {
await loginPage.goto();
await loginPage.login('admin', 'Test@123');
});
await test.step('导航到字典管理', async () => {
await dictPage.goto();
});
await test.step('创建字典类型', async () => {
await dictPage.createDictType(dictName, dictType, 1, 'E2E测试字典');
});
await test.step('选择字典类型', async () => {
await dictPage.selectDictType(dictType);
});
await test.step('创建字典数据', async () => {
await dictPage.createDictData('E2E选项A', 'option_a', 1, 1);
});
await test.step('验证字典数据已创建', async () => {
const exists = await dictPage.dataContainsText('E2E选项A');
expect(exists).toBe(true);
});
});
});
@@ -113,11 +113,11 @@ test.describe('系统配置工作流', () => {
const deleteBtn = firstRow.getByRole('button', { name: '删除' });
if (await deleteBtn.isVisible({ timeout: 3000 }).catch(() => false)) {
await deleteBtn.click();
const confirmBtn = page.locator('.el-message-box');
const confirmBtn = page.locator('.ant-message-box');
await confirmBtn.waitFor({ state: 'visible', timeout: 3000 });
await test.step('确认删除', async () => {
const confirmBtn = page.locator('.el-message-box').getByRole('button', { name: '确定' });
const confirmBtn = page.locator('.ant-message-box').getByRole('button', { name: '确定' });
if (await confirmBtn.isVisible({ timeout: 2000 }).catch(() => false)) {
await confirmBtn.click();
await page.waitForLoadState('networkidle');
@@ -125,7 +125,7 @@ test.describe('系统配置工作流', () => {
});
await test.step('验证删除成功', async () => {
const messageBox = page.locator('.el-message-box');
const messageBox = page.locator('.ant-message-box');
await expect(messageBox).not.toBeVisible({ timeout: 5000 });
console.log(`配置已删除`);
});
@@ -111,11 +111,11 @@ test.describe('字典管理工作流', () => {
const deleteBtn = firstRow.getByRole('button', { name: '删除' });
if (await deleteBtn.isVisible({ timeout: 3000 }).catch(() => false)) {
await deleteBtn.click();
const confirmBtn = page.locator('.el-message-box');
const confirmBtn = page.locator('.ant-message-box');
await confirmBtn.waitFor({ state: 'visible', timeout: 3000 });
await test.step('确认删除', async () => {
const confirmBtn = page.locator('.el-message-box').getByRole('button', { name: '确定' });
const confirmBtn = page.locator('.ant-message-box').getByRole('button', { name: '确定' });
if (await confirmBtn.isVisible({ timeout: 2000 }).catch(() => false)) {
await confirmBtn.click();
await page.waitForLoadState('networkidle');
@@ -123,7 +123,7 @@ test.describe('字典管理工作流', () => {
});
await test.step('验证删除成功', async () => {
const messageBox = page.locator('.el-message-box');
const messageBox = page.locator('.ant-message-box');
await expect(messageBox).not.toBeVisible({ timeout: 5000 });
console.log(`字典已删除`);
});
@@ -20,19 +20,19 @@ test.describe('数据字典管理完整工作流', () => {
await test.step('点击新增字典按钮', async () => {
await page.locator('button:has-text("新增字典")').click();
await page.waitForSelector('.el-dialog', { state: 'visible', timeout: 5000 });
await page.waitForSelector('.ant-modal', { state: 'visible', timeout: 5000 });
});
await test.step('填写字典信息', async () => {
const dialog = page.locator('.el-dialog');
const dialog = page.locator('.ant-modal');
await dialog.locator('input').first().fill(dictName);
await dialog.locator('input').nth(1).fill(dictType);
});
await test.step('提交字典表单', async () => {
await page.locator('.el-dialog button:has-text("确定")').click();
await page.waitForSelector('.el-dialog', { state: 'hidden', timeout: 10000 });
await expect(page.locator('.el-message--success')).toBeVisible({ timeout: 5000 });
await page.locator('.ant-modal button:has-text("确定")').click();
await page.waitForSelector('.ant-modal', { state: 'hidden', timeout: 10000 });
await expect(page.locator('.ant-message-success')).toBeVisible({ timeout: 5000 });
});
await test.step('验证字典已创建', async () => {
@@ -57,18 +57,18 @@ test.describe('数据字典管理完整工作流', () => {
await test.step('编辑字典', async () => {
const dictRow = page.locator(`tr:has-text("${dictName}")`);
await dictRow.locator('button:has-text("编辑")').click();
await page.waitForSelector('.el-dialog', { state: 'visible', timeout: 5000 });
await page.waitForSelector('.ant-modal', { state: 'visible', timeout: 5000 });
});
await test.step('修改字典信息', async () => {
const dialog = page.locator('.el-dialog');
const dialog = page.locator('.ant-modal');
await dialog.locator('input').first().fill(updatedName);
});
await test.step('提交更新', async () => {
await page.locator('.el-dialog button:has-text("确定")').click();
await page.waitForSelector('.el-dialog', { state: 'hidden', timeout: 10000 });
await expect(page.locator('.el-message--success')).toBeVisible({ timeout: 5000 });
await page.locator('.ant-modal button:has-text("确定")').click();
await page.waitForSelector('.ant-modal', { state: 'hidden', timeout: 10000 });
await expect(page.locator('.ant-message-success')).toBeVisible({ timeout: 5000 });
});
await test.step('验证字典已更新', async () => {
@@ -91,13 +91,13 @@ test.describe('数据字典管理完整工作流', () => {
await test.step('删除字典', async () => {
const dictRow = page.locator(`tr:has-text("更新字典_${timestamp}")`);
await dictRow.locator('button:has-text("删除")').click();
await page.waitForSelector('.el-message-box', { state: 'visible', timeout: 5000 });
await page.waitForSelector('.ant-message-box', { state: 'visible', timeout: 5000 });
});
await test.step('确认删除', async () => {
await page.locator('.el-message-box button:has-text("确定")').click();
await page.waitForSelector('.el-message-box', { state: 'hidden', timeout: 10000 });
await expect(page.locator('.el-message--success')).toBeVisible({ timeout: 5000 });
await page.locator('.ant-message-box button:has-text("确定")').click();
await page.waitForSelector('.ant-message-box', { state: 'hidden', timeout: 10000 });
await expect(page.locator('.ant-message-success')).toBeVisible({ timeout: 5000 });
});
});
@@ -56,7 +56,7 @@ test.describe('异常日志工作流', () => {
await detailButton.click();
await test.step('验证详情对话框显示', async () => {
const dialog = page.locator('.el-dialog');
const dialog = page.locator('.ant-modal');
await expect(dialog).toBeVisible({ timeout: 5000 });
console.log('异常日志详情对话框已打开');
});
@@ -9,11 +9,11 @@ test.describe('文件管理工作流', () => {
await page.locator('text=系统管理').click();
await page.waitForTimeout(500);
await page.locator('.el-menu-item:has-text("文件管理")').click();
await page.locator('.ant-menu-item:has-text("文件管理")').click();
await page.waitForLoadState('networkidle');
await page.waitForTimeout(1000);
await expect(page.locator('.el-table')).toBeVisible({ timeout: 10000 });
await expect(page.locator('.ant-table')).toBeVisible({ timeout: 10000 });
});
await test.step('上传文件', async () => {
@@ -30,7 +30,7 @@ test.describe('文件管理工作流', () => {
});
await test.step('验证文件上传成功', async () => {
const successMessage = page.locator('.el-message--success');
const successMessage = page.locator('.ant-message-success');
if (await successMessage.isVisible()) {
expect(await successMessage.textContent()).toContain('成功');
}
@@ -54,10 +54,10 @@ test.describe('文件管理工作流', () => {
});
await test.step('按类型筛选', async () => {
const typeFilter = page.locator('.el-select:has-text("类型")');
const typeFilter = page.locator('.ant-select:has-text("类型")');
if (await typeFilter.isVisible()) {
await typeFilter.click();
await page.locator('.el-select-dropdown__item').first().click();
await page.locator('.ant-select-item').first().click();
await page.waitForTimeout(1000);
}
});
@@ -71,7 +71,7 @@ test.describe('文件管理工作流', () => {
});
await test.step('选择文件', async () => {
const fileCheckbox = page.locator('.el-checkbox').first();
const fileCheckbox = page.locator('.ant-checkbox').first();
if (await fileCheckbox.isVisible()) {
await fileCheckbox.click();
}
@@ -0,0 +1,81 @@
import { test, expect } from '@playwright/test';
import { LoginPage } from '../pages/LoginPage';
import { DashboardPage } from '../pages/DashboardPage';
test.describe('User Journey: 登录 → 仪表盘 → 登出', () => {
test('UJ-01: 管理员完整登录登出流程', async ({ page }) => {
const loginPage = new LoginPage(page);
const dashboardPage = new DashboardPage(page);
await test.step('访问登录页面', async () => {
await loginPage.goto();
await expect(page).toHaveURL(/.*login/);
await expect(loginPage.usernameInput).toBeVisible();
await expect(loginPage.passwordInput).toBeVisible();
await expect(loginPage.loginButton).toBeVisible();
});
await test.step('输入凭据并登录', async () => {
await loginPage.login('admin', 'Test@123');
});
await test.step('验证登录成功跳转到仪表盘', async () => {
await expect(page).toHaveURL(/.*dashboard/);
});
await test.step('验证仪表盘内容加载', async () => {
await expect(page.locator('.ant-statistic').first()).toBeVisible({ timeout: 15000 });
});
await test.step('验证侧边菜单可见', async () => {
await expect(page.locator('.ant-menu')).toBeVisible();
});
await test.step('点击头像下拉菜单', async () => {
await page.locator('.ant-avatar').first().click();
await page.waitForTimeout(500);
await expect(page.locator('.ant-dropdown-menu-item:has-text("退出登录")')).toBeVisible();
});
await test.step('点击退出登录', async () => {
await page.locator('.ant-dropdown-menu-item:has-text("退出登录")').click();
await page.waitForURL(/.*login/, { timeout: 10000 });
});
await test.step('验证已跳转到登录页面', async () => {
await expect(page).toHaveURL(/.*login/);
});
});
test('UJ-01b: 登录失败场景', async ({ page }) => {
const loginPage = new LoginPage(page);
await test.step('访问登录页面', async () => {
await loginPage.goto();
});
await test.step('输入错误密码', async () => {
await loginPage.loginAndExpectError('admin', 'wrongpassword');
});
await test.step('验证错误消息显示', async () => {
const errorMsg = await loginPage.getErrorMessage();
expect(errorMsg).not.toBeNull();
});
await test.step('验证仍在登录页面', async () => {
await expect(page).toHaveURL(/.*login/);
});
});
test('UJ-01c: 未登录访问受保护页面重定向到登录', async ({ page }) => {
await test.step('直接访问仪表盘', async () => {
await page.goto('/dashboard');
await page.waitForTimeout(2000);
});
await test.step('验证被重定向到登录页面', async () => {
await expect(page).toHaveURL(/.*login/, { timeout: 10000 });
});
});
});
@@ -0,0 +1,58 @@
import { test, expect } from '@playwright/test';
import { LoginPage } from '../pages/LoginPage';
import { DashboardPage } from '../pages/DashboardPage';
import { MenuManagementPage } from '../pages/MenuManagementPage';
test.describe('User Journey: 菜单管理全链路', () => {
test.describe.configure({ mode: 'serial' });
const timestamp = Date.now();
const menuName = `E2E菜单_${timestamp}`;
test('UJ-03: 菜单创建→编辑→删除', async ({ page }) => {
const loginPage = new LoginPage(page);
const menuPage = new MenuManagementPage(page);
await test.step('登录', async () => {
await loginPage.goto();
await loginPage.login('admin', 'Test@123');
});
await test.step('导航到菜单管理', async () => {
await menuPage.goto();
});
await test.step('创建新菜单', async () => {
await menuPage.clickCreateMenu();
await menuPage.fillMenuForm({
name: menuName,
type: 'menu',
path: `/e2e-test-${timestamp}`,
icon: 'file',
component: `e2e/Test_${timestamp}`,
permission: `e2e:test_${timestamp}`,
sort: 99,
status: 'ACTIVE',
visible: true,
});
await menuPage.submitForm();
const success = await menuPage.containsText(menuName);
expect(success).toBe(true);
});
await test.step('编辑菜单', async () => {
await menuPage.editMenu(menuName);
const modal = page.locator('.ant-modal').filter({ hasText: /编辑菜单/ });
const nameInput = modal.locator('.ant-form-item').filter({ hasText: '菜单名称' }).locator('input');
await nameInput.clear();
await nameInput.fill(`${menuName}_已编辑`);
await menuPage.submitForm();
});
await test.step('删除菜单', async () => {
await menuPage.goto();
await menuPage.deleteMenu(`${menuName}_已编辑`);
await menuPage.confirmDelete();
});
});
});
@@ -111,11 +111,11 @@ test.describe('通知管理工作流', () => {
const deleteBtn = firstRow.getByRole('button', { name: '删除' });
if (await deleteBtn.isVisible({ timeout: 3000 }).catch(() => false)) {
await deleteBtn.click();
const confirmBtn = page.locator('.el-message-box');
const confirmBtn = page.locator('.ant-message-box');
await confirmBtn.waitFor({ state: 'visible', timeout: 3000 });
await test.step('确认删除', async () => {
const confirmBtn = page.locator('.el-message-box').getByRole('button', { name: '确定' });
const confirmBtn = page.locator('.ant-message-box').getByRole('button', { name: '确定' });
if (await confirmBtn.isVisible({ timeout: 2000 }).catch(() => false)) {
await confirmBtn.click();
await page.waitForLoadState('networkidle');
@@ -123,7 +123,7 @@ test.describe('通知管理工作流', () => {
});
await test.step('验证删除成功', async () => {
const messageBox = page.locator('.el-message-box');
const messageBox = page.locator('.ant-message-box');
await expect(messageBox).not.toBeVisible({ timeout: 5000 });
console.log(`通知已删除`);
});
@@ -0,0 +1,65 @@
import { test, expect } from '@playwright/test';
import { LoginPage } from '../pages/LoginPage';
import { NotificationPage } from '../pages/NotificationPage';
import { FileManagementPage } from '../pages/FileManagementPage';
test.describe('User Journey: 通知与文件管理', () => {
test.describe.configure({ mode: 'serial' });
const timestamp = Date.now();
test('UJ-06: 通知管理 CRUD', async ({ page }) => {
const loginPage = new LoginPage(page);
const notifyPage = new NotificationPage(page);
const title = `E2E通知_${timestamp}`;
await test.step('登录', async () => {
await loginPage.goto();
await loginPage.login('admin', 'Test@123');
});
await test.step('导航到通知管理', async () => {
await notifyPage.goto();
});
await test.step('创建通知', async () => {
await notifyPage.addNotification(title, 'E2E测试通知内容', '通知');
});
await test.step('验证通知已创建', async () => {
const exists = await notifyPage.containsText(title);
expect(exists).toBe(true);
});
await test.step('编辑通知', async () => {
await notifyPage.editNotification(title, '更新后的通知内容');
});
await test.step('删除通知', async () => {
await notifyPage.deleteNotification(title);
});
});
test('UJ-07: 文件上传与管理', async ({ page }) => {
const loginPage = new LoginPage(page);
const filePage = new FileManagementPage(page);
await test.step('登录', async () => {
await loginPage.goto();
await loginPage.login('admin', 'Test@123');
});
await test.step('导航到文件管理', async () => {
await filePage.goto();
});
await test.step('验证文件管理页面加载', async () => {
await expect(filePage.uploadButton).toBeVisible({ timeout: 10000 });
await expect(filePage.refreshButton).toBeVisible();
});
await test.step('刷新文件列表', async () => {
await filePage.reload();
});
});
});
@@ -21,20 +21,20 @@ test.describe('系统配置管理完整工作流', () => {
await test.step('点击新增配置按钮', async () => {
await page.locator('button:has-text("新增配置")').click();
await page.waitForSelector('.el-dialog', { state: 'visible', timeout: 5000 });
await page.waitForSelector('.ant-modal', { state: 'visible', timeout: 5000 });
});
await test.step('填写配置信息', async () => {
const dialog = page.locator('.el-dialog');
const dialog = page.locator('.ant-modal');
await dialog.locator('input').first().fill(configName);
await dialog.locator('input').nth(1).fill(configKey);
await dialog.locator('input').nth(2).fill(configValue);
});
await test.step('提交配置表单', async () => {
await page.locator('.el-dialog button:has-text("确定")').click();
await page.waitForSelector('.el-dialog', { state: 'hidden', timeout: 10000 });
await expect(page.locator('.el-message--success')).toBeVisible({ timeout: 5000 });
await page.locator('.ant-modal button:has-text("确定")').click();
await page.waitForSelector('.ant-modal', { state: 'hidden', timeout: 10000 });
await expect(page.locator('.ant-message-success')).toBeVisible({ timeout: 5000 });
});
await test.step('验证配置已创建', async () => {
@@ -59,18 +59,18 @@ test.describe('系统配置管理完整工作流', () => {
await test.step('编辑配置', async () => {
const configRow = page.locator(`tr:has-text("${configName}")`);
await configRow.locator('button:has-text("编辑")').click();
await page.waitForSelector('.el-dialog', { state: 'visible', timeout: 5000 });
await page.waitForSelector('.ant-modal', { state: 'visible', timeout: 5000 });
});
await test.step('修改配置值', async () => {
const dialog = page.locator('.el-dialog');
const dialog = page.locator('.ant-modal');
await dialog.locator('input').nth(2).fill(updatedValue);
});
await test.step('提交更新', async () => {
await page.locator('.el-dialog button:has-text("确定")').click();
await page.waitForSelector('.el-dialog', { state: 'hidden', timeout: 10000 });
await expect(page.locator('.el-message--success')).toBeVisible({ timeout: 5000 });
await page.locator('.ant-modal button:has-text("确定")').click();
await page.waitForSelector('.ant-modal', { state: 'hidden', timeout: 10000 });
await expect(page.locator('.ant-message-success')).toBeVisible({ timeout: 5000 });
});
await test.step('验证配置已更新', async () => {
@@ -93,13 +93,13 @@ test.describe('系统配置管理完整工作流', () => {
await test.step('删除配置', async () => {
const configRow = page.locator(`tr:has-text("${configName}")`);
await configRow.locator('button:has-text("删除")').click();
await page.waitForSelector('.el-message-box', { state: 'visible', timeout: 5000 });
await page.waitForSelector('.ant-message-box', { state: 'visible', timeout: 5000 });
});
await test.step('确认删除', async () => {
await page.locator('.el-message-box button:has-text("确定")').click();
await page.waitForSelector('.el-message-box', { state: 'hidden', timeout: 10000 });
await expect(page.locator('.el-message--success')).toBeVisible({ timeout: 5000 });
await page.locator('.ant-message-box button:has-text("确定")').click();
await page.waitForSelector('.ant-message-box', { state: 'hidden', timeout: 10000 });
await expect(page.locator('.ant-message-success')).toBeVisible({ timeout: 5000 });
});
});
@@ -6,21 +6,21 @@ test.describe('用户权限边界验证', () => {
await page.goto('/users');
await page.waitForLoadState('networkidle');
await expect(page).toHaveURL(/.*users/);
await expect(page.locator('.el-table')).toBeVisible({ timeout: 10000 });
await expect(page.locator('.ant-table')).toBeVisible({ timeout: 10000 });
});
await test.step('验证可以访问角色管理', async () => {
await page.goto('/roles');
await page.waitForLoadState('networkidle');
await expect(page).toHaveURL(/.*roles/);
await expect(page.locator('.el-table')).toBeVisible({ timeout: 10000 });
await expect(page.locator('.ant-table')).toBeVisible({ timeout: 10000 });
});
await test.step('验证可以访问菜单管理', async () => {
await page.goto('/menus');
await page.waitForLoadState('networkidle');
await expect(page).toHaveURL(/.*menus/);
await expect(page.locator('.el-table')).toBeVisible({ timeout: 10000 });
await expect(page.locator('.ant-table')).toBeVisible({ timeout: 10000 });
});
});
@@ -29,7 +29,7 @@ test.describe('用户权限边界验证', () => {
await page.goto('/dashboard');
await page.waitForLoadState('networkidle');
const avatarButton = page.locator('.el-avatar').first();
const avatarButton = page.locator('.ant-avatar').first();
await avatarButton.click({ timeout: 10000 });
await page.waitForTimeout(500);
@@ -68,9 +68,9 @@ test.describe('用户权限边界验证', () => {
if (await createButton.isVisible()) {
await createButton.click();
await page.waitForTimeout(2000);
const errorMessage = page.locator('.el-message--error');
const errorMessage = page.locator('.ant-message-error');
const hasError = await errorMessage.isVisible().catch(() => false);
expect(hasError || await page.locator('.el-dialog').isVisible()).toBeTruthy();
expect(hasError || await page.locator('.ant-modal').isVisible()).toBeTruthy();
}
});
});
@@ -80,7 +80,7 @@ test.describe('用户权限边界验证', () => {
await page.goto('/dashboard');
await page.waitForLoadState('networkidle');
const avatarButton = page.locator('.el-avatar').first();
const avatarButton = page.locator('.ant-avatar').first();
await avatarButton.click({ timeout: 10000 });
await page.waitForTimeout(500);
@@ -0,0 +1,134 @@
import { test, expect } from '@playwright/test';
import { LoginPage } from '../pages/LoginPage';
import { DashboardPage } from '../pages/DashboardPage';
import { UserManagementPage } from '../pages/UserManagementPage';
import { RoleManagementPage } from '../pages/RoleManagementPage';
test.describe('User Journey: 用户与角色管理全链路', () => {
test.describe.configure({ mode: 'serial' });
let loginPage: LoginPage;
let dashboardPage: DashboardPage;
let userPage: UserManagementPage;
let rolePage: RoleManagementPage;
const timestamp = Date.now();
const roleName = `E2E角色_${timestamp}`;
const roleKey = `e2e_role_${timestamp}`;
const username = `e2e_user_${timestamp}`;
test.beforeAll(async ({ browser }) => {
const context = await browser.newContext();
const page = await context.newPage();
loginPage = new LoginPage(page);
await loginPage.goto();
await loginPage.login('admin', 'Test@123');
dashboardPage = new DashboardPage(page);
userPage = new UserManagementPage(page);
rolePage = new RoleManagementPage(page);
});
test('UJ-02a: 创建角色', async () => {
await test.step('导航到角色管理', async () => {
await rolePage.goto();
});
await test.step('点击新增角色', async () => {
await rolePage.clickCreateRole();
});
await test.step('填写角色表单', async () => {
await rolePage.fillRoleForm({
roleName,
roleKey,
roleSort: 99,
status: 'ACTIVE',
});
});
await test.step('提交表单', async () => {
await rolePage.submitForm();
});
await test.step('验证创建成功', async () => {
const success = await rolePage.waitForSuccessMessage();
expect(success).toBe(true);
});
});
test('UJ-02b: 创建用户并分配角色', async () => {
await test.step('导航到用户管理', async () => {
await userPage.goto();
});
await test.step('点击新增用户', async () => {
await userPage.clickCreateUser();
});
await test.step('填写用户表单', async () => {
await userPage.fillUserForm({
username,
password: 'Test@123456',
nickname: `E2E测试用户_${timestamp}`,
email: `e2e_${timestamp}@test.com`,
phone: '13800138000',
});
});
await test.step('提交表单', async () => {
await userPage.submitForm();
});
await test.step('验证创建成功', async () => {
const success = await userPage.waitForSuccessMessage();
expect(success).toBe(true);
});
});
test('UJ-02c: 编辑用户', async () => {
await test.step('导航到用户管理', async () => {
await userPage.goto();
await userPage.waitForTableReady();
});
await test.step('编辑第一个用户', async () => {
await userPage.editUser(1);
});
await test.step('修改昵称', async () => {
const modal = userPage.page.locator('.ant-modal').filter({ hasText: /编辑用户/ });
const nicknameInput = modal.locator('.ant-form-item').filter({ hasText: '昵称' }).locator('input');
await nicknameInput.clear();
await nicknameInput.fill(`修改昵称_${timestamp}`);
});
await test.step('提交修改', async () => {
await userPage.submitForm();
});
await test.step('验证修改成功', async () => {
const success = await userPage.waitForSuccessMessage();
expect(success).toBe(true);
});
});
test('UJ-02d: 删除用户', async () => {
await test.step('导航到用户管理', async () => {
await userPage.goto();
await userPage.waitForTableReady();
});
await test.step('删除最后一个用户', async () => {
const count = await userPage.getUserCount();
if (count > 0) {
await userPage.deleteUser(count);
await userPage.confirmDelete();
}
});
await test.step('验证删除成功', async () => {
const success = await userPage.waitForSuccessMessage();
expect(success).toBe(true);
});
});
});