refactor(security): 重构安全配置并优化测试环境

- 移除旧的测试套件和UAT测试文件
- 更新密码编码器配置使用BCrypt strength=12
- 添加用户角色关联表和相关服务
- 优化前端日期显示格式
- 清理无用资源和配置文件
- 增强测试数据管理和清理功能
This commit is contained in:
张翔
2026-03-27 13:00:22 +08:00
parent ce30893a96
commit af44c23f21
294 changed files with 16057 additions and 22601 deletions
+41 -23
View File
@@ -11,18 +11,20 @@ export class DashboardPage {
readonly fileManagementLink: Locator;
readonly operationLogLink: Locator;
readonly loginLogLink: Locator;
readonly dictionaryLink: Locator;
constructor(page: Page) {
this.page = page;
this.userInfo = page.locator('.el-avatar');
this.userManagementLink = page.getByRole('menuitem', { name: '用户管理' });
this.roleManagementLink = page.getByRole('menuitem', { name: '角色管理' });
this.menuManagementLink = page.getByRole('menuitem', { name: '菜单管理' });
this.systemConfigLink = page.getByRole('menuitem', { name: '参数配置' });
this.noticeManagementLink = page.getByRole('menuitem', { name: '通知公告' });
this.fileManagementLink = page.getByRole('menuitem', { name: '文件列表' });
this.operationLogLink = page.getByRole('menuitem', { name: '操作日志' });
this.loginLogLink = page.getByRole('menuitem', { name: '登录日志' });
this.userManagementLink = page.locator('.el-menu-item:has-text("用户管理")');
this.roleManagementLink = page.locator('.el-menu-item:has-text("角色管理")');
this.menuManagementLink = page.locator('.el-menu-item:has-text("菜单管理")');
this.systemConfigLink = page.locator('.el-menu-item:has-text("参数配置")');
this.noticeManagementLink = page.locator('.el-menu-item:has-text("通知公告")');
this.fileManagementLink = page.locator('.el-menu-item:has-text("文件列表")');
this.operationLogLink = page.locator('.el-menu-item:has-text("操作日志")');
this.loginLogLink = page.locator('.el-menu-item:has-text("登录日志")');
this.dictionaryLink = page.locator('.el-menu-item:has-text("字典管理")');
}
async goto() {
@@ -31,55 +33,55 @@ export class DashboardPage {
}
async navigateToUserManagement() {
const systemMenu = this.page.locator('text=系统管理');
const systemMenu = this.page.locator('.el-sub-menu__title:has-text("系统管理")');
await systemMenu.click();
await this.page.waitForTimeout(500);
await this.userManagementLink.click();
await this.page.waitForURL('**/users');
await this.page.waitForURL('**/users', { timeout: 10000 });
}
async navigateToRoleManagement() {
const systemMenu = this.page.locator('text=系统管理');
const systemMenu = this.page.locator('.el-sub-menu__title:has-text("系统管理")');
await systemMenu.click();
await this.page.waitForTimeout(500);
await this.roleManagementLink.click();
await this.page.waitForURL('**/roles');
await this.page.waitForURL('**/roles', { timeout: 10000 });
}
async navigateToMenuManagement() {
const systemMenu = this.page.locator('text=系统管理');
const systemMenu = this.page.locator('.el-sub-menu__title:has-text("系统管理")');
await systemMenu.click();
await this.page.waitForTimeout(500);
await this.menuManagementLink.click();
await this.page.waitForURL('**/menus');
await this.page.waitForURL('**/menus', { timeout: 10000 });
}
async navigateToSystemConfig() {
const configMenu = this.page.locator('text=系统配置');
const configMenu = this.page.locator('.el-sub-menu__title:has-text("系统配置")');
await configMenu.click();
await this.page.waitForTimeout(500);
await this.systemConfigLink.click();
await this.page.waitForURL('**/sysconfig');
await this.page.waitForURL('**/sys/config', { timeout: 10000 });
}
async navigateToNoticeManagement() {
const notifyMenu = this.page.locator('text=通知中心');
const notifyMenu = this.page.locator('.el-sub-menu__title:has-text("通知中心")');
await notifyMenu.click();
await this.page.waitForTimeout(500);
await this.noticeManagementLink.click();
await this.page.waitForURL('**/notice');
await this.page.waitForURL('**/notice', { timeout: 10000 });
}
async navigateToFileManagement() {
const fileMenu = this.page.locator('text=文件管理');
const fileMenu = this.page.locator('.el-sub-menu__title:has-text("文件管理")');
await fileMenu.click();
await this.page.waitForTimeout(500);
await this.fileManagementLink.click();
await this.page.waitForURL('**/files');
await this.page.waitForURL('**/files', { timeout: 10000 });
}
async navigateToAudit() {
const auditMenu = this.page.locator('text=审计中心');
const auditMenu = this.page.locator('.el-sub-menu__title:has-text("审计中心")');
await auditMenu.click();
await this.page.waitForTimeout(500);
}
@@ -87,13 +89,29 @@ export class DashboardPage {
async navigateToOperationLog() {
await this.navigateToAudit();
await this.operationLogLink.click();
await this.page.waitForURL('**/oplog');
await this.page.waitForURL('**/oplog', { timeout: 10000 });
}
async navigateToLoginLog() {
await this.navigateToAudit();
await this.loginLogLink.click();
await this.page.waitForURL('**/loginlog');
await this.page.waitForURL('**/loginlog', { timeout: 10000 });
}
async navigateToNotification() {
const notifyMenu = this.page.locator('.el-sub-menu__title:has-text("通知中心")');
await notifyMenu.click();
await this.page.waitForTimeout(500);
await this.noticeManagementLink.click();
await this.page.waitForURL('**/notification', { timeout: 10000 });
}
async navigateToDictionary() {
const configMenu = this.page.locator('.el-sub-menu__title:has-text("系统配置")');
await configMenu.click();
await this.page.waitForTimeout(500);
await this.dictionaryLink.click();
await this.page.waitForURL('**/dict', { timeout: 10000 });
}
async getUsername(): Promise<string | null> {
@@ -1,90 +1,183 @@
import { Page, expect } from '@playwright/test';
import { Page, Locator } from '@playwright/test';
export class DictionaryManagementPage {
readonly page: Page;
readonly table;
readonly addButton;
readonly editButton;
readonly deleteButton;
readonly saveButton;
readonly cancelButton;
readonly searchInput;
readonly searchButton;
readonly dictNameInput;
readonly dictTypeInput;
readonly dictStatusSelect;
readonly table: Locator;
readonly createDictTypeButton: Locator;
readonly createDictDataButton: Locator;
readonly searchInput: Locator;
readonly searchButton: Locator;
readonly successMessage: Locator;
readonly dictTypeTable: Locator;
readonly dictDataTable: Locator;
constructor(page: Page) {
this.page = page;
this.table = page.locator('.el-table');
this.addButton = page.getByRole('button', { name: '新增字典' });
this.editButton = page.getByRole('button', { name: '编辑' });
this.deleteButton = page.getByRole('button', { name: '删除' });
this.saveButton = page.getByRole('button', { name: '确定' });
this.cancelButton = page.getByRole('button', { name: '取消' });
this.searchInput = page.getByPlaceholder('搜索字典名称');
this.searchButton = page.getByRole('button', { name: '搜索' });
this.dictNameInput = page.getByPlaceholder('请输入字典名称');
this.dictTypeInput = page.getByPlaceholder('请输入字典类型');
this.dictStatusSelect = page.locator('.el-select');
this.table = page.locator('.el-table').or(page.locator('.dict-table'));
this.createDictTypeButton = page.getByRole('button', { name: '新增字典类型' }).or(page.locator('button:has-text("新增字典类型")'));
this.createDictDataButton = page.getByRole('button', { name: '新增字典数据' }).or(page.locator('button:has-text("新增字典数据")'));
this.searchInput = page.locator('input[placeholder*="搜索"]').or(page.locator('input[name*="keyword"]'));
this.searchButton = page.getByRole('button', { name: '搜索' }).or(page.locator('button:has-text("搜索")'));
this.successMessage = page.locator('.el-message--success').or(page.locator('.success-message'));
this.dictTypeTable = page.locator('.dict-type-table').or(page.locator('.el-table').first());
this.dictDataTable = page.locator('.dict-data-table').or(page.locator('.el-table').nth(1));
}
async goto() {
await this.page.goto('/system/dict');
await this.page.goto('/dict');
await this.page.waitForLoadState('networkidle');
}
async addDictionary(dictName: string, dictType: string, status: string = '0') {
await this.addButton.click();
await this.dictNameInput.fill(dictName);
await this.dictTypeInput.fill(dictType);
await this.saveButton.click();
await this.page.waitForLoadState('networkidle');
async clickCreateDictType() {
await this.createDictTypeButton.click();
await this.page.waitForTimeout(500);
}
async editDictionary(dictType: string, newName: string) {
const row = this.table.locator('tr').filter({ hasText: dictType }).first();
await row.locator('.el-button--primary').click();
await this.dictNameInput.clear();
await this.dictNameInput.fill(newName);
await this.saveButton.click();
await this.page.waitForLoadState('networkidle');
async clickCreateDictData() {
await this.createDictDataButton.click();
await this.page.waitForTimeout(500);
}
async deleteDictionary(dictType: string) {
const row = this.table.locator('tr').filter({ hasText: dictType }).first();
await row.locator('.el-button--danger').click();
async fillDictTypeForm(dictTypeData: {
dictName: string;
dictType: string;
status?: string;
remark?: string;
}) {
const dialog = this.page.locator('.el-dialog');
await this.saveButton.click();
await this.page.waitForLoadState('networkidle');
await dialog.locator('input').first().fill(dictTypeData.dictName);
await dialog.locator('input').nth(1).fill(dictTypeData.dictType);
if (dictTypeData.status) {
const statusRadio = dialog.locator(`input[value="${dictTypeData.status}"]`);
if (await statusRadio.count() > 0) {
await statusRadio.check();
}
}
if (dictTypeData.remark) {
const remarkInput = dialog.locator('textarea');
if (await remarkInput.count() > 0) {
await remarkInput.fill(dictTypeData.remark);
}
}
}
async searchDictionary(keyword: string) {
async fillDictDataForm(dictData: {
dictLabel: string;
dictValue: string;
dictType?: string;
cssClass?: string;
listClass?: string;
isDefault?: string;
status?: string;
sort?: number;
}) {
const dialog = this.page.locator('.el-dialog');
await dialog.locator('input').first().fill(dictData.dictLabel);
await dialog.locator('input').nth(1).fill(dictData.dictValue);
if (dictData.dictType) {
const dictTypeSelect = dialog.locator('.el-select');
if (await dictTypeSelect.count() > 0) {
await dictTypeSelect.click();
await this.page.waitForTimeout(300);
await this.page.getByRole('option', { name: dictData.dictType }).click();
}
}
if (dictData.cssClass) {
const cssClassInput = dialog.locator('input[placeholder*="CSS"]');
if (await cssClassInput.count() > 0) {
await cssClassInput.fill(dictData.cssClass);
}
}
if (dictData.listClass) {
const listClassInput = dialog.locator('input[placeholder*="列表"]');
if (await listClassInput.count() > 0) {
await listClassInput.fill(dictData.listClass);
}
}
if (dictData.isDefault) {
const defaultRadio = dialog.locator(`input[value="${dictData.isDefault}"]`);
if (await defaultRadio.count() > 0) {
await defaultRadio.check();
}
}
if (dictData.status) {
const statusRadio = dialog.locator(`input[value="${dictData.status}"]`);
if (await statusRadio.count() > 0) {
await statusRadio.check();
}
}
if (dictData.sort !== undefined) {
const sortInput = dialog.locator('input[type="number"]');
if (await sortInput.count() > 0) {
await sortInput.fill(String(dictData.sort));
}
}
}
async submitForm() {
await this.page.getByRole('button', { name: '确定' }).or(this.page.locator('button:has-text("确定")')).click();
}
async editDictType(dictName: string) {
const dictTypeRow = this.dictTypeTable.locator('tbody tr').filter({ hasText: dictName });
await dictTypeRow.getByRole('button', { name: '编辑' }).or(this.page.locator('.edit-button')).click();
}
async editDictData(dictLabel: string) {
const dictDataRow = this.dictDataTable.locator('tbody tr').filter({ hasText: dictLabel });
await dictDataRow.getByRole('button', { name: '编辑' }).or(this.page.locator('.edit-button')).click();
}
async deleteDictType(dictName: string) {
const dictTypeRow = this.dictTypeTable.locator('tbody tr').filter({ hasText: dictName });
await dictTypeRow.getByRole('button', { name: '删除' }).or(this.page.locator('.delete-button')).click();
}
async deleteDictData(dictLabel: string) {
const dictDataRow = this.dictDataTable.locator('tbody tr').filter({ hasText: dictLabel });
await dictDataRow.getByRole('button', { name: '删除' }).or(this.page.locator('.delete-button')).click();
}
async confirmDelete() {
await this.page.getByRole('button', { name: '确定' }).or(this.page.locator('.confirm-dialog .confirm-button')).click();
}
async search(keyword: string) {
await this.searchInput.fill(keyword);
await this.searchButton.click();
await this.page.waitForLoadState('networkidle');
}
async clearSearch() {
await this.searchInput.clear();
await this.searchButton.click();
await this.page.waitForLoadState('networkidle');
async containsText(text: string): Promise<boolean> {
return await this.table.getByText(text).count() > 0;
}
async verifyTableContains(text: string) {
await expect(this.table).toContainText(text);
async isSuccessMessageVisible(): Promise<boolean> {
try {
return await this.successMessage.isVisible({ timeout: 3000 });
} catch {
return false;
}
}
async verifyTableNotContains(text: string) {
await expect(this.table).not.toContainText(text);
async getDictTypeCount(): Promise<number> {
return await this.dictTypeTable.locator('tbody tr').count();
}
async getTableRowCount() {
const rows = await this.table.locator('.el-table__row').count();
return rows;
async getDictDataCount(): Promise<number> {
return await this.dictDataTable.locator('tbody tr').count();
}
}
async reload() {
await this.page.reload();
}
}
@@ -0,0 +1,89 @@
import { Page, Locator } from '@playwright/test';
export class ExceptionLogPage {
readonly page: Page;
readonly table: Locator;
readonly searchInput: Locator;
readonly searchButton: Locator;
readonly exportButton: Locator;
readonly refreshButton: Locator;
readonly detailButton: Locator;
readonly successMessage: Locator;
constructor(page: Page) {
this.page = page;
this.table = page.locator('.el-table').or(page.locator('.exception-log-table'));
this.searchInput = page.locator('input[placeholder*="搜索"]').or(page.locator('input[name*="keyword"]'));
this.searchButton = page.getByRole('button', { name: '搜索' }).or(page.locator('button:has-text("搜索")'));
this.exportButton = page.getByRole('button', { name: '导出' }).or(page.locator('button:has-text("导出")'));
this.refreshButton = page.getByRole('button', { name: '刷新' }).or(page.locator('button:has-text("刷新")'));
this.detailButton = page.getByRole('button', { name: '详情' }).or(page.locator('.detail-button'));
this.successMessage = page.locator('.el-message--success').or(page.locator('.success-message'));
}
async goto() {
await this.page.goto('/exceptionlog');
await this.page.waitForLoadState('networkidle');
}
async search(keyword: string) {
await this.searchInput.fill(keyword);
await this.searchButton.click();
await this.page.waitForTimeout(1000);
}
async clearSearch() {
await this.searchInput.fill('');
await this.searchButton.click();
await this.page.waitForTimeout(1000);
}
async exportData() {
await this.exportButton.click();
}
async refresh() {
await this.refreshButton.click();
await this.page.waitForLoadState('networkidle');
}
async viewDetail(exceptionId: string) {
const exceptionRow = this.table.locator('tbody tr').filter({ hasText: exceptionId });
await exceptionRow.locator('.detail-button').or(this.page.getByRole('button', { name: '详情' })).click();
}
async closeDetailDialog() {
await this.page.getByRole('button', { name: '关闭' }).or(this.page.locator('.el-dialog .close-button')).click();
}
async containsText(text: string): Promise<boolean> {
return await this.table.getByText(text).count() > 0;
}
async getTableRowCount(): Promise<number> {
return await this.table.locator('tbody tr').count();
}
async isSuccessMessageVisible(): Promise<boolean> {
try {
return await this.successMessage.isVisible({ timeout: 3000 });
} catch {
return false;
}
}
async reload() {
await this.page.reload();
}
async verifyTableContains(text: string): Promise<void> {
const contains = await this.containsText(text);
if (!contains) {
throw new Error(`Table does not contain text: ${text}`);
}
}
async getLogCount(): Promise<number> {
return await this.table.locator('tbody tr').count();
}
}
@@ -72,4 +72,24 @@ export class FileManagementPage {
const rows = await this.table.locator('.el-table__row').count();
return rows;
}
async clickUploadButton() {
await this.uploadButton.waitFor({ state: 'visible', timeout: 10000 });
await this.uploadButton.click();
}
async submitUpload() {
const confirmButton = this.page.getByRole('button', { name: '确定' }).or(this.page.locator('.el-dialog .el-button--primary'));
await confirmButton.click();
}
async clickDeleteButton(rowNumber: number) {
const row = this.table.locator(`tbody tr:nth-child(${rowNumber})`);
await row.locator('.el-button--danger').click();
}
async clickDownloadButton(rowNumber: number) {
const row = this.table.locator(`tbody tr:nth-child(${rowNumber})`);
await row.locator('.el-button--primary').first().click();
}
}
@@ -41,7 +41,14 @@ export class LoginPage {
console.log('Login failed or timeout:', error);
const currentUrl = this.page.url();
console.log('Current URL:', currentUrl);
const errorMessage = await this.getErrorMessage();
if (errorMessage) {
console.log('Login error message:', errorMessage);
}
await this.page.waitForTimeout(1000);
throw error;
}
}
@@ -0,0 +1,152 @@
import { Page, Locator } from '@playwright/test';
export class MenuManagementPage {
readonly page: Page;
readonly table: Locator;
readonly createMenuButton: Locator;
readonly searchInput: Locator;
readonly searchButton: Locator;
readonly successMessage: Locator;
readonly treeContainer: Locator;
readonly expandAllButton: Locator;
readonly collapseAllButton: Locator;
constructor(page: Page) {
this.page = page;
this.table = page.locator('.el-table').or(page.locator('.menu-table'));
this.createMenuButton = page.getByRole('button', { name: '新增菜单' }).or(page.locator('button:has-text("新增菜单")'));
this.searchInput = page.locator('input[placeholder*="搜索"]').or(page.locator('input[name*="keyword"]'));
this.searchButton = page.getByRole('button', { name: '搜索' }).or(page.locator('button:has-text("搜索")'));
this.successMessage = page.locator('.el-message--success').or(page.locator('.success-message'));
this.treeContainer = page.locator('.el-tree').or(page.locator('.menu-tree'));
this.expandAllButton = page.getByRole('button', { name: '展开全部' }).or(page.locator('button:has-text("展开全部")'));
this.collapseAllButton = page.getByRole('button', { name: '折叠全部' }).or(page.locator('button:has-text("折叠全部")'));
}
async goto() {
await this.page.goto('/menus');
await this.page.waitForLoadState('networkidle');
}
async clickCreateMenu() {
await this.createMenuButton.click();
await this.page.waitForTimeout(500);
}
async fillMenuForm(menuData: {
menuName: string;
menuType?: string;
path?: string;
component?: string;
permission?: string;
sort?: number;
visible?: string;
status?: string;
}) {
const dialog = this.page.locator('.el-dialog');
await dialog.locator('input').first().fill(menuData.menuName);
if (menuData.menuType) {
const menuTypeSelect = dialog.locator('.el-select').first();
await menuTypeSelect.click();
await this.page.waitForTimeout(300);
await this.page.getByRole('option', { name: menuData.menuType }).click();
}
if (menuData.path) {
const pathInput = dialog.locator('input[placeholder*="路径"]');
if (await pathInput.count() > 0) {
await pathInput.fill(menuData.path);
}
}
if (menuData.component) {
const componentInput = dialog.locator('input[placeholder*="组件"]');
if (await componentInput.count() > 0) {
await componentInput.fill(menuData.component);
}
}
if (menuData.permission) {
const permissionInput = dialog.locator('input[placeholder*="权限"]');
if (await permissionInput.count() > 0) {
await permissionInput.fill(menuData.permission);
}
}
if (menuData.sort !== undefined) {
const sortInput = dialog.locator('input[type="number"]');
if (await sortInput.count() > 0) {
await sortInput.fill(String(menuData.sort));
}
}
if (menuData.visible) {
const visibleRadio = dialog.locator(`input[value="${menuData.visible}"]`);
if (await visibleRadio.count() > 0) {
await visibleRadio.check();
}
}
if (menuData.status) {
const statusRadio = dialog.locator(`input[value="${menuData.status}"]`);
if (await statusRadio.count() > 0) {
await statusRadio.check();
}
}
}
async submitForm() {
await this.page.getByRole('button', { name: '确定' }).or(this.page.locator('button:has-text("确定")')).click();
}
async editMenu(menuName: string) {
const menuRow = this.table.locator('tbody tr').filter({ hasText: menuName });
await menuRow.getByRole('button', { name: '编辑' }).or(this.page.locator('.edit-button')).click();
}
async deleteMenu(menuName: string) {
const menuRow = this.table.locator('tbody tr').filter({ hasText: menuName });
await menuRow.getByRole('button', { name: '删除' }).or(this.page.locator('.delete-button')).click();
}
async confirmDelete() {
await this.page.getByRole('button', { name: '确定' }).or(this.page.locator('.confirm-dialog .confirm-button')).click();
}
async search(keyword: string) {
await this.searchInput.fill(keyword);
await this.searchButton.click();
}
async expandAll() {
await this.expandAllButton.click();
await this.page.waitForTimeout(500);
}
async collapseAll() {
await this.collapseAllButton.click();
await this.page.waitForTimeout(500);
}
async containsText(text: string): Promise<boolean> {
return await this.table.getByText(text).count() > 0;
}
async isSuccessMessageVisible(): Promise<boolean> {
try {
return await this.successMessage.isVisible({ timeout: 3000 });
} catch {
return false;
}
}
async getMenuCount(): Promise<number> {
return await this.table.locator('tbody tr').count();
}
async reload() {
await this.page.reload();
}
}
@@ -12,6 +12,11 @@ export class RoleManagementPage {
readonly remarkInput: Locator;
readonly permissionDialog: Locator;
readonly savePermissionButton: Locator;
readonly searchInput: Locator;
readonly searchButton: Locator;
readonly pagination: Locator;
readonly nextPageButton: Locator;
readonly prevPageButton: Locator;
constructor(page: Page) {
this.page = page;
@@ -25,6 +30,11 @@ export class RoleManagementPage {
this.remarkInput = page.locator('textarea[placeholder*="备注"]').or(page.locator('textarea[name*="remark"]'));
this.permissionDialog = page.locator('.permission-dialog').or(page.locator('.el-dialog'));
this.savePermissionButton = page.getByRole('button', { name: '保存' }).or(page.locator('.permission-dialog .save-button'));
this.searchInput = page.locator('input[placeholder*="搜索角色名称或标识"]').or(page.locator('input[name*="keyword"]'));
this.searchButton = page.getByRole('button', { name: '搜索' }).or(page.locator('button:has-text("搜索")'));
this.pagination = page.locator('.el-pagination').or(page.locator('.pagination'));
this.nextPageButton = page.locator('.el-pagination .btn-next').or(page.locator('.pagination .next-page'));
this.prevPageButton = page.locator('.el-pagination .btn-prev').or(page.locator('.pagination .prev-page'));
}
async goto() {
@@ -46,6 +56,40 @@ export class RoleManagementPage {
}) {
await this.page.locator('.el-dialog').locator('input').first().fill(roleData.roleName);
await this.page.locator('.el-dialog').locator('input').nth(1).fill(roleData.roleKey);
if (roleData.roleSort) {
const sortInput = this.page.locator('.el-dialog').locator('.el-input-number');
if (await sortInput.count() > 0) {
const input = sortInput.locator('input');
await input.fill(roleData.roleSort);
}
}
if (roleData.status) {
const statusSelect = this.page.locator('.el-dialog').locator('.el-form-item').filter({ hasText: '状态' }).locator('.el-select');
if (await statusSelect.count() > 0) {
await statusSelect.click();
await this.page.waitForTimeout(500);
const statusText = roleData.status === 'ACTIVE' ? '正常' : '禁用';
const dropdown = this.page.locator('.el-select-dropdown');
if (await dropdown.count() > 0) {
const options = dropdown.locator('.el-select-dropdown__item');
const optionCount = await options.count();
for (let i = 0; i < optionCount; i++) {
const optionText = await options.nth(i).textContent();
if (optionText && optionText.includes(statusText)) {
await options.nth(i).click();
break;
}
}
}
await this.page.waitForTimeout(300);
}
}
if (roleData.remark) {
await this.page.locator('.el-dialog').locator('textarea').fill(roleData.remark);
}
@@ -98,4 +142,57 @@ export class RoleManagementPage {
async getRoleName(rowNumber: number): Promise<string | null> {
return await this.table.locator(`tbody tr:nth-child(${rowNumber}) td:first-child`).textContent();
}
async clickPermissionButton(rowNumber: number) {
await this.table.locator(`tbody tr:nth-child(${rowNumber})`).getByRole('button', { name: '权限' }).or(this.page.locator(`tbody tr:nth-child(${rowNumber}) .permission-button`)).click();
}
async deselectPermission(permissionValue: string) {
const checkbox = this.page.locator(`input[type="checkbox"][value="${permissionValue}"]`);
if (await checkbox.isChecked()) {
await checkbox.click();
}
}
async search(keyword: string) {
await this.searchInput.fill(keyword);
await this.searchButton.click();
}
async clearSearch() {
await this.searchInput.fill('');
await this.searchButton.click();
}
async clickStatusButton(rowNumber: number) {
const row = this.table.locator(`tbody tr:nth-child(${rowNumber})`);
await row.locator('.el-button--text').filter({ hasText: /状态|启用|禁用/ }).first().click();
}
async getCurrentPage(): Promise<string> {
try {
const activePage = this.page.locator('.el-pager li.is-active');
if (await activePage.count() > 0) {
return await activePage.textContent() || '1';
}
const currentPage = this.page.locator('.el-pagination__current');
if (await currentPage.count() > 0) {
return await currentPage.textContent() || '1';
}
return '1';
} catch (error) {
console.log('获取当前页码失败,返回默认值');
return '1';
}
}
async nextPage() {
await this.nextPageButton.click();
}
async prevPage() {
await this.prevPageButton.click();
}
}
@@ -15,7 +15,7 @@ export class UserManagementPage {
this.page = page;
this.table = page.locator('.el-table').first();
this.createUserButton = page.getByRole('button', { name: '新增用户' }).or(page.locator('button:has-text("新增用户")'));
this.searchInput = page.locator('input[placeholder*="搜索"]').or(page.locator('input[name*="keyword"]'));
this.searchInput = page.locator('input[placeholder*="搜索用户名或邮箱"]').or(page.locator('input[name*="keyword"]'));
this.searchButton = page.getByRole('button', { name: '搜索' }).or(page.locator('button:has-text("搜索")'));
this.successMessage = page.locator('.el-message--success').or(page.locator('.success-message'));
this.pagination = page.locator('.el-pagination').or(page.locator('.pagination'));
@@ -40,6 +40,7 @@ export class UserManagementPage {
phone?: string;
password: string;
confirmPassword?: string;
status?: string;
}) {
const dialog = this.page.locator('.el-dialog');
await dialog.locator('input').first().fill(userData.username);
@@ -52,18 +53,31 @@ export class UserManagementPage {
const phoneInput = dialog.locator('input[placeholder*="手机号"]');
if (await phoneInput.count() > 0) {
await phoneInput.fill(userData.phone);
} else {
const phoneSelect = dialog.locator('.el-select');
if (await phoneSelect.count() > 0) {
await phoneSelect.first().click();
await this.page.waitForTimeout(300);
const selectInput = this.page.locator('.el-select-dropdown__input');
if (await selectInput.count() > 0) {
await selectInput.fill(userData.phone);
await this.page.waitForTimeout(300);
}
}
if (userData.status) {
const statusSelect = dialog.locator('.el-form-item').filter({ hasText: '状态' }).locator('.el-select');
if (await statusSelect.count() > 0) {
await statusSelect.click();
await this.page.waitForTimeout(500);
const statusText = userData.status === '1' || userData.status === 'ACTIVE' ? '正常' : '禁用';
const dropdown = this.page.locator('.el-select-dropdown');
if (await dropdown.count() > 0) {
const options = dropdown.locator('.el-select-dropdown__item');
const optionCount = await options.count();
for (let i = 0; i < optionCount; i++) {
const optionText = await options.nth(i).textContent();
if (optionText && optionText.includes(statusText)) {
await options.nth(i).click();
break;
}
}
await this.page.keyboard.press('Enter');
}
await this.page.waitForTimeout(300);
}
}
}
@@ -98,7 +112,22 @@ export class UserManagementPage {
}
async getCurrentPage(): Promise<string> {
return await this.page.locator('.el-pagination .el-pager li.active').or(this.page.locator('.pagination .current-page')).textContent() || '1';
try {
const activePage = this.page.locator('.el-pager li.is-active');
if (await activePage.count() > 0) {
return await activePage.textContent() || '1';
}
const currentPage = this.page.locator('.el-pagination__current');
if (await currentPage.count() > 0) {
return await currentPage.textContent() || '1';
}
return '1';
} catch (error) {
console.log('获取当前页码失败,返回默认值');
return '1';
}
}
async getUserCount(): Promise<number> {
@@ -124,4 +153,73 @@ export class UserManagementPage {
async reload() {
await this.page.reload();
}
async clickStatusButton(rowNumber: number) {
const row = this.table.locator(`tbody tr:nth-child(${rowNumber})`);
await row.locator('.el-tag').first().click();
await this.page.waitForTimeout(500);
const dropdown = this.page.locator('.el-dropdown');
if (await dropdown.count() > 0) {
const options = dropdown.locator('.el-dropdown-menu__item');
const optionCount = await options.count();
for (let i = 0; i < optionCount; i++) {
const optionText = await options.nth(i).textContent();
if (optionText && (optionText.includes('启用') || optionText.includes('禁用'))) {
await options.nth(i).click();
break;
}
}
}
await this.page.waitForTimeout(300);
}
async clickEditButton(rowNumber: number) {
await this.table.locator(`tbody tr:nth-child(${rowNumber})`).getByRole('button', { name: '编辑' }).or(this.page.locator(`tbody tr:nth-child(${rowNumber}) .edit-button`)).click();
}
async clickDeleteButton(rowNumber: number) {
await this.table.locator(`tbody tr:nth-child(${rowNumber})`).getByRole('button', { name: '删除' }).or(this.page.locator(`tbody tr:nth-child(${rowNumber}) .delete-button`)).click();
}
async fillNickname(nickname: string) {
const dialog = this.page.locator('.el-dialog');
await dialog.locator('input').nth(1).fill(nickname);
}
async selectRole(roleName: string) {
const dialog = this.page.locator('.el-dialog');
const roleSelect = dialog.locator('.el-select');
if (await roleSelect.count() > 0) {
await roleSelect.first().click();
await this.page.waitForTimeout(500);
const dropdown = this.page.locator('.el-select-dropdown');
if (await dropdown.count() > 0) {
const options = dropdown.locator('.el-select-dropdown__item');
const optionCount = await options.count();
for (let i = 0; i < optionCount; i++) {
const optionText = await options.nth(i).textContent();
if (optionText && optionText.includes(roleName)) {
await options.nth(i).click();
break;
}
}
}
await this.page.waitForTimeout(300);
}
}
async clearSearch() {
await this.searchInput.fill('');
await this.searchButton.click();
}
async getTableRowCount(): Promise<number> {
return await this.table.locator('tbody tr').count();
}
}