feat(admin): 添加用户管理相关文件
添加用户管理视图、API和状态管理文件
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
/**
|
||||
* Dashboard页面E2E测试
|
||||
*
|
||||
* 测试仪表盘页面的所有功能和交互
|
||||
*
|
||||
* @tags @dashboard @e2e @view
|
||||
*/
|
||||
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { TestLogger } from '../core/test-logger.js';
|
||||
|
||||
test.describe('E2E: Dashboard页面', () => {
|
||||
let logger: TestLogger;
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
logger = new TestLogger();
|
||||
// 先登录
|
||||
await page.goto('http://localhost:5174/login');
|
||||
await page.fill('input[placeholder="请输入用户名"]', 'admin');
|
||||
await page.fill('input[placeholder="请输入密码"]', 'admin123');
|
||||
await page.click('button:has-text("登录")');
|
||||
await page.waitForURL('**/dashboard');
|
||||
});
|
||||
|
||||
test('应该显示仪表盘内容 @smoke', async ({ page }) => {
|
||||
// Then: 应该显示仪表盘元素
|
||||
await expect(page.locator('.dashboard')).toBeVisible();
|
||||
await expect(page.locator('.el-header')).toBeVisible();
|
||||
await expect(page.locator('.el-aside')).toBeVisible();
|
||||
});
|
||||
|
||||
test('应该显示侧边栏菜单 @smoke', async ({ page }) => {
|
||||
// Then: 应该显示菜单项
|
||||
await expect(page.locator('.el-menu')).toBeVisible();
|
||||
|
||||
// 验证主要菜单项存在
|
||||
const menuItems = ['系统管理', '用户管理', '角色管理', '菜单管理'];
|
||||
for (const item of menuItems) {
|
||||
await expect(page.locator(`.el-menu-item:has-text("${item}")`)).toBeVisible();
|
||||
}
|
||||
});
|
||||
|
||||
test('应该能够导航到用户管理页面 @regression', async ({ page }) => {
|
||||
// When: 点击用户管理菜单
|
||||
await page.click('.el-menu-item:has-text("用户管理")');
|
||||
|
||||
// Then: 应该导航到用户管理页面
|
||||
await page.waitForURL('**/user-management');
|
||||
await expect(page).toHaveURL(/.*user-management/);
|
||||
});
|
||||
|
||||
test('应该能够导航到角色管理页面 @regression', async ({ page }) => {
|
||||
// When: 点击角色管理菜单
|
||||
await page.click('.el-menu-item:has-text("角色管理")');
|
||||
|
||||
// Then: 应该导航到角色管理页面
|
||||
await page.waitForURL('**/role-management');
|
||||
await expect(page).toHaveURL(/.*role-management/);
|
||||
});
|
||||
|
||||
test('应该能够导航到菜单管理页面 @regression', async ({ page }) => {
|
||||
// When: 点击菜单管理菜单
|
||||
await page.click('.el-menu-item:has-text("菜单管理")');
|
||||
|
||||
// Then: 应该导航到菜单管理页面
|
||||
await page.waitForURL('**/menu-management');
|
||||
await expect(page).toHaveURL(/.*menu-management/);
|
||||
});
|
||||
|
||||
test('应该显示用户信息 @regression', async ({ page }) => {
|
||||
// Then: 应该显示用户头像或用户名
|
||||
const avatarVisible = await page.locator('.el-avatar').isVisible().catch(() => false);
|
||||
const usernameVisible = await page.locator('.username').isVisible().catch(() => false);
|
||||
const headerVisible = await page.locator('.el-header').isVisible().catch(() => false);
|
||||
|
||||
// 至少有一个元素可见
|
||||
expect(avatarVisible || usernameVisible || headerVisible).toBe(true);
|
||||
});
|
||||
|
||||
test('应该能够展开/收起侧边栏 @regression', async ({ page }) => {
|
||||
// Given: 侧边栏是展开的
|
||||
const aside = page.locator('.el-aside');
|
||||
const initialWidth = await aside.boundingBox().then(box => box?.width || 200);
|
||||
|
||||
// When: 点击收起按钮(如果存在)
|
||||
const collapseBtn = page.locator('.collapse-btn, .hamburger');
|
||||
if (await collapseBtn.isVisible().catch(() => false)) {
|
||||
await collapseBtn.click();
|
||||
|
||||
// Then: 侧边栏应该收起
|
||||
const collapsedWidth = await aside.boundingBox().then(box => box?.width || 64);
|
||||
expect(collapsedWidth).toBeLessThan(initialWidth);
|
||||
}
|
||||
});
|
||||
|
||||
test('应该能够退出登录 @critical', async ({ page }) => {
|
||||
// When: 点击用户头像或退出按钮
|
||||
const userMenu = page.locator('.el-avatar, .user-menu').first();
|
||||
if (await userMenu.isVisible()) {
|
||||
await userMenu.click();
|
||||
|
||||
// 点击退出登录
|
||||
const logoutBtn = page.locator('.el-dropdown-menu__item:has-text("退出"), button:has-text("退出")');
|
||||
if (await logoutBtn.isVisible().catch(() => false)) {
|
||||
await logoutBtn.click();
|
||||
|
||||
// Then: 应该跳转到登录页面
|
||||
await page.waitForURL('**/login');
|
||||
await expect(page).toHaveURL(/.*login/);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test('应该在不同视口下正常显示 @responsive', async ({ page }) => {
|
||||
// Given: 不同视口尺寸
|
||||
const viewports = [
|
||||
{ width: 1920, height: 1080, name: 'desktop' },
|
||||
{ width: 1366, height: 768, name: 'laptop' },
|
||||
{ width: 768, height: 1024, name: 'tablet' }
|
||||
];
|
||||
|
||||
for (const viewport of viewports) {
|
||||
await page.setViewportSize({ width: viewport.width, height: viewport.height });
|
||||
await page.goto('http://localhost:5174/dashboard');
|
||||
|
||||
// Then: 仪表盘应该可见
|
||||
await expect(page.locator('.dashboard')).toBeVisible();
|
||||
await expect(page.locator('.el-header')).toBeVisible();
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,100 @@
|
||||
/**
|
||||
* Login页面E2E测试
|
||||
*
|
||||
* 测试登录页面的所有功能和交互
|
||||
*
|
||||
* @tags @login @auth @e2e @view
|
||||
*/
|
||||
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { TestLogger } from '../core/test-logger.js';
|
||||
|
||||
test.describe('E2E: Login页面', () => {
|
||||
let logger: TestLogger;
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
logger = new TestLogger();
|
||||
await page.goto('http://localhost:5174/login');
|
||||
await page.waitForLoadState('networkidle');
|
||||
});
|
||||
|
||||
test('应该显示登录表单 @smoke', async ({ page }) => {
|
||||
// Then: 应该显示登录表单元素
|
||||
await expect(page.locator('input[placeholder="请输入用户名"]')).toBeVisible();
|
||||
await expect(page.locator('input[placeholder="请输入密码"]')).toBeVisible();
|
||||
await expect(page.locator('button:has-text("登录")')).toBeVisible();
|
||||
});
|
||||
|
||||
test('应该能够成功登录 @critical', async ({ page }) => {
|
||||
// Given: 输入有效的登录凭据
|
||||
await page.fill('input[placeholder="请输入用户名"]', 'admin');
|
||||
await page.fill('input[placeholder="请输入密码"]', 'admin123');
|
||||
|
||||
// When: 点击登录按钮
|
||||
await page.click('button:has-text("登录")');
|
||||
|
||||
// Then: 应该跳转到仪表盘
|
||||
await page.waitForURL('**/dashboard');
|
||||
await expect(page).toHaveURL(/.*dashboard/);
|
||||
});
|
||||
|
||||
test('应该显示错误消息当凭据无效 @regression', async ({ page }) => {
|
||||
// Given: 输入无效的登录凭据
|
||||
await page.fill('input[placeholder="请输入用户名"]', 'invalid');
|
||||
await page.fill('input[placeholder="请输入密码"]', 'wrongpassword');
|
||||
|
||||
// When: 点击登录按钮
|
||||
await page.click('button:has-text("登录")');
|
||||
|
||||
// Then: 应该显示错误消息
|
||||
await expect(page.locator('.el-message--error')).toBeVisible();
|
||||
});
|
||||
|
||||
test('应该验证必填字段 @regression', async ({ page }) => {
|
||||
// When: 直接点击登录按钮(不输入任何内容)
|
||||
await page.click('button:has-text("登录")');
|
||||
|
||||
// Then: 应该显示验证错误
|
||||
await expect(page.locator('.el-form-item__error')).toBeVisible();
|
||||
});
|
||||
|
||||
test('应该记住用户名当勾选记住我 @regression', async ({ page, context }) => {
|
||||
// Given: 输入凭据并勾选记住我
|
||||
await page.fill('input[placeholder="请输入用户名"]', 'admin');
|
||||
await page.fill('input[placeholder="请输入密码"]', 'admin123');
|
||||
|
||||
// 勾选记住我(如果存在)
|
||||
const rememberMe = page.locator('input[type="checkbox"]').first();
|
||||
if (await rememberMe.isVisible().catch(() => false)) {
|
||||
await rememberMe.check();
|
||||
}
|
||||
|
||||
// When: 登录
|
||||
await page.click('button:has-text("登录")');
|
||||
await page.waitForURL('**/dashboard');
|
||||
|
||||
// Then: 重新打开登录页面应该记住用户名
|
||||
await page.goto('http://localhost:5174/login');
|
||||
const usernameInput = page.locator('input[placeholder="请输入用户名"]');
|
||||
await expect(usernameInput).toHaveValue('admin');
|
||||
});
|
||||
|
||||
test('应该在不同视口下正常显示 @responsive', async ({ page }) => {
|
||||
// Given: 不同视口尺寸
|
||||
const viewports = [
|
||||
{ width: 1920, height: 1080, name: 'desktop' },
|
||||
{ width: 1366, height: 768, name: 'laptop' },
|
||||
{ width: 768, height: 1024, name: 'tablet' },
|
||||
{ width: 375, height: 667, name: 'mobile' }
|
||||
];
|
||||
|
||||
for (const viewport of viewports) {
|
||||
await page.setViewportSize({ width: viewport.width, height: viewport.height });
|
||||
await page.goto('http://localhost:5174/login');
|
||||
|
||||
// Then: 登录表单应该可见
|
||||
await expect(page.locator('input[placeholder="请输入用户名"]')).toBeVisible();
|
||||
await expect(page.locator('input[placeholder="请输入密码"]')).toBeVisible();
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* MenuManagement页面E2E测试
|
||||
*
|
||||
* 测试菜单管理页面的所有功能和交互
|
||||
*
|
||||
* @tags @menu-management @e2e @view
|
||||
*/
|
||||
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { TestLogger } from '../core/test-logger.js';
|
||||
|
||||
test.describe('E2E: MenuManagement页面', () => {
|
||||
let logger: TestLogger;
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
logger = new TestLogger();
|
||||
// 先登录
|
||||
await page.goto('http://localhost:5174/login');
|
||||
await page.fill('input[placeholder="请输入用户名"]', 'admin');
|
||||
await page.fill('input[placeholder="请输入密码"]', 'admin123');
|
||||
await page.click('button:has-text("登录")');
|
||||
await page.waitForURL('**/dashboard');
|
||||
|
||||
// 导航到菜单管理页面
|
||||
await page.click('.el-menu-item:has-text("菜单管理")');
|
||||
await page.waitForURL('**/menu-management');
|
||||
});
|
||||
|
||||
test('应该显示菜单管理页面 @smoke', async ({ page }) => {
|
||||
await expect(page.locator('.menu-management, .el-tree')).toBeVisible();
|
||||
});
|
||||
|
||||
test('应该显示菜单树 @smoke', async ({ page }) => {
|
||||
await expect(page.locator('.el-tree-node')).toHaveCount.greaterThan(0);
|
||||
});
|
||||
|
||||
test('应该能够展开/收起菜单节点 @regression', async ({ page }) => {
|
||||
const expandIcon = page.locator('.el-tree-node__expand-icon').first();
|
||||
if (await expandIcon.isVisible()) {
|
||||
await expandIcon.click();
|
||||
await page.waitForTimeout(300);
|
||||
// 验证子节点显示
|
||||
await expect(page.locator('.el-tree-node__children')).toBeVisible();
|
||||
}
|
||||
});
|
||||
|
||||
test('应该能够打开新增菜单对话框 @critical', async ({ page }) => {
|
||||
const addBtn = page.locator('button:has-text("新增"), .add-btn').first();
|
||||
await addBtn.click();
|
||||
await expect(page.locator('.el-dialog:has-text("新增菜单")')).toBeVisible();
|
||||
});
|
||||
|
||||
test('应该能够创建新菜单 @critical', async ({ page }) => {
|
||||
const addBtn = page.locator('button:has-text("新增"), .add-btn').first();
|
||||
await addBtn.click();
|
||||
await page.fill('.el-dialog input[placeholder*="菜单名称"]', '测试菜单');
|
||||
await page.fill('.el-dialog input[placeholder*="路由路径"]', '/test');
|
||||
const submitBtn = page.locator('.el-dialog button:has-text("确定")').first();
|
||||
await submitBtn.click();
|
||||
await expect(page.locator('.el-message--success')).toBeVisible();
|
||||
});
|
||||
|
||||
test('应该能够编辑菜单 @critical', async ({ page }) => {
|
||||
const editBtn = page.locator('.el-tree-node .el-button:has-text("编辑"), .el-tree-node .edit-btn').first();
|
||||
if (await editBtn.isVisible()) {
|
||||
await editBtn.click();
|
||||
await expect(page.locator('.el-dialog:has-text("编辑菜单")')).toBeVisible();
|
||||
const nameInput = page.locator('.el-dialog input[placeholder*="菜单名称"]').first();
|
||||
await nameInput.clear();
|
||||
await nameInput.fill('更新菜单');
|
||||
const submitBtn = page.locator('.el-dialog button:has-text("确定")').first();
|
||||
await submitBtn.click();
|
||||
await expect(page.locator('.el-message--success')).toBeVisible();
|
||||
}
|
||||
});
|
||||
|
||||
test('应该能够删除菜单 @critical', async ({ page }) => {
|
||||
const deleteBtn = page.locator('.el-tree-node .el-button:has-text("删除"), .el-tree-node .delete-btn').first();
|
||||
if (await deleteBtn.isVisible()) {
|
||||
await deleteBtn.click();
|
||||
await expect(page.locator('.el-message-box:has-text("确认删除")')).toBeVisible();
|
||||
const confirmBtn = page.locator('.el-message-box button:has-text("确定")').first();
|
||||
await confirmBtn.click();
|
||||
await expect(page.locator('.el-message--success')).toBeVisible();
|
||||
}
|
||||
});
|
||||
|
||||
test('应该能够拖拽排序菜单 @regression', async ({ page }) => {
|
||||
const dragNode = page.locator('.el-tree-node__content').first();
|
||||
const targetNode = page.locator('.el-tree-node__content').nth(1);
|
||||
|
||||
if (await dragNode.isVisible() && await targetNode.isVisible()) {
|
||||
await dragNode.dragTo(targetNode);
|
||||
await page.waitForTimeout(500);
|
||||
// 验证排序成功
|
||||
await expect(page.locator('.el-message--success')).toBeVisible();
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* OperationLogManagement页面E2E测试
|
||||
*
|
||||
* 测试操作日志管理页面的所有功能和交互
|
||||
*
|
||||
* @tags @operation-log @e2e @view
|
||||
*/
|
||||
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { TestLogger } from '../core/test-logger.js';
|
||||
|
||||
test.describe('E2E: OperationLogManagement页面', () => {
|
||||
let logger: TestLogger;
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
logger = new TestLogger();
|
||||
// 先登录
|
||||
await page.goto('http://localhost:5174/login');
|
||||
await page.fill('input[placeholder="请输入用户名"]', 'admin');
|
||||
await page.fill('input[placeholder="请输入密码"]', 'admin123');
|
||||
await page.click('button:has-text("登录")');
|
||||
await page.waitForURL('**/dashboard');
|
||||
|
||||
// 导航到操作日志页面
|
||||
await page.click('.el-menu-item:has-text("操作日志")');
|
||||
await page.waitForURL('**/operation-log');
|
||||
});
|
||||
|
||||
test('应该显示操作日志页面 @smoke', async ({ page }) => {
|
||||
await expect(page.locator('.operation-log, .el-table')).toBeVisible();
|
||||
await expect(page.locator('.el-pagination')).toBeVisible();
|
||||
});
|
||||
|
||||
test('应该显示日志列表 @smoke', async ({ page }) => {
|
||||
await expect(page.locator('.el-table__row')).toHaveCount.greaterThan(0);
|
||||
const headers = ['操作人', '操作类型', '操作内容', '操作时间', 'IP地址'];
|
||||
for (const header of headers) {
|
||||
await expect(page.locator(`.el-table__header:has-text("${header}")`)).toBeVisible();
|
||||
}
|
||||
});
|
||||
|
||||
test('应该能够搜索日志 @regression', async ({ page }) => {
|
||||
const searchInput = page.locator('.search-input, input[placeholder*="搜索"]').first();
|
||||
if (await searchInput.isVisible()) {
|
||||
await searchInput.fill('admin');
|
||||
await searchInput.press('Enter');
|
||||
await page.waitForTimeout(500);
|
||||
await expect(page.locator('.el-table__row')).toBeVisible();
|
||||
}
|
||||
});
|
||||
|
||||
test('应该能够按日期筛选 @regression', async ({ page }) => {
|
||||
const datePicker = page.locator('.el-date-picker, .date-range-picker').first();
|
||||
if (await datePicker.isVisible()) {
|
||||
await datePicker.click();
|
||||
// 选择日期范围
|
||||
const startDate = page.locator('.el-picker-panel .available').first();
|
||||
await startDate.click();
|
||||
const endDate = page.locator('.el-picker-panel .available').nth(5);
|
||||
await endDate.click();
|
||||
await page.waitForTimeout(500);
|
||||
await expect(page.locator('.el-table__row')).toBeVisible();
|
||||
}
|
||||
});
|
||||
|
||||
test('应该能够查看日志详情 @regression', async ({ page }) => {
|
||||
const detailBtn = page.locator('.el-table__row .el-button:has-text("详情"), .el-table__row .detail-btn').first();
|
||||
if (await detailBtn.isVisible()) {
|
||||
await detailBtn.click();
|
||||
await expect(page.locator('.el-dialog:has-text("日志详情")')).toBeVisible();
|
||||
await expect(page.locator('.el-dialog .detail-content')).toBeVisible();
|
||||
}
|
||||
});
|
||||
|
||||
test('应该能够导出日志 @regression', async ({ page }) => {
|
||||
const exportBtn = page.locator('button:has-text("导出"), .export-btn').first();
|
||||
if (await exportBtn.isVisible()) {
|
||||
await exportBtn.click();
|
||||
// 验证导出成功提示
|
||||
await expect(page.locator('.el-message--success')).toBeVisible();
|
||||
}
|
||||
});
|
||||
|
||||
test('应该能够分页 @regression', async ({ page }) => {
|
||||
const nextBtn = page.locator('.el-pagination .btn-next').first();
|
||||
if (await nextBtn.isVisible() && await nextBtn.isEnabled()) {
|
||||
await nextBtn.click();
|
||||
await page.waitForTimeout(500);
|
||||
await expect(page.locator('.el-table__row')).toBeVisible();
|
||||
}
|
||||
});
|
||||
|
||||
test('应该能够清空筛选条件 @regression', async ({ page }) => {
|
||||
const resetBtn = page.locator('button:has-text("重置"), .reset-btn').first();
|
||||
if (await resetBtn.isVisible()) {
|
||||
await resetBtn.click();
|
||||
await page.waitForTimeout(500);
|
||||
await expect(page.locator('.el-table__row')).toBeVisible();
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* RoleManagement页面E2E测试
|
||||
*
|
||||
* 测试角色管理页面的所有功能和交互
|
||||
*
|
||||
* @tags @role-management @e2e @view
|
||||
*/
|
||||
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { TestLogger } from '../core/test-logger.js';
|
||||
|
||||
test.describe('E2E: RoleManagement页面', () => {
|
||||
let logger: TestLogger;
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
logger = new TestLogger();
|
||||
// 先登录
|
||||
await page.goto('http://localhost:5174/login');
|
||||
await page.fill('input[placeholder="请输入用户名"]', 'admin');
|
||||
await page.fill('input[placeholder="请输入密码"]', 'admin123');
|
||||
await page.click('button:has-text("登录")');
|
||||
await page.waitForURL('**/dashboard');
|
||||
|
||||
// 导航到角色管理页面
|
||||
await page.click('.el-menu-item:has-text("角色管理")');
|
||||
await page.waitForURL('**/role-management');
|
||||
});
|
||||
|
||||
test('应该显示角色管理页面 @smoke', async ({ page }) => {
|
||||
await expect(page.locator('.role-management, .el-table')).toBeVisible();
|
||||
await expect(page.locator('.el-pagination')).toBeVisible();
|
||||
});
|
||||
|
||||
test('应该显示角色列表 @smoke', async ({ page }) => {
|
||||
await expect(page.locator('.el-table__row')).toHaveCount.greaterThan(0);
|
||||
const headers = ['角色名称', '角色编码', '描述', '状态', '操作'];
|
||||
for (const header of headers) {
|
||||
await expect(page.locator(`.el-table__header:has-text("${header}")`)).toBeVisible();
|
||||
}
|
||||
});
|
||||
|
||||
test('应该能够打开新增角色对话框 @critical', async ({ page }) => {
|
||||
const addBtn = page.locator('button:has-text("新增"), .add-btn').first();
|
||||
await addBtn.click();
|
||||
await expect(page.locator('.el-dialog:has-text("新增角色")')).toBeVisible();
|
||||
});
|
||||
|
||||
test('应该能够创建新角色 @critical', async ({ page }) => {
|
||||
const addBtn = page.locator('button:has-text("新增"), .add-btn').first();
|
||||
await addBtn.click();
|
||||
await page.fill('.el-dialog input[placeholder*="角色名称"]', '测试角色');
|
||||
await page.fill('.el-dialog input[placeholder*="角色编码"]', 'test_role');
|
||||
const submitBtn = page.locator('.el-dialog button:has-text("确定")').first();
|
||||
await submitBtn.click();
|
||||
await expect(page.locator('.el-message--success')).toBeVisible();
|
||||
});
|
||||
|
||||
test('应该能够编辑角色 @critical', async ({ page }) => {
|
||||
const editBtn = page.locator('.el-table__row .el-button:has-text("编辑"), .el-table__row .edit-btn').first();
|
||||
if (await editBtn.isVisible()) {
|
||||
await editBtn.click();
|
||||
await expect(page.locator('.el-dialog:has-text("编辑角色")')).toBeVisible();
|
||||
const nameInput = page.locator('.el-dialog input[placeholder*="角色名称"]').first();
|
||||
await nameInput.clear();
|
||||
await nameInput.fill('更新角色');
|
||||
const submitBtn = page.locator('.el-dialog button:has-text("确定")').first();
|
||||
await submitBtn.click();
|
||||
await expect(page.locator('.el-message--success')).toBeVisible();
|
||||
}
|
||||
});
|
||||
|
||||
test('应该能够删除角色 @critical', async ({ page }) => {
|
||||
const deleteBtn = page.locator('.el-table__row .el-button:has-text("删除"), .el-table__row .delete-btn').first();
|
||||
if (await deleteBtn.isVisible()) {
|
||||
await deleteBtn.click();
|
||||
await expect(page.locator('.el-message-box:has-text("确认删除")')).toBeVisible();
|
||||
const confirmBtn = page.locator('.el-message-box button:has-text("确定")').first();
|
||||
await confirmBtn.click();
|
||||
await expect(page.locator('.el-message--success')).toBeVisible();
|
||||
}
|
||||
});
|
||||
|
||||
test('应该能够分配权限 @critical', async ({ page }) => {
|
||||
const permissionBtn = page.locator('.el-table__row .el-button:has-text("权限"), .el-table__row .permission-btn').first();
|
||||
if (await permissionBtn.isVisible()) {
|
||||
await permissionBtn.click();
|
||||
await expect(page.locator('.el-dialog:has-text("分配权限")')).toBeVisible();
|
||||
await expect(page.locator('.el-tree')).toBeVisible();
|
||||
const submitBtn = page.locator('.el-dialog button:has-text("确定")').first();
|
||||
await submitBtn.click();
|
||||
await expect(page.locator('.el-message--success')).toBeVisible();
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,193 @@
|
||||
/**
|
||||
* UserManagement页面E2E测试
|
||||
*
|
||||
* 测试用户管理页面的所有功能和交互
|
||||
*
|
||||
* @tags @user-management @e2e @view
|
||||
*/
|
||||
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { TestLogger } from '../core/test-logger.js';
|
||||
|
||||
test.describe('E2E: UserManagement页面', () => {
|
||||
let logger: TestLogger;
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
logger = new TestLogger();
|
||||
// 先登录
|
||||
await page.goto('http://localhost:5174/login');
|
||||
await page.fill('input[placeholder="请输入用户名"]', 'admin');
|
||||
await page.fill('input[placeholder="请输入密码"]', 'admin123');
|
||||
await page.click('button:has-text("登录")');
|
||||
await page.waitForURL('**/dashboard');
|
||||
|
||||
// 导航到用户管理页面
|
||||
await page.click('.el-menu-item:has-text("用户管理")');
|
||||
await page.waitForURL('**/user-management');
|
||||
});
|
||||
|
||||
test('应该显示用户管理页面 @smoke', async ({ page }) => {
|
||||
// Then: 应该显示页面元素
|
||||
await expect(page.locator('.user-management, .el-table')).toBeVisible();
|
||||
await expect(page.locator('.el-pagination')).toBeVisible();
|
||||
});
|
||||
|
||||
test('应该显示用户列表 @smoke', async ({ page }) => {
|
||||
// Then: 应该显示用户数据表格
|
||||
await expect(page.locator('.el-table__row')).toHaveCount.greaterThan(0);
|
||||
|
||||
// 验证表头
|
||||
const headers = ['用户名', '邮箱', '手机号', '状态', '操作'];
|
||||
for (const header of headers) {
|
||||
await expect(page.locator(`.el-table__header:has-text("${header}")`)).toBeVisible();
|
||||
}
|
||||
});
|
||||
|
||||
test('应该能够搜索用户 @regression', async ({ page }) => {
|
||||
// Given: 搜索关键词
|
||||
const searchInput = page.locator('.search-input, input[placeholder*="搜索"]').first();
|
||||
|
||||
if (await searchInput.isVisible()) {
|
||||
// When: 输入搜索关键词
|
||||
await searchInput.fill('admin');
|
||||
await searchInput.press('Enter');
|
||||
|
||||
// Then: 应该显示搜索结果
|
||||
await page.waitForTimeout(500);
|
||||
await expect(page.locator('.el-table__row')).toBeVisible();
|
||||
}
|
||||
});
|
||||
|
||||
test('应该能够打开新增用户对话框 @critical', async ({ page }) => {
|
||||
// When: 点击新增按钮
|
||||
const addBtn = page.locator('button:has-text("新增"), .add-btn').first();
|
||||
await addBtn.click();
|
||||
|
||||
// Then: 应该显示新增对话框
|
||||
await expect(page.locator('.el-dialog:has-text("新增用户")')).toBeVisible();
|
||||
await expect(page.locator('.el-dialog input[placeholder*="用户名"]').first()).toBeVisible();
|
||||
});
|
||||
|
||||
test('应该能够创建新用户 @critical', async ({ page }) => {
|
||||
// When: 打开新增对话框
|
||||
const addBtn = page.locator('button:has-text("新增"), .add-btn').first();
|
||||
await addBtn.click();
|
||||
|
||||
// 填写表单
|
||||
await page.fill('.el-dialog input[placeholder*="用户名"]', 'testuser');
|
||||
await page.fill('.el-dialog input[placeholder*="邮箱"]', 'test@example.com');
|
||||
await page.fill('.el-dialog input[placeholder*="手机号"]', '13800138000');
|
||||
|
||||
// 提交表单
|
||||
const submitBtn = page.locator('.el-dialog button:has-text("确定"), .el-dialog .el-button--primary').first();
|
||||
await submitBtn.click();
|
||||
|
||||
// Then: 应该显示成功消息
|
||||
await expect(page.locator('.el-message--success')).toBeVisible();
|
||||
});
|
||||
|
||||
test('应该能够编辑用户 @critical', async ({ page }) => {
|
||||
// When: 点击编辑按钮
|
||||
const editBtn = page.locator('.el-table__row .el-button:has-text("编辑"), .el-table__row .edit-btn').first();
|
||||
if (await editBtn.isVisible()) {
|
||||
await editBtn.click();
|
||||
|
||||
// Then: 应该显示编辑对话框
|
||||
await expect(page.locator('.el-dialog:has-text("编辑用户")')).toBeVisible();
|
||||
|
||||
// 修改数据
|
||||
const usernameInput = page.locator('.el-dialog input[placeholder*="用户名"]').first();
|
||||
await usernameInput.clear();
|
||||
await usernameInput.fill('updateduser');
|
||||
|
||||
// 提交
|
||||
const submitBtn = page.locator('.el-dialog button:has-text("确定")').first();
|
||||
await submitBtn.click();
|
||||
|
||||
// Then: 应该显示成功消息
|
||||
await expect(page.locator('.el-message--success')).toBeVisible();
|
||||
}
|
||||
});
|
||||
|
||||
test('应该能够删除用户 @critical', async ({ page }) => {
|
||||
// When: 点击删除按钮
|
||||
const deleteBtn = page.locator('.el-table__row .el-button:has-text("删除"), .el-table__row .delete-btn').first();
|
||||
if (await deleteBtn.isVisible()) {
|
||||
await deleteBtn.click();
|
||||
|
||||
// Then: 应该显示确认对话框
|
||||
await expect(page.locator('.el-message-box:has-text("确认删除")')).toBeVisible();
|
||||
|
||||
// 确认删除
|
||||
const confirmBtn = page.locator('.el-message-box button:has-text("确定")').first();
|
||||
await confirmBtn.click();
|
||||
|
||||
// Then: 应该显示成功消息
|
||||
await expect(page.locator('.el-message--success')).toBeVisible();
|
||||
}
|
||||
});
|
||||
|
||||
test('应该能够分页 @regression', async ({ page }) => {
|
||||
// When: 点击下一页
|
||||
const nextBtn = page.locator('.el-pagination .btn-next, .el-pagination button:has-text("下一页")').first();
|
||||
if (await nextBtn.isVisible() && await nextBtn.isEnabled()) {
|
||||
await nextBtn.click();
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
// Then: 应该显示不同数据
|
||||
await expect(page.locator('.el-table__row')).toBeVisible();
|
||||
}
|
||||
});
|
||||
|
||||
test('应该能够重置搜索条件 @regression', async ({ page }) => {
|
||||
// Given: 输入搜索条件
|
||||
const searchInput = page.locator('.search-input, input[placeholder*="搜索"]').first();
|
||||
if (await searchInput.isVisible()) {
|
||||
await searchInput.fill('test');
|
||||
|
||||
// When: 点击重置按钮
|
||||
const resetBtn = page.locator('button:has-text("重置"), .reset-btn').first();
|
||||
if (await resetBtn.isVisible()) {
|
||||
await resetBtn.click();
|
||||
|
||||
// Then: 搜索条件应该被清空
|
||||
await expect(searchInput).toHaveValue('');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test('应该能够批量删除 @regression', async ({ page }) => {
|
||||
// When: 选择多个用户
|
||||
const checkboxes = page.locator('.el-table__row .el-checkbox__input').slice(0, 2);
|
||||
for (const checkbox of await checkboxes.all()) {
|
||||
await checkbox.click();
|
||||
}
|
||||
|
||||
// 点击批量删除
|
||||
const batchDeleteBtn = page.locator('button:has-text("批量删除")').first();
|
||||
if (await batchDeleteBtn.isVisible()) {
|
||||
await batchDeleteBtn.click();
|
||||
|
||||
// Then: 应该显示确认对话框
|
||||
await expect(page.locator('.el-message-box:has-text("确认")')).toBeVisible();
|
||||
}
|
||||
});
|
||||
|
||||
test('应该在不同视口下正常显示 @responsive', async ({ page }) => {
|
||||
// Given: 不同视口尺寸
|
||||
const viewports = [
|
||||
{ width: 1920, height: 1080, name: 'desktop' },
|
||||
{ width: 1366, height: 768, name: 'laptop' },
|
||||
{ width: 1024, height: 768, name: 'tablet' }
|
||||
];
|
||||
|
||||
for (const viewport of viewports) {
|
||||
await page.setViewportSize({ width: viewport.width, height: viewport.height });
|
||||
await page.goto('http://localhost:5174/user-management');
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
// Then: 用户列表应该可见
|
||||
await expect(page.locator('.el-table')).toBeVisible();
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user