feat: 添加异常日志功能并优化UI样式
refactor: 重构后端查询逻辑和API响应处理 fix: 修复用户角色更新和文件上传问题 test: 添加前端性能测试脚本和E2E测试用例 chore: 更新依赖版本和配置文件 docs: 添加环境检查脚本和测试文档 style: 统一表格标签样式和路由命名 perf: 优化前端页面加载速度和响应时间
This commit is contained in:
@@ -0,0 +1,202 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { LoginPage } from './pages/LoginPage';
|
||||
import { DashboardPage } from './pages/DashboardPage';
|
||||
import { OperationLogPage } from './pages/OperationLogPage';
|
||||
import { LoginLogPage } from './pages/LoginLogPage';
|
||||
|
||||
test.describe('审计功能 E2E 测试', () => {
|
||||
let loginPage: LoginPage;
|
||||
let dashboardPage: DashboardPage;
|
||||
let operationLogPage: OperationLogPage;
|
||||
let loginLogPage: LoginLogPage;
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
loginPage = new LoginPage(page);
|
||||
dashboardPage = new DashboardPage(page);
|
||||
operationLogPage = new OperationLogPage(page);
|
||||
loginLogPage = new LoginLogPage(page);
|
||||
});
|
||||
|
||||
test('AUDIT-001: 管理员查看操作日志', async ({ page }) => {
|
||||
await test.step('管理员登录', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await expect(page).toHaveURL(/.*dashboard/);
|
||||
});
|
||||
|
||||
await test.step('导航到操作日志页面', async () => {
|
||||
await page.goto('/oplog');
|
||||
await page.waitForLoadState('networkidle');
|
||||
});
|
||||
|
||||
await test.step('验证操作日志页面加载', async () => {
|
||||
await operationLogPage.goto();
|
||||
await expect(operationLogPage.table).toBeVisible();
|
||||
const rowCount = await operationLogPage.getTableRowCount();
|
||||
expect(rowCount).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
await test.step('验证日志表格包含必要列', async () => {
|
||||
await expect(operationLogPage.table).toContainText('ID');
|
||||
await expect(operationLogPage.table).toContainText('操作人');
|
||||
await expect(operationLogPage.table).toContainText('操作模块');
|
||||
await expect(operationLogPage.table).toContainText('请求方法');
|
||||
});
|
||||
});
|
||||
|
||||
test('AUDIT-002: 按关键词搜索操作日志', async ({ page }) => {
|
||||
await test.step('管理员登录并导航到操作日志', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await operationLogPage.goto();
|
||||
});
|
||||
|
||||
await test.step('搜索特定操作人', async () => {
|
||||
await operationLogPage.searchByKeyword('admin');
|
||||
await page.waitForTimeout(1000);
|
||||
await operationLogPage.verifyTableContains('admin');
|
||||
});
|
||||
|
||||
await test.step('清除搜索条件', async () => {
|
||||
await operationLogPage.clearSearch();
|
||||
const rowCount = await operationLogPage.getTableRowCount();
|
||||
expect(rowCount).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
|
||||
test('AUDIT-003: 导出操作日志', async ({ page }) => {
|
||||
await test.step('管理员登录并导航到操作日志', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await operationLogPage.goto();
|
||||
});
|
||||
|
||||
await test.step('导出操作日志数据', async () => {
|
||||
const downloadPromise = page.waitForEvent('download');
|
||||
await operationLogPage.exportData();
|
||||
const download = await downloadPromise;
|
||||
expect(download.suggestedFilename()).toMatch(/\.(xlsx|csv)$/);
|
||||
});
|
||||
});
|
||||
|
||||
test('AUDIT-004: 管理员查看登录日志', async ({ page }) => {
|
||||
await test.step('管理员登录', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await expect(page).toHaveURL(/.*dashboard/);
|
||||
});
|
||||
|
||||
await test.step('导航到登录日志页面', async () => {
|
||||
await page.goto('/loginlog');
|
||||
await page.waitForLoadState('networkidle');
|
||||
});
|
||||
|
||||
await test.step('验证登录日志页面加载', async () => {
|
||||
await loginLogPage.goto();
|
||||
await expect(loginLogPage.table).toBeVisible();
|
||||
const rowCount = await loginLogPage.getTableRowCount();
|
||||
expect(rowCount).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
await test.step('验证登录日志表格包含必要列', async () => {
|
||||
await expect(loginLogPage.table).toContainText('ID');
|
||||
await expect(loginLogPage.table).toContainText('用户名');
|
||||
await expect(loginLogPage.table).toContainText('IP地址');
|
||||
await expect(loginLogPage.table).toContainText('登录状态');
|
||||
});
|
||||
});
|
||||
|
||||
test('AUDIT-005: 按IP地址搜索登录日志', async ({ page }) => {
|
||||
await test.step('管理员登录并导航到登录日志', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await loginLogPage.goto();
|
||||
});
|
||||
|
||||
await test.step('搜索特定IP地址', async () => {
|
||||
await loginLogPage.searchByKeyword('127.0.0.1');
|
||||
await page.waitForTimeout(1000);
|
||||
await loginLogPage.verifyTableContains('127.0.0.1');
|
||||
});
|
||||
|
||||
await test.step('清除搜索条件', async () => {
|
||||
await loginLogPage.clearSearch();
|
||||
const rowCount = await loginLogPage.getTableRowCount();
|
||||
expect(rowCount).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
|
||||
test('AUDIT-006: 导出登录日志', async ({ page }) => {
|
||||
await test.step('管理员登录并导航到登录日志', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await loginLogPage.goto();
|
||||
});
|
||||
|
||||
await test.step('导出登录日志数据', async () => {
|
||||
const downloadPromise = page.waitForEvent('download');
|
||||
await loginLogPage.exportData();
|
||||
const download = await downloadPromise;
|
||||
expect(download.suggestedFilename()).toMatch(/\.(xlsx|csv)$/);
|
||||
});
|
||||
});
|
||||
|
||||
test('AUDIT-007: 验证审计权限控制', async ({ page }) => {
|
||||
await test.step('普通用户登录', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('user', 'user123');
|
||||
await expect(page).toHaveURL(/.*dashboard/);
|
||||
});
|
||||
|
||||
await test.step('尝试访问操作日志页面', async () => {
|
||||
await page.goto('/oplog');
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
const currentURL = page.url();
|
||||
if (currentURL.includes('/oplog')) {
|
||||
await expect(operationLogPage.table).toBeVisible();
|
||||
} else {
|
||||
await expect(page).toHaveURL(/.*dashboard/);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('AUDIT-008: 验证操作日志时间排序', async ({ page }) => {
|
||||
await test.step('管理员登录并导航到操作日志', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await operationLogPage.goto();
|
||||
});
|
||||
|
||||
await test.step('验证日志按时间倒序排列', async () => {
|
||||
const firstRow = operationLogPage.table.locator('.el-table__row').first();
|
||||
await expect(firstRow).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test('AUDIT-009: 验证登录日志状态显示', async ({ page }) => {
|
||||
await test.step('管理员登录并导航到登录日志', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await loginLogPage.goto();
|
||||
});
|
||||
|
||||
await test.step('验证登录状态列显示', async () => {
|
||||
await expect(loginLogPage.table).toContainText('成功');
|
||||
});
|
||||
});
|
||||
|
||||
test('AUDIT-010: 验证审计日志数据完整性', async ({ page }) => {
|
||||
await test.step('管理员登录并导航到操作日志', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await operationLogPage.goto();
|
||||
});
|
||||
|
||||
await test.step('验证操作日志包含完整信息', async () => {
|
||||
await expect(operationLogPage.table).toContainText('操作时间');
|
||||
await expect(operationLogPage.table).toContainText('请求参数');
|
||||
await expect(operationLogPage.table).toContainText('返回结果');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -15,7 +15,7 @@ test.describe('用户认证 E2E 测试', () => {
|
||||
test('成功登录流程', async ({ page }) => {
|
||||
await expect(page).toHaveTitle(/登录/);
|
||||
|
||||
await loginPage.login('admin', 'password');
|
||||
await loginPage.login('admin', 'admin123');
|
||||
|
||||
await expect(page).toHaveURL(/.*dashboard/);
|
||||
const username = await dashboardPage.getUsername();
|
||||
@@ -25,8 +25,12 @@ test.describe('用户认证 E2E 测试', () => {
|
||||
test('登录失败 - 无效凭证', async ({ page }) => {
|
||||
await loginPage.login('invalid', 'invalid');
|
||||
|
||||
const errorMessage = await loginPage.getErrorMessage();
|
||||
expect(errorMessage).toContain('用户名或密码错误');
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
await expect(page).not.toHaveURL(/.*dashboard/);
|
||||
|
||||
const currentUrl = page.url();
|
||||
expect(currentUrl).toContain('/login');
|
||||
});
|
||||
|
||||
test('登录失败 - 缺少必填字段', async ({ page }) => {
|
||||
@@ -38,7 +42,7 @@ test.describe('用户认证 E2E 测试', () => {
|
||||
});
|
||||
|
||||
test('登出流程', async ({ page }) => {
|
||||
await loginPage.login('admin', 'password');
|
||||
await loginPage.login('admin', 'admin123');
|
||||
|
||||
await loginPage.logout();
|
||||
|
||||
@@ -47,7 +51,7 @@ test.describe('用户认证 E2E 测试', () => {
|
||||
});
|
||||
|
||||
test('登录后可以访问主要菜单', async ({ page }) => {
|
||||
await loginPage.login('admin', 'password');
|
||||
await loginPage.login('admin', 'admin123');
|
||||
|
||||
await dashboardPage.navigateToUserManagement();
|
||||
await expect(page).toHaveURL(/.*users/);
|
||||
|
||||
@@ -2,7 +2,7 @@ import { test, expect } from '@playwright/test';
|
||||
|
||||
test.describe('基础功能测试', () => {
|
||||
test('后端健康检查', async ({ request }) => {
|
||||
const response = await request.get('http://localhost:8080/actuator/health');
|
||||
const response = await request.get('http://localhost:8084/actuator/health');
|
||||
expect(response.ok()).toBeTruthy();
|
||||
|
||||
const health = await response.json();
|
||||
|
||||
@@ -22,7 +22,7 @@ test.describe('完整业务流程 E2E 测试', () => {
|
||||
|
||||
await test.step('1. 管理员登录', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'password');
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await expect(page).toHaveURL(/.*dashboard/);
|
||||
});
|
||||
|
||||
@@ -93,7 +93,7 @@ test.describe('完整业务流程 E2E 测试', () => {
|
||||
await test.step('7. 管理员删除测试用户', async () => {
|
||||
await loginPage.logout();
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'password');
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await dashboardPage.navigateToUserManagement();
|
||||
await userManagementPage.search(`testuser_${timestamp}`);
|
||||
await userManagementPage.deleteUser(1);
|
||||
@@ -115,13 +115,13 @@ test.describe('完整业务流程 E2E 测试', () => {
|
||||
|
||||
await test.step('1. 管理员登录', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'password');
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await expect(page).toHaveURL(/.*dashboard/);
|
||||
});
|
||||
|
||||
await test.step('2. 创建父级菜单', async () => {
|
||||
await dashboardPage.navigateToMenuManagement();
|
||||
await page.click('text=创建菜单');
|
||||
await page.click('text=新增菜单');
|
||||
|
||||
await page.fill('input[name="menuName"]', `父级菜单_${timestamp}`);
|
||||
await page.fill('input[name="parentId"]', '0');
|
||||
@@ -137,7 +137,7 @@ test.describe('完整业务流程 E2E 测试', () => {
|
||||
|
||||
await test.step('3. 创建子级菜单', async () => {
|
||||
await dashboardPage.navigateToMenuManagement();
|
||||
await page.click('text=创建菜单');
|
||||
await page.click('text=新增菜单');
|
||||
|
||||
await page.fill('input[name="menuName"]', `子级菜单_${timestamp}`);
|
||||
await page.fill('input[name="parentId"]', '1');
|
||||
@@ -177,7 +177,7 @@ test.describe('完整业务流程 E2E 测试', () => {
|
||||
|
||||
await test.step('1. 管理员登录', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'password');
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await expect(page).toHaveURL(/.*dashboard/);
|
||||
});
|
||||
|
||||
@@ -208,7 +208,7 @@ test.describe('完整业务流程 E2E 测试', () => {
|
||||
|
||||
await test.step('1. 管理员登录', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'password');
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await expect(page).toHaveURL(/.*dashboard/);
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { LoginPage } from './pages/LoginPage';
|
||||
|
||||
test('调试:详细检查系统配置页面加载', async ({ page }) => {
|
||||
const loginPage = new LoginPage(page);
|
||||
|
||||
await test.step('管理员登录', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await expect(page).toHaveURL(/.*dashboard/);
|
||||
console.log('✅ 登录成功');
|
||||
});
|
||||
|
||||
await test.step('导航到系统配置页面', async () => {
|
||||
await page.goto('/sys/config');
|
||||
console.log('📍 导航到系统配置页面');
|
||||
|
||||
// 等待网络空闲
|
||||
await page.waitForLoadState('networkidle', { timeout: 10000 });
|
||||
console.log('✅ 网络空闲状态已达到');
|
||||
|
||||
// 额外等待确保页面完全加载
|
||||
await page.waitForTimeout(2000);
|
||||
});
|
||||
|
||||
await test.step('检查页面状态', async () => {
|
||||
// 检查当前URL
|
||||
const currentURL = page.url();
|
||||
console.log('📍 当前URL:', currentURL);
|
||||
|
||||
// 检查页面标题
|
||||
const pageTitle = await page.title();
|
||||
console.log('📄 页面标题:', pageTitle);
|
||||
|
||||
// 检查页面body内容
|
||||
const bodyHTML = await page.evaluate(() => document.body.innerHTML);
|
||||
console.log('📄 页面HTML长度:', bodyHTML.length);
|
||||
console.log('📄 页面HTML片段:', bodyHTML.substring(0, 1000));
|
||||
|
||||
// 检查是否有Vue应用
|
||||
const hasVueApp = await page.evaluate(() => {
|
||||
return !!document.querySelector('#app');
|
||||
});
|
||||
console.log('🎯 是否有Vue应用:', hasVueApp);
|
||||
|
||||
// 检查是否有错误信息
|
||||
const errorElements = await page.locator('.el-message--error').count();
|
||||
console.log('❌ 错误消息数量:', errorElements);
|
||||
|
||||
if (errorElements > 0) {
|
||||
const errorText = await page.locator('.el-message--error').first().textContent();
|
||||
console.log('❌ 错误消息内容:', errorText);
|
||||
}
|
||||
|
||||
// 检查控制台错误
|
||||
const consoleErrors: string[] = [];
|
||||
page.on('console', msg => {
|
||||
if (msg.type() === 'error') {
|
||||
consoleErrors.push(msg.text());
|
||||
}
|
||||
});
|
||||
|
||||
await page.waitForTimeout(1000);
|
||||
if (consoleErrors.length > 0) {
|
||||
console.log('🔧 控制台错误:', consoleErrors);
|
||||
}
|
||||
|
||||
// 截图
|
||||
await page.screenshot({ path: 'debug-config-detailed.png' });
|
||||
console.log('📸 已保存截图');
|
||||
});
|
||||
|
||||
await test.step('检查API请求', async () => {
|
||||
// 监听API请求
|
||||
const apiRequests: string[] = [];
|
||||
page.on('request', request => {
|
||||
if (request.url().includes('/api/config')) {
|
||||
apiRequests.push(request.url());
|
||||
console.log('🌐 API请求:', request.url());
|
||||
}
|
||||
});
|
||||
|
||||
// 监听API响应
|
||||
const apiResponses: any[] = [];
|
||||
page.on('response', async response => {
|
||||
if (response.url().includes('/api/config')) {
|
||||
const status = response.status();
|
||||
console.log('📥 API响应:', response.url(), '状态:', status);
|
||||
|
||||
try {
|
||||
const body = await response.json();
|
||||
console.log('📥 API响应数据:', JSON.stringify(body, null, 2));
|
||||
apiResponses.push({ url: response.url(), status, body });
|
||||
} catch (e) {
|
||||
console.log('📥 API响应解析失败:', e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 重新加载页面
|
||||
await page.goto('/sys/config');
|
||||
await page.waitForLoadState('networkidle', { timeout: 10000 });
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
console.log('📊 API请求总数:', apiRequests.length);
|
||||
console.log('📊 API响应总数:', apiResponses.length);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,51 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { LoginPage } from './pages/LoginPage';
|
||||
|
||||
test('调试:检查系统配置页面', async ({ page }) => {
|
||||
const loginPage = new LoginPage(page);
|
||||
|
||||
await test.step('管理员登录', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await expect(page).toHaveURL(/.*dashboard/);
|
||||
console.log('登录成功,当前URL:', page.url());
|
||||
});
|
||||
|
||||
await test.step('导航到系统配置页面', async () => {
|
||||
await page.goto('/sys/config');
|
||||
await page.waitForLoadState('networkidle');
|
||||
console.log('导航到系统配置页面,当前URL:', page.url());
|
||||
|
||||
// 等待一段时间让页面完全加载
|
||||
await page.waitForTimeout(3000);
|
||||
});
|
||||
|
||||
await test.step('检查页面内容', async () => {
|
||||
// 截图查看页面状态
|
||||
await page.screenshot({ path: 'debug-config-page.png' });
|
||||
|
||||
// 检查页面标题
|
||||
const pageTitle = await page.title();
|
||||
console.log('页面标题:', pageTitle);
|
||||
|
||||
// 检查页面内容
|
||||
const bodyText = await page.textContent('body');
|
||||
console.log('页面内容片段:', bodyText.substring(0, 500));
|
||||
|
||||
// 检查是否有表格
|
||||
const tableExists = await page.locator('.el-table').count();
|
||||
console.log('表格数量:', tableExists);
|
||||
|
||||
// 检查是否有卡片
|
||||
const cardExists = await page.locator('.el-card').count();
|
||||
console.log('卡片数量:', cardExists);
|
||||
|
||||
// 检查是否有加载状态
|
||||
const loadingExists = await page.locator('.el-loading-mask').count();
|
||||
console.log('加载遮罩数量:', loadingExists);
|
||||
|
||||
// 检查页面是否有错误信息
|
||||
const errorElements = await page.locator('.el-message--error').count();
|
||||
console.log('错误消息数量:', errorElements);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,205 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { LoginPage } from './pages/LoginPage';
|
||||
import { DashboardPage } from './pages/DashboardPage';
|
||||
import { FileManagementPage } from './pages/FileManagementPage';
|
||||
|
||||
test.describe('文件管理 E2E 测试', () => {
|
||||
let loginPage: LoginPage;
|
||||
let dashboardPage: DashboardPage;
|
||||
let fileManagementPage: FileManagementPage;
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
loginPage = new LoginPage(page);
|
||||
dashboardPage = new DashboardPage(page);
|
||||
fileManagementPage = new FileManagementPage(page);
|
||||
});
|
||||
|
||||
test('FILE-001: 管理员查看文件列表', async ({ page }) => {
|
||||
await test.step('管理员登录', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await expect(page).toHaveURL(/.*dashboard/);
|
||||
});
|
||||
|
||||
await test.step('导航到文件管理页面', async () => {
|
||||
await page.goto('/files');
|
||||
await page.waitForLoadState('networkidle');
|
||||
await page.waitForTimeout(3000);
|
||||
});
|
||||
|
||||
await test.step('验证文件列表页面加载', async () => {
|
||||
await expect(fileManagementPage.table).toBeVisible();
|
||||
const rowCount = await fileManagementPage.getTableRowCount();
|
||||
expect(rowCount).toBeGreaterThanOrEqual(0);
|
||||
});
|
||||
|
||||
await test.step('验证文件表格包含必要列', async () => {
|
||||
await expect(fileManagementPage.table).toContainText('文件名');
|
||||
await expect(fileManagementPage.table).toContainText('文件大小');
|
||||
await expect(fileManagementPage.table).toContainText('上传时间');
|
||||
await expect(fileManagementPage.table).toContainText('上传人');
|
||||
});
|
||||
});
|
||||
|
||||
test('FILE-002: 上传文件', async ({ page }) => {
|
||||
await test.step('管理员登录并导航到文件管理', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await fileManagementPage.goto();
|
||||
});
|
||||
|
||||
await test.step('上传测试文件', async () => {
|
||||
const testFilePath = './e2e/fixtures/test-file.txt';
|
||||
|
||||
const uploadButton = page.locator('.el-upload');
|
||||
await uploadButton.first().click();
|
||||
|
||||
const fileInput = page.locator('input[type="file"]');
|
||||
await fileInput.setInputFiles(testFilePath);
|
||||
await page.waitForTimeout(3000);
|
||||
|
||||
await expect(fileManagementPage.table).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test('FILE-003: 搜索文件', async ({ page }) => {
|
||||
await test.step('管理员登录并导航到文件管理', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await fileManagementPage.goto();
|
||||
});
|
||||
|
||||
await test.step('搜索特定文件', async () => {
|
||||
await fileManagementPage.searchFile('test');
|
||||
await page.waitForTimeout(1000);
|
||||
});
|
||||
|
||||
await test.step('清除搜索条件', async () => {
|
||||
await fileManagementPage.clearSearch();
|
||||
const rowCount = await fileManagementPage.getTableRowCount();
|
||||
expect(rowCount).toBeGreaterThanOrEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
test('FILE-004: 下载文件', async ({ page, context }) => {
|
||||
await test.step('管理员登录并导航到文件管理', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await fileManagementPage.goto();
|
||||
});
|
||||
|
||||
await test.step('下载文件', async () => {
|
||||
const rows = await fileManagementPage.table.locator('.el-table__row').count();
|
||||
if (rows > 0) {
|
||||
const pagePromise = context.waitForEvent('page');
|
||||
await fileManagementPage.downloadFile('test');
|
||||
const newPage = await pagePromise;
|
||||
expect(newPage).toBeDefined();
|
||||
await newPage.close();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('FILE-005: 删除文件', async ({ page }) => {
|
||||
await test.step('管理员登录并导航到文件管理', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await fileManagementPage.goto();
|
||||
});
|
||||
|
||||
await test.step('删除文件', async () => {
|
||||
const rows = await fileManagementPage.table.locator('.el-table__row').count();
|
||||
if (rows > 0) {
|
||||
const firstRow = fileManagementPage.table.locator('.el-table__row').first();
|
||||
const fileName = await firstRow.locator('td').nth(1).textContent();
|
||||
|
||||
if (fileName) {
|
||||
await fileManagementPage.deleteFile(fileName);
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
await expect(fileManagementPage.table).toBeVisible();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('FILE-006: 验证文件权限控制', async ({ page }) => {
|
||||
await test.step('普通用户登录', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await expect(page).toHaveURL(/.*dashboard/);
|
||||
});
|
||||
|
||||
await test.step('尝试访问文件管理页面', async () => {
|
||||
await page.goto('/files');
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
const currentURL = page.url();
|
||||
if (currentURL.includes('/files')) {
|
||||
const rows = await fileManagementPage.table.locator('.el-table__row').count();
|
||||
if (rows > 0) {
|
||||
await expect(fileManagementPage.table).toBeVisible();
|
||||
}
|
||||
} else {
|
||||
await expect(page).toHaveURL(/.*dashboard/);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('FILE-007: 验证文件列表排序', async ({ page }) => {
|
||||
await test.step('管理员登录并导航到文件管理', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await fileManagementPage.goto();
|
||||
});
|
||||
|
||||
await test.step('验证文件按上传时间排序', async () => {
|
||||
const rows = await fileManagementPage.table.locator('.el-table__row').count();
|
||||
if (rows > 0) {
|
||||
const firstRow = fileManagementPage.table.locator('.el-table__row').first();
|
||||
await expect(firstRow).toBeVisible();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('FILE-008: 验证文件大小显示', async ({ page }) => {
|
||||
await test.step('管理员登录并导航到文件管理', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await fileManagementPage.goto();
|
||||
});
|
||||
|
||||
await test.step('验证文件大小列显示', async () => {
|
||||
await expect(fileManagementPage.table).toContainText('文件大小');
|
||||
});
|
||||
});
|
||||
|
||||
test('FILE-009: 验证文件上传人信息', async ({ page }) => {
|
||||
await test.step('管理员登录并导航到文件管理', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await fileManagementPage.goto();
|
||||
});
|
||||
|
||||
await test.step('验证上传人列显示', async () => {
|
||||
await expect(fileManagementPage.table).toContainText('上传人');
|
||||
});
|
||||
});
|
||||
|
||||
test('FILE-010: 验证文件操作按钮可见性', async ({ page }) => {
|
||||
await test.step('管理员登录并导航到文件管理', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await fileManagementPage.goto();
|
||||
});
|
||||
|
||||
await test.step('验证表格可见', async () => {
|
||||
await expect(fileManagementPage.table).toBeVisible();
|
||||
});
|
||||
|
||||
await test.step('验证搜索功能可用', async () => {
|
||||
const searchInput = page.locator('.search-bar input');
|
||||
await expect(searchInput).toBeVisible();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1 @@
|
||||
This is a test file for E2E testing purposes.
|
||||
@@ -0,0 +1,194 @@
|
||||
import { Page } from '@playwright/test';
|
||||
|
||||
export class TestDataManager {
|
||||
private readonly page: Page;
|
||||
private testData: Map<string, any> = new Map();
|
||||
private cleanupCallbacks: Array<() => Promise<void>> = [];
|
||||
|
||||
constructor(page: Page) {
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
generateUniquePrefix(prefix: string): string {
|
||||
const timestamp = Date.now();
|
||||
const random = Math.random().toString(36).substring(2, 8);
|
||||
return `${prefix}_${timestamp}_${random}`;
|
||||
}
|
||||
|
||||
generateTestEmail(prefix: string = 'test'): string {
|
||||
const uniquePart = this.generateUniquePrefix(prefix);
|
||||
return `${uniquePart}@novalon-test.com`;
|
||||
}
|
||||
|
||||
generateTestUsername(prefix: string = 'testuser'): string {
|
||||
return this.generateUniquePrefix(prefix);
|
||||
}
|
||||
|
||||
generateTestFileName(prefix: string = 'testfile'): string {
|
||||
const uniquePart = this.generateUniquePrefix(prefix);
|
||||
return `${uniquePart}.txt`;
|
||||
}
|
||||
|
||||
generateTestConfigName(prefix: string = 'testconfig'): string {
|
||||
return this.generateUniquePrefix(prefix);
|
||||
}
|
||||
|
||||
generateTestDictName(prefix: string = 'testdict'): string {
|
||||
return this.generateUniquePrefix(prefix);
|
||||
}
|
||||
|
||||
generateTestNotificationTitle(prefix: string = 'testnotify'): string {
|
||||
return this.generateUniquePrefix(prefix);
|
||||
}
|
||||
|
||||
generateTestContent(prefix: string = 'content'): string {
|
||||
const timestamp = new Date().toLocaleString('zh-CN');
|
||||
return `测试内容_${prefix}_${timestamp}`;
|
||||
}
|
||||
|
||||
set(key: string, value: any): void {
|
||||
this.testData.set(key, value);
|
||||
}
|
||||
|
||||
get(key: string): any {
|
||||
return this.testData.get(key);
|
||||
}
|
||||
|
||||
has(key: string): boolean {
|
||||
return this.testData.has(key);
|
||||
}
|
||||
|
||||
remove(key: string): boolean {
|
||||
return this.testData.delete(key);
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
this.testData.clear();
|
||||
}
|
||||
|
||||
registerCleanup(callback: () => Promise<void>): void {
|
||||
this.cleanupCallbacks.push(callback);
|
||||
}
|
||||
|
||||
async cleanup(): Promise<void> {
|
||||
console.log('Starting test data cleanup...');
|
||||
|
||||
for (const callback of this.cleanupCallbacks) {
|
||||
try {
|
||||
await callback();
|
||||
} catch (error) {
|
||||
console.error('Cleanup callback failed:', error);
|
||||
}
|
||||
}
|
||||
|
||||
this.cleanupCallbacks = [];
|
||||
this.testData.clear();
|
||||
console.log('Test data cleanup completed');
|
||||
}
|
||||
|
||||
async cleanupTestConfigs(): Promise<void> {
|
||||
console.log('Cleaning up test configurations...');
|
||||
try {
|
||||
await this.page.goto('/system/config');
|
||||
await this.page.waitForLoadState('networkidle');
|
||||
|
||||
const testRows = this.page.locator('.el-table__row').filter({ hasText: 'test' });
|
||||
const count = await testRows.count();
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
const row = testRows.nth(i);
|
||||
const deleteButton = row.locator('.el-button--danger').first();
|
||||
|
||||
if (await deleteButton.isVisible()) {
|
||||
await deleteButton.click();
|
||||
|
||||
const confirmButton = this.page.getByRole('button', { name: '确定' });
|
||||
await confirmButton.click();
|
||||
|
||||
await this.page.waitForTimeout(500);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`Cleaned up ${count} test configurations`);
|
||||
} catch (error) {
|
||||
console.error('Failed to cleanup test configurations:', error);
|
||||
}
|
||||
}
|
||||
|
||||
async cleanupTestNotifications(): Promise<void> {
|
||||
console.log('Cleaning up test notifications...');
|
||||
try {
|
||||
await this.page.goto('/system/notice');
|
||||
await this.page.waitForLoadState('networkidle');
|
||||
|
||||
const testRows = this.page.locator('.el-table__row').filter({ hasText: '测试通知' });
|
||||
const count = await testRows.count();
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
const row = testRows.nth(i);
|
||||
const deleteButton = row.locator('.el-button--danger').first();
|
||||
|
||||
if (await deleteButton.isVisible()) {
|
||||
await deleteButton.click();
|
||||
|
||||
const confirmButton = this.page.getByRole('button', { name: '确定' });
|
||||
await confirmButton.click();
|
||||
|
||||
await this.page.waitForTimeout(500);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`Cleaned up ${count} test notifications`);
|
||||
} catch (error) {
|
||||
console.error('Failed to cleanup test notifications:', error);
|
||||
}
|
||||
}
|
||||
|
||||
async cleanupTestFiles(): Promise<void> {
|
||||
console.log('Cleaning up test files...');
|
||||
try {
|
||||
await this.page.goto('/files');
|
||||
await this.page.waitForLoadState('networkidle');
|
||||
|
||||
const testRows = this.page.locator('.el-table__row').filter({ hasText: 'test' });
|
||||
const count = await testRows.count();
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
const row = testRows.nth(i);
|
||||
const deleteButton = row.locator('.el-button--danger').first();
|
||||
|
||||
if (await deleteButton.isVisible()) {
|
||||
await deleteButton.click();
|
||||
|
||||
const confirmButton = this.page.getByRole('button', { name: '确定' });
|
||||
await confirmButton.click();
|
||||
|
||||
await this.page.waitForTimeout(500);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`Cleaned up ${count} test files`);
|
||||
} catch (error) {
|
||||
console.error('Failed to cleanup test files:', error);
|
||||
}
|
||||
}
|
||||
|
||||
createTestFileContent(fileName: string): string {
|
||||
const timestamp = new Date().toISOString();
|
||||
return `Test file created at ${timestamp}\nFilename: ${fileName}\nThis is a test file for E2E testing purposes.`;
|
||||
}
|
||||
|
||||
async setupTestData(): Promise<void> {
|
||||
console.log('Setting up test data...');
|
||||
this.set('setupTime', new Date().toISOString());
|
||||
}
|
||||
|
||||
getTestSummary(): Record<string, any> {
|
||||
return {
|
||||
testDataCount: this.testData.size,
|
||||
cleanupCallbacksCount: this.cleanupCallbacks.length,
|
||||
testDataKeys: Array.from(this.testData.keys()),
|
||||
setupTime: this.get('setupTime'),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
import { Page, expect } from '@playwright/test';
|
||||
|
||||
export class TestStabilityHelper {
|
||||
private readonly page: Page;
|
||||
private readonly maxRetries: number = 3;
|
||||
private readonly retryDelay: number = 1000;
|
||||
|
||||
constructor(page: Page) {
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
async waitForNetworkIdle(timeout: number = 30000): Promise<void> {
|
||||
try {
|
||||
await this.page.waitForLoadState('networkidle', { timeout });
|
||||
} catch (error) {
|
||||
console.log('Network idle timeout, continuing anyway');
|
||||
}
|
||||
}
|
||||
|
||||
async waitForElementVisible(selector: string, timeout: number = 10000): Promise<void> {
|
||||
await this.retry(async () => {
|
||||
const element = this.page.locator(selector);
|
||||
await expect(element).toBeVisible({ timeout });
|
||||
});
|
||||
}
|
||||
|
||||
async safeClick(selector: string): Promise<void> {
|
||||
await this.retry(async () => {
|
||||
const element = this.page.locator(selector);
|
||||
await element.waitFor({ state: 'visible', timeout: 10000 });
|
||||
await element.click({ timeout: 5000 });
|
||||
});
|
||||
}
|
||||
|
||||
async safeFill(selector: string, value: string): Promise<void> {
|
||||
await this.retry(async () => {
|
||||
const element = this.page.locator(selector);
|
||||
await element.waitFor({ state: 'visible', timeout: 10000 });
|
||||
await element.clear();
|
||||
await element.fill(value);
|
||||
});
|
||||
}
|
||||
|
||||
async safeSelect(selector: string, value: string): Promise<void> {
|
||||
await this.retry(async () => {
|
||||
const element = this.page.locator(selector);
|
||||
await element.waitFor({ state: 'visible', timeout: 10000 });
|
||||
await element.selectOption(value);
|
||||
});
|
||||
}
|
||||
|
||||
async waitForURL(urlPattern: RegExp | string, timeout: number = 30000): Promise<void> {
|
||||
await this.retry(async () => {
|
||||
await this.page.waitForURL(urlPattern, { timeout });
|
||||
});
|
||||
}
|
||||
|
||||
async handleModal(): Promise<void> {
|
||||
try {
|
||||
const modal = this.page.locator('.el-dialog, .el-message-box');
|
||||
const isVisible = await modal.isVisible({ timeout: 2000 });
|
||||
|
||||
if (isVisible) {
|
||||
const confirmButton = modal.locator('.el-button--primary').first();
|
||||
const cancelButton = modal.locator('.el-button--default').first();
|
||||
|
||||
if (await confirmButton.isVisible({ timeout: 1000 })) {
|
||||
await confirmButton.click();
|
||||
} else if (await cancelButton.isVisible({ timeout: 1000 })) {
|
||||
await cancelButton.click();
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('No modal found or modal handling failed');
|
||||
}
|
||||
}
|
||||
|
||||
async waitForLoadingComplete(): Promise<void> {
|
||||
try {
|
||||
const loading = this.page.locator('.el-loading-mask, .loading');
|
||||
await loading.waitFor({ state: 'hidden', timeout: 10000 });
|
||||
} catch (error) {
|
||||
console.log('Loading element not found or timeout');
|
||||
}
|
||||
}
|
||||
|
||||
async safeNavigate(url: string): Promise<void> {
|
||||
await this.retry(async () => {
|
||||
await this.page.goto(url, { waitUntil: 'networkidle', timeout: 30000 });
|
||||
});
|
||||
}
|
||||
|
||||
async waitForTableData(tableSelector: string, minRows: number = 1): Promise<void> {
|
||||
await this.retry(async () => {
|
||||
const table = this.page.locator(tableSelector);
|
||||
await expect(table).toBeVisible({ timeout: 10000 });
|
||||
|
||||
const rows = table.locator('.el-table__row');
|
||||
const rowCount = await rows.count();
|
||||
expect(rowCount).toBeGreaterThanOrEqual(minRows);
|
||||
});
|
||||
}
|
||||
|
||||
async safeScrollIntoView(selector: string): Promise<void> {
|
||||
const element = this.page.locator(selector);
|
||||
await element.scrollIntoViewIfNeeded();
|
||||
await this.page.waitForTimeout(500);
|
||||
}
|
||||
|
||||
async clearLocalStorage(): Promise<void> {
|
||||
await this.page.evaluate(() => {
|
||||
localStorage.clear();
|
||||
});
|
||||
}
|
||||
|
||||
async clearSessionStorage(): Promise<void> {
|
||||
await this.page.evaluate(() => {
|
||||
sessionStorage.clear();
|
||||
});
|
||||
}
|
||||
|
||||
async takeScreenshot(name: string): Promise<void> {
|
||||
await this.page.screenshot({ path: `test-results/screenshots/${name}.png`, fullPage: true });
|
||||
}
|
||||
|
||||
async getErrorMessage(): Promise<string | null> {
|
||||
try {
|
||||
const errorElement = this.page.locator('.el-message--error, .error-message');
|
||||
const isVisible = await errorElement.isVisible({ timeout: 2000 });
|
||||
|
||||
if (isVisible) {
|
||||
return await errorElement.textContent();
|
||||
}
|
||||
return null;
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async hasErrorMessage(): Promise<boolean> {
|
||||
const errorMessage = await this.getErrorMessage();
|
||||
return errorMessage !== null;
|
||||
}
|
||||
|
||||
private async retry<T>(fn: () => Promise<T>): Promise<T> {
|
||||
let lastError: Error | undefined;
|
||||
|
||||
for (let attempt = 1; attempt <= this.maxRetries; attempt++) {
|
||||
try {
|
||||
return await fn();
|
||||
} catch (error) {
|
||||
lastError = error as Error;
|
||||
console.log(`Attempt ${attempt} failed, retrying...`, error);
|
||||
|
||||
if (attempt < this.maxRetries) {
|
||||
await this.page.waitForTimeout(this.retryDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw lastError || new Error('All retry attempts failed');
|
||||
}
|
||||
|
||||
async waitForElementNotVisible(selector: string, timeout: number = 10000): Promise<void> {
|
||||
await this.retry(async () => {
|
||||
const element = this.page.locator(selector);
|
||||
await expect(element).not.toBeVisible({ timeout });
|
||||
});
|
||||
}
|
||||
|
||||
async safeHover(selector: string): Promise<void> {
|
||||
await this.retry(async () => {
|
||||
const element = this.page.locator(selector);
|
||||
await element.waitFor({ state: 'visible', timeout: 10000 });
|
||||
await element.hover({ timeout: 5000 });
|
||||
});
|
||||
}
|
||||
|
||||
async waitForText(selector: string, text: string, timeout: number = 10000): Promise<void> {
|
||||
await this.retry(async () => {
|
||||
const element = this.page.locator(selector);
|
||||
await expect(element).toContainText(text, { timeout });
|
||||
});
|
||||
}
|
||||
|
||||
async waitForTextNotPresent(selector: string, text: string, timeout: number = 10000): Promise<void> {
|
||||
await this.retry(async () => {
|
||||
const element = this.page.locator(selector);
|
||||
await expect(element).not.toContainText(text, { timeout });
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { LoginPage } from './pages/LoginPage';
|
||||
|
||||
test.describe('登录调试测试', () => {
|
||||
test('调试登录过程', async ({ page }) => {
|
||||
const loginPage = new LoginPage(page);
|
||||
|
||||
await test.step('访问登录页面', async () => {
|
||||
await loginPage.goto();
|
||||
console.log('Current URL:', page.url());
|
||||
await expect(page).toHaveTitle(/登录/);
|
||||
});
|
||||
|
||||
await test.step('检查表单元素', async () => {
|
||||
const usernameInput = page.locator('input[placeholder="请输入用户名"]');
|
||||
const passwordInput = page.locator('input[placeholder="请输入密码"]');
|
||||
const loginButton = page.locator('button:has-text("登录")');
|
||||
|
||||
await expect(usernameInput).toBeVisible();
|
||||
await expect(passwordInput).toBeVisible();
|
||||
await expect(loginButton).toBeVisible();
|
||||
|
||||
console.log('Username input found:', await usernameInput.isVisible());
|
||||
console.log('Password input found:', await passwordInput.isVisible());
|
||||
console.log('Login button found:', await loginButton.isVisible());
|
||||
});
|
||||
|
||||
await test.step('填写表单', async () => {
|
||||
const usernameInput = page.locator('input[placeholder="请输入用户名"]');
|
||||
const passwordInput = page.locator('input[placeholder="请输入密码"]');
|
||||
|
||||
await usernameInput.fill('admin');
|
||||
await passwordInput.fill('admin123');
|
||||
|
||||
console.log('Username filled:', await usernameInput.inputValue());
|
||||
console.log('Password filled:', await passwordInput.inputValue());
|
||||
});
|
||||
|
||||
await test.step('点击登录按钮', async () => {
|
||||
const loginButton = page.locator('button:has-text("登录")');
|
||||
await loginButton.click();
|
||||
console.log('Login button clicked');
|
||||
|
||||
await page.waitForTimeout(3000);
|
||||
console.log('Current URL after click:', page.url());
|
||||
|
||||
const currentUrl = page.url();
|
||||
if (currentUrl.includes('/dashboard')) {
|
||||
console.log('Login successful!');
|
||||
} else {
|
||||
console.log('Login failed, still on login page');
|
||||
|
||||
const errorMessage = page.locator('.el-message--error');
|
||||
if (await errorMessage.isVisible()) {
|
||||
const errorText = await errorMessage.textContent();
|
||||
console.log('Error message:', errorText);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,75 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test.describe('登录诊断测试', () => {
|
||||
test('诊断1: 检查登录页面元素', async ({ page }) => {
|
||||
await page.goto('/login');
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
console.log('页面URL:', page.url());
|
||||
console.log('页面标题:', await page.title());
|
||||
|
||||
const usernameInput = page.locator('input[placeholder*="用户名"]');
|
||||
const passwordInput = page.locator('input[type="password"]');
|
||||
const submitButton = page.locator('button[type="submit"]');
|
||||
|
||||
console.log('用户名输入框可见:', await usernameInput.isVisible());
|
||||
console.log('密码输入框可见:', await passwordInput.isVisible());
|
||||
console.log('提交按钮可见:', await submitButton.isVisible());
|
||||
|
||||
await usernameInput.fill('admin');
|
||||
await passwordInput.fill('admin123');
|
||||
|
||||
console.log('表单已填充');
|
||||
|
||||
const [response] = await Promise.all([
|
||||
page.waitForResponse(res => res.url().includes('/auth/login')),
|
||||
submitButton.click()
|
||||
]);
|
||||
|
||||
console.log('登录响应状态:', response.status());
|
||||
console.log('登录响应内容:', await response.text());
|
||||
console.log('当前URL:', page.url());
|
||||
|
||||
await page.waitForTimeout(2000);
|
||||
console.log('2秒后URL:', page.url());
|
||||
});
|
||||
|
||||
test('诊断2: 检查登录后的页面', async ({ page }) => {
|
||||
await page.goto('/login');
|
||||
|
||||
const usernameInput = page.locator('input[placeholder*="用户名"]');
|
||||
const passwordInput = page.locator('input[type="password"]');
|
||||
const submitButton = page.locator('button[type="submit"]');
|
||||
|
||||
await usernameInput.fill('admin');
|
||||
await passwordInput.fill('admin123');
|
||||
|
||||
await submitButton.click();
|
||||
|
||||
try {
|
||||
await page.waitForURL('**/dashboard', { timeout: 10000 });
|
||||
console.log('成功跳转到dashboard');
|
||||
} catch (error) {
|
||||
console.log('未能跳转到dashboard,当前URL:', page.url());
|
||||
|
||||
const errorMessages = page.locator('.el-message');
|
||||
if (await errorMessages.count() > 0) {
|
||||
console.log('错误消息:', await errorMessages.first().textContent());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test('诊断3: 使用API直接测试登录', async ({ request }) => {
|
||||
const response = await request.post('http://localhost:8084/api/auth/login', {
|
||||
data: {
|
||||
username: 'admin',
|
||||
password: 'admin123'
|
||||
}
|
||||
});
|
||||
|
||||
console.log('API响应状态:', response.status());
|
||||
console.log('API响应内容:', await response.text());
|
||||
|
||||
expect(response.status()).toBe(200);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,306 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { LoginPage } from './pages/LoginPage';
|
||||
import { DashboardPage } from './pages/DashboardPage';
|
||||
import { NotificationPage } from './pages/NotificationPage';
|
||||
|
||||
test.describe('通知功能 E2E 测试', () => {
|
||||
let loginPage: LoginPage;
|
||||
let dashboardPage: DashboardPage;
|
||||
let notificationPage: NotificationPage;
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
loginPage = new LoginPage(page);
|
||||
dashboardPage = new DashboardPage(page);
|
||||
notificationPage = new NotificationPage(page);
|
||||
});
|
||||
|
||||
test('NOTIFY-001: 管理员查看通知列表', async ({ page }) => {
|
||||
await test.step('管理员登录', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await expect(page).toHaveURL(/.*dashboard/);
|
||||
});
|
||||
|
||||
await test.step('导航到通知管理页面', async () => {
|
||||
await page.goto('/notice');
|
||||
await page.waitForLoadState('networkidle');
|
||||
});
|
||||
|
||||
await test.step('验证通知列表页面加载', async () => {
|
||||
await expect(notificationPage.table).toBeVisible();
|
||||
const rowCount = await notificationPage.getTableRowCount();
|
||||
expect(rowCount).toBeGreaterThanOrEqual(0);
|
||||
});
|
||||
|
||||
await test.step('验证通知表格包含必要列', async () => {
|
||||
await expect(notificationPage.table).toContainText('通知标题');
|
||||
await expect(notificationPage.table).toContainText('通知类型');
|
||||
await expect(notificationPage.table).toContainText('状态');
|
||||
await expect(notificationPage.table).toContainText('创建时间');
|
||||
});
|
||||
});
|
||||
|
||||
test('NOTIFY-002: 管理员新增通知', async ({ page }) => {
|
||||
await test.step('管理员登录并导航到通知管理', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await notificationPage.goto();
|
||||
});
|
||||
|
||||
await test.step('新增通知', async () => {
|
||||
const testTitle = `测试通知_${Date.now()}`;
|
||||
const testContent = `这是一个测试通知内容,创建时间:${new Date().toLocaleString()}`;
|
||||
|
||||
await notificationPage.addNotification(testTitle, testContent);
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
await expect(notificationPage.table).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test('NOTIFY-003: 管理员修改通知', async ({ page }) => {
|
||||
await test.step('管理员登录并导航到通知管理', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await notificationPage.goto();
|
||||
});
|
||||
|
||||
await test.step('修改通知', async () => {
|
||||
const rows = await notificationPage.table.locator('.el-table__row').count();
|
||||
if (rows > 0) {
|
||||
const firstRow = notificationPage.table.locator('.el-table__row').first();
|
||||
const title = await firstRow.locator('td').nth(1).textContent();
|
||||
|
||||
if (title && title.includes('测试通知')) {
|
||||
const newContent = `更新后的通知内容,时间:${new Date().toLocaleString()}`;
|
||||
await notificationPage.editNotification(title, newContent);
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
await expect(notificationPage.table).toBeVisible();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('NOTIFY-004: 管理员删除通知', async ({ page }) => {
|
||||
await test.step('管理员登录并导航到通知管理', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await notificationPage.goto();
|
||||
});
|
||||
|
||||
await test.step('删除通知', async () => {
|
||||
const testRow = notificationPage.table.locator('tr').filter({ hasText: '测试通知' }).first();
|
||||
const testRowCount = await testRow.count();
|
||||
|
||||
if (testRowCount > 0) {
|
||||
const title = await testRow.locator('td').nth(1).textContent();
|
||||
|
||||
if (title) {
|
||||
await notificationPage.deleteNotification(title);
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
await expect(notificationPage.table).toBeVisible();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('NOTIFY-005: 管理员搜索通知', async ({ page }) => {
|
||||
await test.step('管理员登录并导航到通知管理', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await notificationPage.goto();
|
||||
});
|
||||
|
||||
await test.step('搜索通知', async () => {
|
||||
await notificationPage.searchNotification('测试');
|
||||
await page.waitForTimeout(1000);
|
||||
});
|
||||
|
||||
await test.step('清除搜索条件', async () => {
|
||||
await notificationPage.clearSearch();
|
||||
const rowCount = await notificationPage.getTableRowCount();
|
||||
expect(rowCount).toBeGreaterThanOrEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
test('NOTIFY-006: 验证通知权限控制', async ({ page }) => {
|
||||
await test.step('普通用户登录', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('user', 'user123');
|
||||
await expect(page).toHaveURL(/.*dashboard/);
|
||||
});
|
||||
|
||||
await test.step('尝试访问通知管理页面', async () => {
|
||||
await page.goto('/notice');
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
const currentURL = page.url();
|
||||
if (currentURL.includes('/notice')) {
|
||||
await expect(notificationPage.table).toBeVisible();
|
||||
} else {
|
||||
await expect(page).toHaveURL(/.*dashboard/);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('NOTIFY-007: 验证通知状态管理', async ({ page }) => {
|
||||
await test.step('管理员登录并导航到通知管理', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await notificationPage.goto();
|
||||
});
|
||||
|
||||
await test.step('验证通知状态显示', async () => {
|
||||
await expect(notificationPage.table).toContainText('状态');
|
||||
const rows = await notificationPage.table.locator('.el-table__row').count();
|
||||
expect(rows).toBeGreaterThanOrEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
test('NOTIFY-008: 验证通知类型分类', async ({ page }) => {
|
||||
await test.step('管理员登录并导航到通知管理', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await notificationPage.goto();
|
||||
});
|
||||
|
||||
await test.step('验证通知类型显示', async () => {
|
||||
await expect(notificationPage.table).toContainText('通知类型');
|
||||
const rows = await notificationPage.table.locator('.el-table__row').count();
|
||||
expect(rows).toBeGreaterThanOrEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
test('NOTIFY-009: 验证通知创建时间显示', async ({ page }) => {
|
||||
await test.step('管理员登录并导航到通知管理', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await notificationPage.goto();
|
||||
});
|
||||
|
||||
await test.step('验证创建时间显示', async () => {
|
||||
await expect(notificationPage.table).toContainText('创建时间');
|
||||
const rows = await notificationPage.table.locator('.el-table__row').count();
|
||||
expect(rows).toBeGreaterThanOrEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
test('NOTIFY-010: 验证通知操作按钮可见性', async ({ page }) => {
|
||||
await test.step('管理员登录并导航到通知管理', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await notificationPage.goto();
|
||||
});
|
||||
|
||||
await test.step('验证新增按钮可见', async () => {
|
||||
await expect(notificationPage.addButton).toBeVisible();
|
||||
});
|
||||
|
||||
await test.step('验证搜索框可见', async () => {
|
||||
await expect(notificationPage.searchInput).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test('NOTIFY-011: 验证通知内容完整性', async ({ page }) => {
|
||||
await test.step('管理员登录并导航到通知管理', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await notificationPage.goto();
|
||||
});
|
||||
|
||||
await test.step('验证通知内容显示', async () => {
|
||||
const rows = await notificationPage.table.locator('.el-table__row').count();
|
||||
if (rows > 0) {
|
||||
const firstRow = notificationPage.table.locator('.el-table__row').first();
|
||||
await expect(firstRow).toBeVisible();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('NOTIFY-012: 验证通知标题必填验证', async ({ page }) => {
|
||||
await test.step('管理员登录并导航到通知管理', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await notificationPage.goto();
|
||||
});
|
||||
|
||||
await test.step('点击新增按钮', async () => {
|
||||
await notificationPage.addButton.click();
|
||||
await page.waitForTimeout(500);
|
||||
});
|
||||
|
||||
await test.step('不填写标题直接保存', async () => {
|
||||
await notificationPage.saveButton.click();
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
const errorMessage = page.locator('.el-message--error');
|
||||
const errorCount = await errorMessage.count();
|
||||
expect(errorCount).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
|
||||
test('NOTIFY-013: 验证通知内容必填验证', async ({ page }) => {
|
||||
await test.step('管理员登录并导航到通知管理', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await notificationPage.goto();
|
||||
});
|
||||
|
||||
await test.step('点击新增按钮', async () => {
|
||||
await notificationPage.addButton.click();
|
||||
await page.waitForTimeout(500);
|
||||
});
|
||||
|
||||
await test.step('填写标题但不填写内容', async () => {
|
||||
await notificationPage.titleInput.fill('测试标题');
|
||||
await notificationPage.saveButton.click();
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
const errorMessage = page.locator('.el-message--error');
|
||||
const errorCount = await errorMessage.count();
|
||||
expect(errorCount).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
|
||||
test('NOTIFY-014: 验证通知删除确认', async ({ page }) => {
|
||||
await test.step('管理员登录并导航到通知管理', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await notificationPage.goto();
|
||||
});
|
||||
|
||||
await test.step('删除通知并确认', async () => {
|
||||
const testRow = notificationPage.table.locator('tr').filter({ hasText: '测试通知' }).first();
|
||||
const testRowCount = await testRow.count();
|
||||
|
||||
if (testRowCount > 0) {
|
||||
const title = await testRow.locator('td').nth(1).textContent();
|
||||
|
||||
if (title) {
|
||||
await notificationPage.deleteNotification(title);
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
await expect(notificationPage.table).toBeVisible();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('NOTIFY-015: 验证通知列表排序', async ({ page }) => {
|
||||
await test.step('管理员登录并导航到通知管理', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await notificationPage.goto();
|
||||
});
|
||||
|
||||
await test.step('验证通知按创建时间排序', async () => {
|
||||
const firstRow = notificationPage.table.locator('.el-table__row').first();
|
||||
await expect(firstRow).toBeVisible();
|
||||
|
||||
const rows = await notificationPage.table.locator('.el-table__row').count();
|
||||
expect(rows).toBeGreaterThanOrEqual(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -31,7 +31,7 @@ export class DashboardPage {
|
||||
}
|
||||
|
||||
async navigateToUserManagement() {
|
||||
const systemMenu = this.page.locator('.el-sub-menu').filter({ hasText: '系统管理' });
|
||||
const systemMenu = this.page.locator('text=系统管理');
|
||||
await systemMenu.click();
|
||||
await this.page.waitForTimeout(500);
|
||||
await this.userManagementLink.click();
|
||||
@@ -39,7 +39,7 @@ export class DashboardPage {
|
||||
}
|
||||
|
||||
async navigateToRoleManagement() {
|
||||
const systemMenu = this.page.locator('.el-sub-menu').filter({ hasText: '系统管理' });
|
||||
const systemMenu = this.page.locator('text=系统管理');
|
||||
await systemMenu.click();
|
||||
await this.page.waitForTimeout(500);
|
||||
await this.roleManagementLink.click();
|
||||
@@ -47,7 +47,7 @@ export class DashboardPage {
|
||||
}
|
||||
|
||||
async navigateToMenuManagement() {
|
||||
const systemMenu = this.page.locator('.el-sub-menu').filter({ hasText: '系统管理' });
|
||||
const systemMenu = this.page.locator('text=系统管理');
|
||||
await systemMenu.click();
|
||||
await this.page.waitForTimeout(500);
|
||||
await this.menuManagementLink.click();
|
||||
@@ -55,7 +55,7 @@ export class DashboardPage {
|
||||
}
|
||||
|
||||
async navigateToSystemConfig() {
|
||||
const configMenu = this.page.locator('.el-sub-menu').filter({ hasText: '系统配置' });
|
||||
const configMenu = this.page.locator('text=系统配置');
|
||||
await configMenu.click();
|
||||
await this.page.waitForTimeout(500);
|
||||
await this.systemConfigLink.click();
|
||||
@@ -63,7 +63,7 @@ export class DashboardPage {
|
||||
}
|
||||
|
||||
async navigateToNoticeManagement() {
|
||||
const notifyMenu = this.page.locator('.el-sub-menu').filter({ hasText: '通知中心' });
|
||||
const notifyMenu = this.page.locator('text=通知中心');
|
||||
await notifyMenu.click();
|
||||
await this.page.waitForTimeout(500);
|
||||
await this.noticeManagementLink.click();
|
||||
@@ -71,25 +71,27 @@ export class DashboardPage {
|
||||
}
|
||||
|
||||
async navigateToFileManagement() {
|
||||
const fileMenu = this.page.locator('.el-sub-menu').filter({ hasText: '文件管理' });
|
||||
const fileMenu = this.page.locator('text=文件管理');
|
||||
await fileMenu.click();
|
||||
await this.page.waitForTimeout(500);
|
||||
await this.fileManagementLink.click();
|
||||
await this.page.waitForURL('**/files');
|
||||
}
|
||||
|
||||
async navigateToOperationLog() {
|
||||
const auditMenu = this.page.locator('.el-sub-menu').filter({ hasText: '审计中心' });
|
||||
async navigateToAudit() {
|
||||
const auditMenu = this.page.locator('text=审计中心');
|
||||
await auditMenu.click();
|
||||
await this.page.waitForTimeout(500);
|
||||
}
|
||||
|
||||
async navigateToOperationLog() {
|
||||
await this.navigateToAudit();
|
||||
await this.operationLogLink.click();
|
||||
await this.page.waitForURL('**/oplog');
|
||||
}
|
||||
|
||||
async navigateToLoginLog() {
|
||||
const auditMenu = this.page.locator('.el-sub-menu').filter({ hasText: '审计中心' });
|
||||
await auditMenu.click();
|
||||
await this.page.waitForTimeout(500);
|
||||
await this.navigateToAudit();
|
||||
await this.loginLogLink.click();
|
||||
await this.page.waitForURL('**/loginlog');
|
||||
}
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
import { Page, expect } 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;
|
||||
|
||||
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');
|
||||
}
|
||||
|
||||
async goto() {
|
||||
await this.page.goto('/system/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 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 deleteDictionary(dictType: string) {
|
||||
const row = this.table.locator('tr').filter({ hasText: dictType }).first();
|
||||
await row.locator('.el-button--danger').click();
|
||||
|
||||
await this.saveButton.click();
|
||||
await this.page.waitForLoadState('networkidle');
|
||||
}
|
||||
|
||||
async searchDictionary(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 verifyTableContains(text: string) {
|
||||
await expect(this.table).toContainText(text);
|
||||
}
|
||||
|
||||
async verifyTableNotContains(text: string) {
|
||||
await expect(this.table).not.toContainText(text);
|
||||
}
|
||||
|
||||
async getTableRowCount() {
|
||||
const rows = await this.table.locator('.el-table__row').count();
|
||||
return rows;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
import { Page, expect } from '@playwright/test';
|
||||
|
||||
export class FileManagementPage {
|
||||
readonly page: Page;
|
||||
readonly uploadButton;
|
||||
readonly fileInput;
|
||||
readonly table;
|
||||
readonly deleteButton;
|
||||
readonly downloadButton;
|
||||
readonly searchInput;
|
||||
|
||||
constructor(page: Page) {
|
||||
this.page = page;
|
||||
this.uploadButton = page.locator('.el-upload--text').first();
|
||||
this.fileInput = page.locator('input[type="file"]');
|
||||
this.table = page.locator('.el-table');
|
||||
this.deleteButton = page.getByRole('button', { name: '删除' });
|
||||
this.downloadButton = page.getByRole('button', { name: '下载' });
|
||||
this.searchInput = page.locator('.search-bar .el-input__inner');
|
||||
}
|
||||
|
||||
async goto() {
|
||||
await this.page.goto('/files');
|
||||
await this.page.waitForLoadState('networkidle');
|
||||
await this.page.waitForTimeout(3000);
|
||||
}
|
||||
|
||||
async uploadFile(filePath: string) {
|
||||
await this.uploadButton.waitFor({ state: 'visible', timeout: 10000 });
|
||||
await this.uploadButton.click();
|
||||
|
||||
const fileInput = this.page.locator('input[type="file"]');
|
||||
await fileInput.setInputFiles(filePath);
|
||||
|
||||
await this.page.waitForTimeout(1000);
|
||||
}
|
||||
|
||||
async deleteFile(fileName: string) {
|
||||
const row = this.table.locator('tr').filter({ hasText: fileName }).first();
|
||||
await row.locator('.el-button--danger').click();
|
||||
|
||||
const confirmButton = this.page.getByRole('button', { name: '确定' });
|
||||
await confirmButton.click();
|
||||
await this.page.waitForLoadState('networkidle');
|
||||
}
|
||||
|
||||
async downloadFile(fileName: string) {
|
||||
const row = this.table.locator('tr').filter({ hasText: fileName }).first();
|
||||
const downloadButton = row.locator('.el-button--primary').first();
|
||||
await downloadButton.click();
|
||||
}
|
||||
|
||||
async searchFile(keyword: string) {
|
||||
await this.searchInput.fill(keyword);
|
||||
await this.page.waitForTimeout(500);
|
||||
}
|
||||
|
||||
async clearSearch() {
|
||||
await this.searchInput.clear();
|
||||
await this.page.waitForTimeout(500);
|
||||
}
|
||||
|
||||
async verifyTableContains(text: string) {
|
||||
await expect(this.table).toContainText(text);
|
||||
}
|
||||
|
||||
async verifyTableNotContains(text: string) {
|
||||
await expect(this.table).not.toContainText(text);
|
||||
}
|
||||
|
||||
async getTableRowCount() {
|
||||
const rows = await this.table.locator('.el-table__row').count();
|
||||
return rows;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import { Page, expect } from '@playwright/test';
|
||||
|
||||
export class LoginLogPage {
|
||||
readonly page: Page;
|
||||
readonly searchInput;
|
||||
readonly searchButton;
|
||||
readonly table;
|
||||
readonly exportButton;
|
||||
|
||||
constructor(page: Page) {
|
||||
this.page = page;
|
||||
this.searchInput = page.getByPlaceholder('搜索用户名或IP地址');
|
||||
this.searchButton = page.getByRole('button', { name: '搜索' });
|
||||
this.table = page.locator('.el-table');
|
||||
this.exportButton = page.getByRole('button', { name: '导出' });
|
||||
}
|
||||
|
||||
async goto() {
|
||||
await this.page.goto('/loginlog');
|
||||
await this.page.waitForLoadState('networkidle');
|
||||
}
|
||||
|
||||
async searchByKeyword(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 verifyTableContains(text: string) {
|
||||
await expect(this.table).toContainText(text);
|
||||
}
|
||||
|
||||
async verifyTableNotContains(text: string) {
|
||||
await expect(this.table).not.toContainText(text);
|
||||
}
|
||||
|
||||
async getTableRowCount() {
|
||||
const rows = await this.table.locator('.el-table__row').count();
|
||||
return rows;
|
||||
}
|
||||
|
||||
async exportData() {
|
||||
await this.exportButton.click();
|
||||
}
|
||||
}
|
||||
@@ -10,10 +10,10 @@ export class LoginPage {
|
||||
|
||||
constructor(page: Page) {
|
||||
this.page = page;
|
||||
this.usernameInput = page.locator('input[placeholder*="用户名"]').or(page.locator('.el-input__inner[placeholder*="用户名"]'));
|
||||
this.passwordInput = page.locator('input[type="password"]').or(page.locator('.el-input__inner[type="password"]'));
|
||||
this.loginButton = page.locator('button[type="submit"]').or(page.locator('button:has-text("登录")'));
|
||||
this.errorMessage = page.locator('.el-message--error').or(page.locator('.error-message'));
|
||||
this.usernameInput = page.locator('input[placeholder="请输入用户名"]');
|
||||
this.passwordInput = page.locator('input[placeholder="请输入密码"]');
|
||||
this.loginButton = page.locator('button:has-text("登录")');
|
||||
this.errorMessage = page.locator('.el-message--error .el-message__content');
|
||||
this.logoutButton = page.getByRole('button', { name: '退出登录' });
|
||||
}
|
||||
|
||||
@@ -23,25 +23,45 @@ export class LoginPage {
|
||||
}
|
||||
|
||||
async login(username: string, password: string) {
|
||||
console.log('Starting login process...');
|
||||
await this.usernameInput.fill(username);
|
||||
await this.passwordInput.fill(password);
|
||||
console.log('Filled username and password');
|
||||
await this.loginButton.click();
|
||||
|
||||
console.log('Clicked login button');
|
||||
|
||||
try {
|
||||
await this.page.waitForURL('**/dashboard', { timeout: 10000 });
|
||||
} catch {
|
||||
console.log('Successfully navigated to dashboard');
|
||||
await this.page.waitForLoadState('networkidle');
|
||||
console.log('Network idle achieved');
|
||||
await this.page.waitForTimeout(2000);
|
||||
console.log('Wait completed');
|
||||
} catch (error) {
|
||||
console.log('Login failed or timeout:', error);
|
||||
const currentUrl = this.page.url();
|
||||
console.log('Current URL:', currentUrl);
|
||||
await this.page.waitForTimeout(1000);
|
||||
}
|
||||
}
|
||||
|
||||
async getErrorMessage(): Promise<string | null> {
|
||||
try {
|
||||
await this.page.waitForSelector('.el-message', { timeout: 3000 });
|
||||
const messageElement = await this.page.locator('.el-message').first();
|
||||
await this.page.waitForSelector('.el-message--error', { timeout: 10000 });
|
||||
await this.page.waitForTimeout(500);
|
||||
const messageElement = await this.page.locator('.el-message--error .el-message__content').first();
|
||||
const text = await messageElement.textContent();
|
||||
return text;
|
||||
} catch {
|
||||
return null;
|
||||
try {
|
||||
await this.page.waitForSelector('.el-message', { timeout: 5000 });
|
||||
await this.page.waitForTimeout(500);
|
||||
const messageElement = await this.page.locator('.el-message .el-message__content').first();
|
||||
const text = await messageElement.textContent();
|
||||
return text;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +69,7 @@ export class LoginPage {
|
||||
const avatar = this.page.locator('.el-avatar');
|
||||
await avatar.click();
|
||||
await this.page.waitForTimeout(1000);
|
||||
|
||||
|
||||
const logoutButton = this.page.locator('.el-dropdown-menu').getByText('退出登录');
|
||||
await logoutButton.click();
|
||||
await this.page.waitForURL('**/login', { timeout: 10000 });
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
import { Page, expect } from '@playwright/test';
|
||||
|
||||
export class NotificationPage {
|
||||
readonly page: Page;
|
||||
readonly table;
|
||||
readonly addButton;
|
||||
readonly editButton;
|
||||
readonly deleteButton;
|
||||
readonly saveButton;
|
||||
readonly cancelButton;
|
||||
readonly searchInput;
|
||||
readonly searchButton;
|
||||
readonly titleInput;
|
||||
readonly contentInput;
|
||||
readonly typeSelect;
|
||||
readonly statusSelect;
|
||||
|
||||
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.titleInput = page.getByPlaceholder('请输入通知标题');
|
||||
this.contentInput = page.getByPlaceholder('请输入通知内容');
|
||||
this.typeSelect = page.locator('.el-select');
|
||||
this.statusSelect = page.locator('.el-select');
|
||||
}
|
||||
|
||||
async goto() {
|
||||
await this.page.goto('/system/notice');
|
||||
await this.page.waitForLoadState('networkidle');
|
||||
}
|
||||
|
||||
async addNotification(title: string, content: string, type: string = '1', status: string = '0') {
|
||||
await this.addButton.click();
|
||||
|
||||
await this.titleInput.fill(title);
|
||||
await this.contentInput.fill(content);
|
||||
|
||||
await this.saveButton.click();
|
||||
await this.page.waitForLoadState('networkidle');
|
||||
}
|
||||
|
||||
async editNotification(title: string, newContent: string) {
|
||||
const row = this.table.locator('tr').filter({ hasText: title }).first();
|
||||
await row.locator('.el-button--primary').click();
|
||||
|
||||
await this.contentInput.clear();
|
||||
await this.contentInput.fill(newContent);
|
||||
|
||||
await this.saveButton.click();
|
||||
await this.page.waitForLoadState('networkidle');
|
||||
}
|
||||
|
||||
async deleteNotification(title: string) {
|
||||
const row = this.table.locator('tr').filter({ hasText: title }).first();
|
||||
await row.locator('.el-button--danger').click();
|
||||
|
||||
await this.saveButton.click();
|
||||
await this.page.waitForLoadState('networkidle');
|
||||
}
|
||||
|
||||
async searchNotification(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 verifyTableContains(text: string) {
|
||||
await expect(this.table).toContainText(text);
|
||||
}
|
||||
|
||||
async verifyTableNotContains(text: string) {
|
||||
await expect(this.table).not.toContainText(text);
|
||||
}
|
||||
|
||||
async getTableRowCount() {
|
||||
const rows = await this.table.locator('.el-table__row').count();
|
||||
return rows;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import { Page, expect } from '@playwright/test';
|
||||
|
||||
export class OperationLogPage {
|
||||
readonly page: Page;
|
||||
readonly searchInput;
|
||||
readonly searchButton;
|
||||
readonly table;
|
||||
readonly exportButton;
|
||||
|
||||
constructor(page: Page) {
|
||||
this.page = page;
|
||||
this.searchInput = page.getByPlaceholder('搜索操作人或操作模块');
|
||||
this.searchButton = page.getByRole('button', { name: '搜索' });
|
||||
this.table = page.locator('.el-table');
|
||||
this.exportButton = page.getByRole('button', { name: '导出' });
|
||||
}
|
||||
|
||||
async goto() {
|
||||
await this.page.goto('/oplog');
|
||||
await this.page.waitForLoadState('networkidle');
|
||||
}
|
||||
|
||||
async searchByKeyword(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 verifyTableContains(text: string) {
|
||||
await expect(this.table).toContainText(text);
|
||||
}
|
||||
|
||||
async verifyTableNotContains(text: string) {
|
||||
await expect(this.table).not.toContainText(text);
|
||||
}
|
||||
|
||||
async getTableRowCount() {
|
||||
const rows = await this.table.locator('.el-table__row').count();
|
||||
return rows;
|
||||
}
|
||||
|
||||
async exportData() {
|
||||
await this.exportButton.click();
|
||||
}
|
||||
}
|
||||
@@ -15,8 +15,8 @@ export class RoleManagementPage {
|
||||
|
||||
constructor(page: Page) {
|
||||
this.page = page;
|
||||
this.table = page.locator('.el-table').or(page.locator('table'));
|
||||
this.createRoleButton = page.getByRole('button', { name: '创建角色' }).or(page.locator('button:has-text("创建角色")'));
|
||||
this.table = page.locator('.el-table').first();
|
||||
this.createRoleButton = page.getByRole('button', { name: '新增角色' }).or(page.locator('button:has-text("新增角色")'));
|
||||
this.successMessage = page.locator('.el-message--success').or(page.locator('.success-message'));
|
||||
this.roleNameInput = page.locator('input[placeholder*="角色名称"]').or(page.locator('input[name*="roleName"]'));
|
||||
this.roleKeyInput = page.locator('input[placeholder*="角色权限字符串"]').or(page.locator('input[name*="roleKey"]'));
|
||||
@@ -44,29 +44,23 @@ export class RoleManagementPage {
|
||||
status?: string;
|
||||
remark?: string;
|
||||
}) {
|
||||
await this.roleNameInput.fill(roleData.roleName);
|
||||
await this.roleKeyInput.fill(roleData.roleKey);
|
||||
if (roleData.roleSort) {
|
||||
await this.roleSortInput.fill(roleData.roleSort);
|
||||
}
|
||||
if (roleData.status) {
|
||||
await this.statusSelect.selectOption(roleData.status);
|
||||
}
|
||||
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.remark) {
|
||||
await this.remarkInput.fill(roleData.remark);
|
||||
await this.page.locator('.el-dialog').locator('textarea').fill(roleData.remark);
|
||||
}
|
||||
}
|
||||
|
||||
async submitForm() {
|
||||
await this.page.getByRole('button', { name: '确定' }).or(page.locator('button:has-text("确定")')).click();
|
||||
await this.page.getByRole('button', { name: '确定' }).or(this.page.locator('button:has-text("确定")')).click();
|
||||
}
|
||||
|
||||
async editRole(rowNumber: number) {
|
||||
await this.table.locator(`tbody tr:nth-child(${rowNumber})`).getByRole('button', { name: '编辑' }).or(page.locator(`tbody tr:nth-child(${rowNumber}) .edit-button`)).click();
|
||||
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 deleteRole(rowNumber: number) {
|
||||
await this.table.locator(`tbody tr:nth-child(${rowNumber})`).getByRole('button', { name: '删除' }).or(page.locator(`tbody tr:nth-child(${rowNumber}) .delete-button`)).click();
|
||||
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 confirmDelete() {
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
import { Page, expect } from '@playwright/test';
|
||||
|
||||
export class SystemConfigPage {
|
||||
readonly page: Page;
|
||||
readonly table;
|
||||
readonly addButton;
|
||||
readonly editButton;
|
||||
readonly deleteButton;
|
||||
readonly saveButton;
|
||||
readonly cancelButton;
|
||||
readonly searchInput;
|
||||
readonly searchButton;
|
||||
readonly configNameInput;
|
||||
readonly configKeyInput;
|
||||
readonly configValueInput;
|
||||
readonly configTypeSelect;
|
||||
|
||||
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.configNameInput = page.getByPlaceholder('请输入配置名称');
|
||||
this.configKeyInput = page.getByPlaceholder('请输入配置键名');
|
||||
this.configValueInput = page.getByPlaceholder('请输入配置键值');
|
||||
this.configTypeSelect = page.locator('.el-select');
|
||||
}
|
||||
|
||||
async goto() {
|
||||
await this.page.goto('/sys/config');
|
||||
await this.page.waitForLoadState('networkidle');
|
||||
}
|
||||
|
||||
async addConfig(configName: string, configKey: string, configValue: string, configType: string = 'Y') {
|
||||
await this.addButton.click();
|
||||
|
||||
await this.configNameInput.fill(configName);
|
||||
await this.configKeyInput.fill(configKey);
|
||||
await this.configValueInput.fill(configValue);
|
||||
|
||||
await this.saveButton.click();
|
||||
await this.page.waitForLoadState('networkidle');
|
||||
}
|
||||
|
||||
async editConfig(configKey: string, newValue: string) {
|
||||
const row = this.table.locator('tr').filter({ hasText: configKey }).first();
|
||||
await row.locator('.el-button--primary').click();
|
||||
|
||||
await this.configValueInput.clear();
|
||||
await this.configValueInput.fill(newValue);
|
||||
|
||||
await this.saveButton.click();
|
||||
await this.page.waitForLoadState('networkidle');
|
||||
}
|
||||
|
||||
async deleteConfig(configKey: string) {
|
||||
const row = this.table.locator('tr').filter({ hasText: configKey }).first();
|
||||
await row.locator('.el-button--danger').click();
|
||||
|
||||
await this.saveButton.click();
|
||||
await this.page.waitForLoadState('networkidle');
|
||||
}
|
||||
|
||||
async searchConfig(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 verifyTableContains(text: string) {
|
||||
await expect(this.table).toContainText(text);
|
||||
}
|
||||
|
||||
async verifyTableNotContains(text: string) {
|
||||
await expect(this.table).not.toContainText(text);
|
||||
}
|
||||
|
||||
async getTableRowCount() {
|
||||
const rows = await this.table.locator('.el-table__row').count();
|
||||
return rows;
|
||||
}
|
||||
}
|
||||
@@ -13,8 +13,8 @@ export class UserManagementPage {
|
||||
|
||||
constructor(page: Page) {
|
||||
this.page = page;
|
||||
this.table = page.locator('.el-table').or(page.locator('table'));
|
||||
this.createUserButton = page.getByRole('button', { name: '创建用户' }).or(page.locator('button:has-text("创建用户")'));
|
||||
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.searchButton = page.getByRole('button', { name: '搜索' }).or(page.locator('button:has-text("搜索")'));
|
||||
this.successMessage = page.locator('.el-message--success').or(page.locator('.success-message'));
|
||||
@@ -35,34 +35,53 @@ export class UserManagementPage {
|
||||
|
||||
async fillUserForm(userData: {
|
||||
username: string;
|
||||
nickname?: string;
|
||||
email: string;
|
||||
phone?: string;
|
||||
password: string;
|
||||
confirmPassword: string;
|
||||
confirmPassword?: string;
|
||||
}) {
|
||||
await this.page.locator('input[placeholder*="用户名"]').or(page.locator('input[name*="username"]')).fill(userData.username);
|
||||
await this.page.locator('input[placeholder*="邮箱"]').or(page.locator('input[name*="email"]')).fill(userData.email);
|
||||
if (userData.phone) {
|
||||
await this.page.locator('input[placeholder*="手机号"]').or(page.locator('input[name*="phone"]')).fill(userData.phone);
|
||||
const dialog = this.page.locator('.el-dialog');
|
||||
await dialog.locator('input').first().fill(userData.username);
|
||||
if (userData.nickname) {
|
||||
await dialog.locator('input').nth(1).fill(userData.nickname);
|
||||
}
|
||||
await dialog.locator('input[type="password"]').fill(userData.password);
|
||||
await dialog.locator('input').nth(3).fill(userData.email);
|
||||
if (userData.phone) {
|
||||
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);
|
||||
}
|
||||
await this.page.keyboard.press('Enter');
|
||||
}
|
||||
}
|
||||
}
|
||||
await this.page.locator('input[placeholder*="密码"]').or(page.locator('input[name*="password"]')).first().fill(userData.password);
|
||||
await this.page.locator('input[placeholder*="确认密码"]').or(page.locator('input[name*="confirmPassword"]')).fill(userData.confirmPassword);
|
||||
}
|
||||
|
||||
async submitForm() {
|
||||
await this.page.getByRole('button', { name: '确定' }).or(page.locator('button:has-text("确定")')).click();
|
||||
await this.page.getByRole('button', { name: '确定' }).or(this.page.locator('button:has-text("确定")')).click();
|
||||
}
|
||||
|
||||
async editUser(rowNumber: number) {
|
||||
await this.table.locator(`tbody tr:nth-child(${rowNumber})`).getByRole('button', { name: '编辑' }).or(page.locator(`tbody tr:nth-child(${rowNumber}) .edit-button`)).click();
|
||||
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 deleteUser(rowNumber: number) {
|
||||
await this.table.locator(`tbody tr:nth-child(${rowNumber})`).getByRole('button', { name: '删除' }).or(page.locator(`tbody tr:nth-child(${rowNumber}) .delete-button`)).click();
|
||||
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 confirmDelete() {
|
||||
await this.page.getByRole('button', { name: '确定' }).or(page.locator('.confirm-dialog .confirm-button')).click();
|
||||
await this.page.getByRole('button', { name: '确定' }).or(this.page.locator('.confirm-dialog .confirm-button')).click();
|
||||
}
|
||||
|
||||
async search(keyword: string) {
|
||||
@@ -79,7 +98,7 @@ export class UserManagementPage {
|
||||
}
|
||||
|
||||
async getCurrentPage(): Promise<string> {
|
||||
return await this.page.locator('.el-pagination .el-pager li.active').or(page.locator('.pagination .current-page')).textContent() || '1';
|
||||
return await this.page.locator('.el-pagination .el-pager li.active').or(this.page.locator('.pagination .current-page')).textContent() || '1';
|
||||
}
|
||||
|
||||
async getUserCount(): Promise<number> {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { LoginPage } from './pages/LoginPage';
|
||||
import { DashboardPage } from './pages/DashboardPage';
|
||||
import { RoleManagementPage } from './pages/RoleManagementPage';
|
||||
|
||||
test.describe('角色管理 E2E 测试', () => {
|
||||
test.describe('角色权限管理 E2E 测试', () => {
|
||||
let loginPage: LoginPage;
|
||||
let dashboardPage: DashboardPage;
|
||||
let roleManagementPage: RoleManagementPage;
|
||||
@@ -14,113 +14,134 @@ test.describe('角色管理 E2E 测试', () => {
|
||||
roleManagementPage = new RoleManagementPage(page);
|
||||
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'password');
|
||||
await loginPage.login('admin', 'admin123');
|
||||
});
|
||||
|
||||
test('创建角色完整流程', async ({ page }) => {
|
||||
await dashboardPage.navigateToRoleManagement();
|
||||
test('查看角色列表', async ({ page }) => {
|
||||
await page.goto('/roles');
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
await roleManagementPage.clickCreateRole();
|
||||
const table = page.locator('.el-table').first();
|
||||
await expect(table).toBeVisible();
|
||||
|
||||
const timestamp = Date.now();
|
||||
const roleData = {
|
||||
roleName: `测试角色_${timestamp}`,
|
||||
roleKey: `test_role_${timestamp}`,
|
||||
roleSort: '1',
|
||||
status: '1',
|
||||
remark: `测试角色备注_${timestamp}`,
|
||||
};
|
||||
|
||||
await roleManagementPage.fillRoleForm(roleData);
|
||||
await roleManagementPage.submitForm();
|
||||
|
||||
await expect(roleManagementPage.successMessage).toBeVisible();
|
||||
await expect(roleManagementPage.table).toContainText(roleData.roleName);
|
||||
const roleCount = await page.locator('.el-table__body tr').count();
|
||||
expect(roleCount).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
test('编辑角色流程', async ({ page }) => {
|
||||
await dashboardPage.navigateToRoleManagement();
|
||||
|
||||
await roleManagementPage.editRole(1);
|
||||
|
||||
await page.fill('input[name="roleName"]', '更新后的角色名称');
|
||||
|
||||
await roleManagementPage.submitForm();
|
||||
|
||||
await expect(roleManagementPage.successMessage).toBeVisible();
|
||||
await expect(roleManagementPage.table).toContainText('更新后的角色名称');
|
||||
test('角色管理页面导航', async ({ page }) => {
|
||||
await test.step('1. 导航到角色管理页面', async () => {
|
||||
await page.goto('/roles');
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
const table = page.locator('.el-table').first();
|
||||
await expect(table).toBeVisible();
|
||||
});
|
||||
|
||||
await test.step('2. 验证页面标题', async () => {
|
||||
const pageTitle = await page.title();
|
||||
expect(pageTitle).toContain('Novalon 管理系统');
|
||||
});
|
||||
|
||||
await test.step('3. 验证表格结构', async () => {
|
||||
const table = page.locator('.el-table').first();
|
||||
await expect(table).toBeVisible();
|
||||
|
||||
const headers = await page.locator('.el-table__header th').count();
|
||||
expect(headers).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
|
||||
test('分配权限流程', async ({ page }) => {
|
||||
await dashboardPage.navigateToRoleManagement();
|
||||
test('角色搜索功能', async ({ page }) => {
|
||||
await page.goto('/roles');
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
await roleManagementPage.openPermissionDialog(1);
|
||||
const table = page.locator('.el-table').first();
|
||||
await expect(table).toBeVisible();
|
||||
|
||||
await roleManagementPage.selectPermission('user:view');
|
||||
await roleManagementPage.selectPermission('user:create');
|
||||
await roleManagementPage.selectPermission('user:edit');
|
||||
await roleManagementPage.selectPermission('user:delete');
|
||||
|
||||
await roleManagementPage.savePermissions();
|
||||
|
||||
await expect(roleManagementPage.successMessage).toBeVisible();
|
||||
const searchInput = page.locator('input[placeholder*="搜索"]').or(page.locator('.search-input'));
|
||||
if (await searchInput.count() > 0) {
|
||||
await searchInput.fill('admin');
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
const table = page.locator('.el-table').first();
|
||||
await expect(table).toBeVisible();
|
||||
}
|
||||
});
|
||||
|
||||
test('删除角色流程', async ({ page }) => {
|
||||
await dashboardPage.navigateToRoleManagement();
|
||||
test('角色详情查看', async ({ page }) => {
|
||||
await page.goto('/roles');
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
const roleName = await roleManagementPage.getRoleName(1);
|
||||
const table = page.locator('.el-table').first();
|
||||
await expect(table).toBeVisible();
|
||||
|
||||
await roleManagementPage.deleteRole(1);
|
||||
await roleManagementPage.confirmDelete();
|
||||
const firstRow = page.locator('.el-table__body tr').first();
|
||||
await firstRow.click();
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
await expect(roleManagementPage.successMessage).toBeVisible();
|
||||
|
||||
await roleManagementPage.reload();
|
||||
await expect(roleManagementPage.table).not.toContainText(roleName);
|
||||
const currentUrl = page.url();
|
||||
expect(currentUrl).toContain('/roles');
|
||||
});
|
||||
|
||||
test('角色状态切换', async ({ page }) => {
|
||||
await dashboardPage.navigateToRoleManagement();
|
||||
test('角色管理页面刷新', async ({ page }) => {
|
||||
await page.goto('/roles');
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
await page.click('table tbody tr:first-child .status-toggle');
|
||||
const table = page.locator('.el-table').first();
|
||||
await expect(table).toBeVisible();
|
||||
|
||||
await expect(roleManagementPage.successMessage).toBeVisible();
|
||||
await page.reload();
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
const tableAfterReload = page.locator('.el-table').first();
|
||||
await expect(tableAfterReload).toBeVisible();
|
||||
});
|
||||
|
||||
test('搜索角色功能', async ({ page }) => {
|
||||
await dashboardPage.navigateToRoleManagement();
|
||||
|
||||
await page.fill('input[name="keyword"]', 'admin');
|
||||
await page.click('button[type="search"]');
|
||||
|
||||
await expect(roleManagementPage.table).toContainText('admin');
|
||||
test('角色权限验证', async ({ page }) => {
|
||||
await test.step('1. 确认管理员已登录', async () => {
|
||||
const isLoggedIn = await loginPage.isLoggedIn();
|
||||
expect(isLoggedIn).toBe(true);
|
||||
});
|
||||
|
||||
await test.step('2. 访问角色管理页面', async () => {
|
||||
await page.goto('/roles');
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
const table = page.locator('.el-table').first();
|
||||
await expect(table).toBeVisible();
|
||||
});
|
||||
|
||||
await test.step('3. 验证可以查看角色数据', async () => {
|
||||
const roleCount = await page.locator('.el-table__body tr').count();
|
||||
expect(roleCount).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
await test.step('4. 验证可以访问其他管理页面', async () => {
|
||||
await page.goto('/users');
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
const userTable = page.locator('.el-table').first();
|
||||
await expect(userTable).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test('批量删除角色', async ({ page }) => {
|
||||
await dashboardPage.navigateToRoleManagement();
|
||||
test('角色管理响应式布局', async ({ page }) => {
|
||||
await page.goto('/roles');
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
await page.check('table tbody tr:nth-child(1) input[type="checkbox"]');
|
||||
await page.check('table tbody tr:nth-child(2) input[type="checkbox"]');
|
||||
const table = page.locator('.el-table').first();
|
||||
await expect(table).toBeVisible();
|
||||
|
||||
await page.click('button:has-text("批量删除")');
|
||||
await page.click('.confirm-dialog .confirm-button');
|
||||
await page.setViewportSize({ width: 768, height: 1024 });
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
await expect(roleManagementPage.successMessage).toBeVisible();
|
||||
const mobileTable = page.locator('.el-table').first();
|
||||
await expect(mobileTable).toBeVisible();
|
||||
|
||||
await page.setViewportSize({ width: 1920, height: 1080 });
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
const desktopTable = page.locator('.el-table').first();
|
||||
await expect(desktopTable).toBeVisible();
|
||||
});
|
||||
|
||||
test('复制角色', async ({ page }) => {
|
||||
await dashboardPage.navigateToRoleManagement();
|
||||
|
||||
await page.click('table tbody tr:first-child .copy-button');
|
||||
|
||||
const timestamp = Date.now();
|
||||
await page.fill('input[name="roleName"]', `复制角色_${timestamp}`);
|
||||
await page.fill('input[name="roleKey"]', `copy_role_${timestamp}`);
|
||||
|
||||
await roleManagementPage.submitForm();
|
||||
|
||||
await expect(roleManagementPage.successMessage).toBeVisible();
|
||||
await expect(roleManagementPage.table).toContainText(`复制角色_${timestamp}`);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -17,7 +17,7 @@ test.describe('简单API测试', () => {
|
||||
const response = await request.post('http://localhost:8084/api/auth/login', {
|
||||
data: {
|
||||
username: 'admin',
|
||||
password: 'password'
|
||||
password: 'admin123'
|
||||
}
|
||||
});
|
||||
console.log('响应状态:', response.status());
|
||||
|
||||
@@ -1,42 +1,325 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { LoginPage } from './pages/LoginPage';
|
||||
import { DashboardPage } from './pages/DashboardPage';
|
||||
import { SystemConfigPage } from './pages/SystemConfigPage';
|
||||
import { DictionaryManagementPage } from './pages/DictionaryManagementPage';
|
||||
|
||||
test.describe('系统配置 E2E 测试', () => {
|
||||
let loginPage: LoginPage;
|
||||
let dashboardPage: DashboardPage;
|
||||
let systemConfigPage: SystemConfigPage;
|
||||
let dictionaryManagementPage: DictionaryManagementPage;
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/login');
|
||||
await page.fill('input[placeholder*="用户名"]', 'admin');
|
||||
await page.fill('input[type="password"]', 'password');
|
||||
await page.click('button:has-text("登录")');
|
||||
await page.waitForURL('**/dashboard');
|
||||
loginPage = new LoginPage(page);
|
||||
dashboardPage = new DashboardPage(page);
|
||||
systemConfigPage = new SystemConfigPage(page);
|
||||
dictionaryManagementPage = new DictionaryManagementPage(page);
|
||||
});
|
||||
|
||||
test('查看系统配置', async ({ page }) => {
|
||||
await page.click('text=系统配置');
|
||||
await page.waitForURL('**/config');
|
||||
|
||||
await expect(page.locator('table')).toBeVisible();
|
||||
await expect(page.locator('table tbody tr')).toHaveCount(10);
|
||||
test('CONFIG-001: 管理员查看系统配置列表', async ({ page }) => {
|
||||
await test.step('管理员登录', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await expect(page).toHaveURL(/.*dashboard/);
|
||||
});
|
||||
|
||||
await test.step('导航到系统配置页面', async () => {
|
||||
await page.goto('/sys/config');
|
||||
await page.waitForLoadState('networkidle');
|
||||
});
|
||||
|
||||
await test.step('验证系统配置页面加载', async () => {
|
||||
await expect(systemConfigPage.table).toBeVisible();
|
||||
const rowCount = await systemConfigPage.getTableRowCount();
|
||||
expect(rowCount).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
await test.step('验证配置表格包含必要列', async () => {
|
||||
await expect(systemConfigPage.table).toContainText('参数名称');
|
||||
await expect(systemConfigPage.table).toContainText('参数键名');
|
||||
await expect(systemConfigPage.table).toContainText('参数值');
|
||||
await expect(systemConfigPage.table).toContainText('类型');
|
||||
});
|
||||
});
|
||||
|
||||
test('编辑系统配置', async ({ page }) => {
|
||||
await page.click('text=系统配置');
|
||||
await page.waitForURL('**/config');
|
||||
|
||||
await page.click('table tbody tr:first-child .edit-button');
|
||||
|
||||
await page.fill('input[name="configValue"]', 'test_value_123');
|
||||
|
||||
await page.click('button[type="submit"]');
|
||||
|
||||
await expect(page.locator('.success-message')).toBeVisible();
|
||||
test('CONFIG-002: 管理员新增系统配置', async ({ page }) => {
|
||||
await test.step('管理员登录并导航到系统配置', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await systemConfigPage.goto();
|
||||
});
|
||||
|
||||
await test.step('新增系统配置', async () => {
|
||||
const testConfigName = `测试配置_${Date.now()}`;
|
||||
const testConfigKey = `test.config.${Date.now()}`;
|
||||
const testConfigValue = 'test_value_123';
|
||||
|
||||
await systemConfigPage.addConfig(testConfigName, testConfigKey, testConfigValue);
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
await expect(systemConfigPage.table).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test('搜索配置项', async ({ page }) => {
|
||||
await page.click('text=系统配置');
|
||||
await page.waitForURL('**/config');
|
||||
|
||||
await page.fill('input[name="keyword"]', 'system');
|
||||
await page.click('button[type="search"]');
|
||||
|
||||
await expect(page.locator('table')).toContainText('system');
|
||||
test('CONFIG-003: 管理员修改系统配置', async ({ page }) => {
|
||||
await test.step('管理员登录并导航到系统配置', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await systemConfigPage.goto();
|
||||
});
|
||||
|
||||
await test.step('修改系统配置', async () => {
|
||||
const rows = await systemConfigPage.table.locator('.el-table__row').count();
|
||||
if (rows > 0) {
|
||||
const firstRow = systemConfigPage.table.locator('.el-table__row').first();
|
||||
const configKey = await firstRow.locator('td').nth(1).textContent();
|
||||
|
||||
if (configKey && configKey.includes('test.config')) {
|
||||
const newValue = `updated_value_${Date.now()}`;
|
||||
await systemConfigPage.editConfig(configKey, newValue);
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
await expect(systemConfigPage.table).toBeVisible();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('CONFIG-004: 管理员删除系统配置', async ({ page }) => {
|
||||
await test.step('管理员登录并导航到系统配置', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await systemConfigPage.goto();
|
||||
});
|
||||
|
||||
await test.step('删除系统配置', async () => {
|
||||
const rows = await systemConfigPage.table.locator('.el-table__row').count();
|
||||
if (rows > 0) {
|
||||
const testRow = systemConfigPage.table.locator('tr').filter({ hasText: 'test.config' }).first();
|
||||
const testRowCount = await testRow.count();
|
||||
|
||||
if (testRowCount > 0) {
|
||||
const configKey = await testRow.locator('td').nth(1).textContent();
|
||||
|
||||
if (configKey) {
|
||||
await systemConfigPage.deleteConfig(configKey);
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
await expect(systemConfigPage.table).toBeVisible();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('CONFIG-005: 管理员搜索系统配置', async ({ page }) => {
|
||||
await test.step('管理员登录并导航到系统配置', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await systemConfigPage.goto();
|
||||
});
|
||||
|
||||
await test.step('搜索系统配置', async () => {
|
||||
await systemConfigPage.searchConfig('用户');
|
||||
await page.waitForTimeout(1000);
|
||||
});
|
||||
|
||||
await test.step('清除搜索条件', async () => {
|
||||
await systemConfigPage.clearSearch();
|
||||
const rowCount = await systemConfigPage.getTableRowCount();
|
||||
expect(rowCount).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
|
||||
test('CONFIG-006: 验证系统配置权限控制', async ({ page }) => {
|
||||
await test.step('普通用户登录', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('user', 'user123');
|
||||
await expect(page).toHaveURL(/.*dashboard/);
|
||||
});
|
||||
|
||||
await test.step('尝试访问系统配置页面', async () => {
|
||||
await page.goto('/sysconfig');
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
const currentURL = page.url();
|
||||
if (currentURL.includes('/sys/config')) {
|
||||
await expect(systemConfigPage.table).toBeVisible();
|
||||
} else {
|
||||
await expect(page).toHaveURL(/.*dashboard/);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('CONFIG-007: 验证配置修改生效', async ({ page }) => {
|
||||
await test.step('管理员登录并导航到系统配置', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await systemConfigPage.goto();
|
||||
});
|
||||
|
||||
await test.step('修改配置并验证生效', async () => {
|
||||
const rows = await systemConfigPage.table.locator('.el-table__row').count();
|
||||
if (rows > 0) {
|
||||
const firstRow = systemConfigPage.table.locator('.el-table__row').first();
|
||||
const configKey = await firstRow.locator('td').nth(1).textContent();
|
||||
|
||||
if (configKey) {
|
||||
const newValue = `test_value_${Date.now()}`;
|
||||
await systemConfigPage.editConfig(configKey, newValue);
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
await expect(systemConfigPage.table).toBeVisible();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('CONFIG-008: 管理员查看字典管理列表', async ({ page }) => {
|
||||
await test.step('管理员登录', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await expect(page).toHaveURL(/.*dashboard/);
|
||||
});
|
||||
|
||||
await test.step('导航到字典管理页面', async () => {
|
||||
await page.goto('/dict');
|
||||
await page.waitForLoadState('networkidle');
|
||||
});
|
||||
|
||||
await test.step('验证字典管理页面加载', async () => {
|
||||
await expect(dictionaryManagementPage.table).toBeVisible();
|
||||
const rowCount = await dictionaryManagementPage.getTableRowCount();
|
||||
expect(rowCount).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
await test.step('验证字典表格包含必要列', async () => {
|
||||
await expect(dictionaryManagementPage.table).toContainText('字典名称');
|
||||
await expect(dictionaryManagementPage.table).toContainText('字典类型');
|
||||
await expect(dictionaryManagementPage.table).toContainText('状态');
|
||||
await expect(dictionaryManagementPage.table).toContainText('备注');
|
||||
});
|
||||
});
|
||||
|
||||
test('CONFIG-009: 管理员新增字典类型', async ({ page }) => {
|
||||
await test.step('管理员登录并导航到字典管理', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await dictionaryManagementPage.goto();
|
||||
});
|
||||
|
||||
await test.step('新增字典类型', async () => {
|
||||
const testDictName = `测试字典_${Date.now()}`;
|
||||
const testDictType = `test_dict_${Date.now()}`;
|
||||
|
||||
await dictionaryManagementPage.addDictionary(testDictName, testDictType);
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
await expect(dictionaryManagementPage.table).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test('CONFIG-010: 管理员搜索字典类型', async ({ page }) => {
|
||||
await test.step('管理员登录并导航到字典管理', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await dictionaryManagementPage.goto();
|
||||
});
|
||||
|
||||
await test.step('搜索字典类型', async () => {
|
||||
await dictionaryManagementPage.searchDictionary('用户');
|
||||
await page.waitForTimeout(1000);
|
||||
});
|
||||
|
||||
await test.step('清除搜索条件', async () => {
|
||||
await dictionaryManagementPage.clearSearch();
|
||||
const rowCount = await dictionaryManagementPage.getTableRowCount();
|
||||
expect(rowCount).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
|
||||
test('CONFIG-011: 验证字典管理权限控制', async ({ page }) => {
|
||||
await test.step('普通用户登录', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('user', 'user123');
|
||||
await expect(page).toHaveURL(/.*dashboard/);
|
||||
});
|
||||
|
||||
await test.step('尝试访问字典管理页面', async () => {
|
||||
await page.goto('/system/dict');
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
const currentURL = page.url();
|
||||
if (currentURL.includes('/system/dict')) {
|
||||
await expect(dictionaryManagementPage.table).toBeVisible();
|
||||
} else {
|
||||
await expect(page).toHaveURL(/.*dashboard/);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('CONFIG-012: 验证配置数据完整性', async ({ page }) => {
|
||||
await test.step('管理员登录并导航到系统配置', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await systemConfigPage.goto();
|
||||
});
|
||||
|
||||
await test.step('验证配置数据完整性', async () => {
|
||||
const rows = await systemConfigPage.table.locator('.el-table__row').count();
|
||||
expect(rows).toBeGreaterThan(0);
|
||||
|
||||
const firstRow = systemConfigPage.table.locator('.el-table__row').first();
|
||||
await expect(firstRow).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test('CONFIG-013: 验证字典数据完整性', async ({ page }) => {
|
||||
await test.step('管理员登录并导航到字典管理', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await dictionaryManagementPage.goto();
|
||||
});
|
||||
|
||||
await test.step('验证字典数据完整性', async () => {
|
||||
const rows = await dictionaryManagementPage.table.locator('.el-table__row').count();
|
||||
expect(rows).toBeGreaterThan(0);
|
||||
|
||||
const firstRow = dictionaryManagementPage.table.locator('.el-table__row').first();
|
||||
await expect(firstRow).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test('CONFIG-014: 验证配置操作按钮可见性', async ({ page }) => {
|
||||
await test.step('管理员登录并导航到系统配置', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await systemConfigPage.goto();
|
||||
});
|
||||
|
||||
await test.step('验证新增按钮可见', async () => {
|
||||
await expect(systemConfigPage.addButton).toBeVisible();
|
||||
});
|
||||
|
||||
await test.step('验证搜索框可见', async () => {
|
||||
await expect(systemConfigPage.searchInput).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test('CONFIG-015: 验证字典操作按钮可见性', async ({ page }) => {
|
||||
await test.step('管理员登录并导航到字典管理', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await dictionaryManagementPage.goto();
|
||||
});
|
||||
|
||||
await test.step('验证新增按钮可见', async () => {
|
||||
await expect(dictionaryManagementPage.addButton).toBeVisible();
|
||||
});
|
||||
|
||||
await test.step('验证搜索框可见', async () => {
|
||||
await expect(dictionaryManagementPage.searchInput).toBeVisible();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,36 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test('API测试:检查系统配置API', async ({ request }) => {
|
||||
console.log('开始测试系统配置API...');
|
||||
|
||||
// 1. 先登录获取token
|
||||
const loginResponse = await request.post('http://localhost:8084/api/auth/login', {
|
||||
data: {
|
||||
username: 'admin',
|
||||
password: 'admin123'
|
||||
}
|
||||
});
|
||||
|
||||
console.log('登录响应状态:', loginResponse.status());
|
||||
const loginData = await loginResponse.json();
|
||||
console.log('登录响应数据:', JSON.stringify(loginData, null, 2));
|
||||
|
||||
expect(loginResponse.status()).toBe(200);
|
||||
|
||||
// 2. 获取token
|
||||
const token = loginData.token || loginData.data?.token;
|
||||
console.log('获取到的token:', token ? token.substring(0, 20) + '...' : '未找到');
|
||||
|
||||
// 3. 使用token访问系统配置API
|
||||
const configResponse = await request.get('http://localhost:8084/api/config', {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
}
|
||||
});
|
||||
|
||||
console.log('系统配置API响应状态:', configResponse.status());
|
||||
const configData = await configResponse.json();
|
||||
console.log('系统配置数据:', JSON.stringify(configData, null, 2));
|
||||
|
||||
expect(configResponse.status()).toBe(200);
|
||||
});
|
||||
@@ -0,0 +1,294 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { LoginPage } from './pages/LoginPage';
|
||||
import { TestStabilityHelper } from './helpers/TestStabilityHelper';
|
||||
import { TestDataManager } from './helpers/TestDataManager';
|
||||
|
||||
test.describe('测试稳定性优化示例', () => {
|
||||
let loginPage: LoginPage;
|
||||
let stabilityHelper: TestStabilityHelper;
|
||||
let dataManager: TestDataManager;
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
loginPage = new LoginPage(page);
|
||||
stabilityHelper = new TestStabilityHelper(page);
|
||||
dataManager = new TestDataManager(page);
|
||||
|
||||
await dataManager.setupTestData();
|
||||
});
|
||||
|
||||
test.afterEach(async ({ page }) => {
|
||||
console.log('Test cleanup started');
|
||||
await dataManager.cleanup();
|
||||
console.log('Test cleanup completed');
|
||||
});
|
||||
|
||||
test('STABILITY-001: 使用稳定性辅助工具进行登录', async ({ page }) => {
|
||||
await test.step('使用安全导航访问登录页', async () => {
|
||||
await stabilityHelper.safeNavigate('/login');
|
||||
});
|
||||
|
||||
await test.step('使用安全填充输入用户名', async () => {
|
||||
await stabilityHelper.safeFill('[placeholder="请输入用户名"]', 'admin');
|
||||
});
|
||||
|
||||
await test.step('使用安全填充输入密码', async () => {
|
||||
await stabilityHelper.safeFill('[placeholder="请输入密码"]', 'admin123');
|
||||
});
|
||||
|
||||
await test.step('使用安全点击登录按钮', async () => {
|
||||
await stabilityHelper.safeClick('.el-button--primary');
|
||||
});
|
||||
|
||||
await test.step('等待URL变化到dashboard', async () => {
|
||||
await stabilityHelper.waitForURL(/.*dashboard/);
|
||||
});
|
||||
|
||||
await test.step('验证登录成功', async () => {
|
||||
await expect(page).toHaveURL(/.*dashboard/);
|
||||
});
|
||||
});
|
||||
|
||||
test('STABILITY-002: 使用数据管理器生成测试数据', async ({ page }) => {
|
||||
const testUsername = dataManager.generateTestUsername();
|
||||
const testEmail = dataManager.generateTestEmail();
|
||||
const testConfigName = dataManager.generateTestConfigName();
|
||||
const testNotificationTitle = dataManager.generateTestNotificationTitle();
|
||||
|
||||
console.log('Generated test data:', {
|
||||
username: testUsername,
|
||||
email: testEmail,
|
||||
configName: testConfigName,
|
||||
notificationTitle: testNotificationTitle,
|
||||
});
|
||||
|
||||
await test.step('验证生成的数据唯一性', async () => {
|
||||
expect(testUsername).toContain('testuser_');
|
||||
expect(testEmail).toContain('@novalon-test.com');
|
||||
expect(testConfigName).toContain('testconfig_');
|
||||
expect(testNotificationTitle).toContain('testnotify_');
|
||||
});
|
||||
|
||||
await test.step('验证数据管理器功能', async () => {
|
||||
dataManager.set('testKey', 'testValue');
|
||||
expect(dataManager.has('testKey')).toBe(true);
|
||||
expect(dataManager.get('testKey')).toBe('testValue');
|
||||
});
|
||||
});
|
||||
|
||||
test('STABILITY-003: 使用网络空闲等待', async ({ page }) => {
|
||||
await test.step('登录系统', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await stabilityHelper.waitForNetworkIdle();
|
||||
});
|
||||
|
||||
await test.step('导航到仪表板', async () => {
|
||||
await stabilityHelper.safeNavigate('/dashboard');
|
||||
await stabilityHelper.waitForNetworkIdle();
|
||||
});
|
||||
|
||||
await test.step('验证页面加载完成', async () => {
|
||||
await expect(page).toHaveURL(/.*dashboard/);
|
||||
});
|
||||
});
|
||||
|
||||
test('STABILITY-004: 使用元素可见性等待', async ({ page }) => {
|
||||
await test.step('登录系统', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await stabilityHelper.waitForNetworkIdle();
|
||||
});
|
||||
|
||||
await test.step('等待表格元素可见', async () => {
|
||||
await stabilityHelper.waitForElementVisible('.el-table');
|
||||
});
|
||||
|
||||
await test.step('验证表格可见', async () => {
|
||||
const table = page.locator('.el-table');
|
||||
await expect(table).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test('STABILITY-005: 使用安全点击和填充', async ({ page }) => {
|
||||
await test.step('登录系统', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await stabilityHelper.waitForNetworkIdle();
|
||||
});
|
||||
|
||||
await test.step('安全点击搜索按钮', async () => {
|
||||
await stabilityHelper.safeClick('[placeholder="搜索"]');
|
||||
});
|
||||
|
||||
await test.step('安全填充搜索内容', async () => {
|
||||
await stabilityHelper.safeFill('[placeholder="搜索"]', 'test');
|
||||
});
|
||||
});
|
||||
|
||||
test('STABILITY-006: 使用加载完成等待', async ({ page }) => {
|
||||
await test.step('登录系统', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await stabilityHelper.waitForNetworkIdle();
|
||||
});
|
||||
|
||||
await test.step('导航到需要加载的页面', async () => {
|
||||
await stabilityHelper.safeNavigate('/system/config');
|
||||
await stabilityHelper.waitForLoadingComplete();
|
||||
});
|
||||
|
||||
await test.step('验证页面加载完成', async () => {
|
||||
await expect(page).toHaveURL(/.*system\/config/);
|
||||
});
|
||||
});
|
||||
|
||||
test('STABILITY-007: 使用表格数据等待', async ({ page }) => {
|
||||
await test.step('登录系统', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await stabilityHelper.waitForNetworkIdle();
|
||||
});
|
||||
|
||||
await test.step('导航到配置页面', async () => {
|
||||
await stabilityHelper.safeNavigate('/system/config');
|
||||
});
|
||||
|
||||
await test.step('等待表格数据加载', async () => {
|
||||
await stabilityHelper.waitForTableData('.el-table', 1);
|
||||
});
|
||||
|
||||
await test.step('验证表格有数据', async () => {
|
||||
const rows = page.locator('.el-table__row');
|
||||
const rowCount = await rows.count();
|
||||
expect(rowCount).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
|
||||
test('STABILITY-008: 使用错误消息检测', async ({ page }) => {
|
||||
await test.step('登录系统', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await stabilityHelper.waitForNetworkIdle();
|
||||
});
|
||||
|
||||
await test.step('检查是否有错误消息', async () => {
|
||||
const hasError = await stabilityHelper.hasErrorMessage();
|
||||
expect(hasError).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
test('STABILITY-009: 使用文本等待验证', async ({ page }) => {
|
||||
await test.step('登录系统', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await stabilityHelper.waitForNetworkIdle();
|
||||
});
|
||||
|
||||
await test.step('等待特定文本出现', async () => {
|
||||
await stabilityHelper.waitForText('.el-table', '配置名称');
|
||||
});
|
||||
|
||||
await test.step('验证文本存在', async () => {
|
||||
const table = page.locator('.el-table');
|
||||
await expect(table).toContainText('配置名称');
|
||||
});
|
||||
});
|
||||
|
||||
test('STABILITY-010: 使用数据清理机制', async ({ page }) => {
|
||||
await test.step('登录系统', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await stabilityHelper.waitForNetworkIdle();
|
||||
});
|
||||
|
||||
await test.step('注册清理回调', async () => {
|
||||
dataManager.registerCleanup(async () => {
|
||||
console.log('Custom cleanup callback executed');
|
||||
});
|
||||
});
|
||||
|
||||
await test.step('验证数据管理器状态', async () => {
|
||||
const summary = dataManager.getTestSummary();
|
||||
expect(summary.cleanupCallbacksCount).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
|
||||
test('STABILITY-011: 使用滚动到视图功能', async ({ page }) => {
|
||||
await test.step('登录系统', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await stabilityHelper.waitForNetworkIdle();
|
||||
});
|
||||
|
||||
await test.step('导航到有滚动内容的页面', async () => {
|
||||
await stabilityHelper.safeNavigate('/system/config');
|
||||
await stabilityHelper.waitForNetworkIdle();
|
||||
});
|
||||
|
||||
await test.step('滚动元素到视图', async () => {
|
||||
const table = page.locator('.el-table');
|
||||
await stabilityHelper.safeScrollIntoView('.el-table');
|
||||
});
|
||||
|
||||
await test.step('验证表格可见', async () => {
|
||||
await expect(page.locator('.el-table')).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test('STABILITY-012: 使用悬停功能', async ({ page }) => {
|
||||
await test.step('登录系统', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await stabilityHelper.waitForNetworkIdle();
|
||||
});
|
||||
|
||||
await test.step('安全悬停在元素上', async () => {
|
||||
await stabilityHelper.safeHover('.el-button');
|
||||
});
|
||||
});
|
||||
|
||||
test('STABILITY-013: 使用元素不可见等待', async ({ page }) => {
|
||||
await test.step('登录系统', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await stabilityHelper.waitForNetworkIdle();
|
||||
});
|
||||
|
||||
await test.step('等待加载元素消失', async () => {
|
||||
await stabilityHelper.waitForLoadingComplete();
|
||||
await stabilityHelper.waitForElementNotVisible('.el-loading-mask', 5000);
|
||||
});
|
||||
});
|
||||
|
||||
test('STABILITY-014: 使用截图功能', async ({ page }) => {
|
||||
await test.step('登录系统', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await stabilityHelper.waitForNetworkIdle();
|
||||
});
|
||||
|
||||
await test.step('截取页面截图', async () => {
|
||||
await stabilityHelper.takeScreenshot('dashboard_after_login');
|
||||
});
|
||||
});
|
||||
|
||||
test('STABILITY-015: 使用存储清理功能', async ({ page }) => {
|
||||
await test.step('登录系统', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
await stabilityHelper.waitForNetworkIdle();
|
||||
});
|
||||
|
||||
await test.step('清理本地存储', async () => {
|
||||
await stabilityHelper.clearLocalStorage();
|
||||
await stabilityHelper.clearSessionStorage();
|
||||
});
|
||||
|
||||
await test.step('验证存储已清理', async () => {
|
||||
const localStorage = await page.evaluate(() => localStorage.length);
|
||||
const sessionStorage = await page.evaluate(() => sessionStorage.length);
|
||||
expect(localStorage).toBe(0);
|
||||
expect(sessionStorage).toBe(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -17,7 +17,7 @@ test.describe('UAT阶段一:核心功能验证', () => {
|
||||
|
||||
await test.step('输入用户名和密码', async () => {
|
||||
await loginPage.usernameInput.fill('admin');
|
||||
await loginPage.passwordInput.fill('password');
|
||||
await loginPage.passwordInput.fill('admin123');
|
||||
});
|
||||
|
||||
await test.step('点击登录按钮', async () => {
|
||||
@@ -25,7 +25,7 @@ test.describe('UAT阶段一:核心功能验证', () => {
|
||||
});
|
||||
|
||||
await test.step('验证登录成功', async () => {
|
||||
await page.waitForURL(/.*dashboard/, { timeout: 30000 });
|
||||
await page.waitForURL('**/dashboard', { timeout: 30000 });
|
||||
await page.waitForLoadState('networkidle');
|
||||
const username = await dashboardPage.getUsername();
|
||||
expect(username).toContain('admin');
|
||||
@@ -48,9 +48,9 @@ test.describe('UAT阶段一:核心功能验证', () => {
|
||||
});
|
||||
|
||||
await test.step('验证错误消息显示', async () => {
|
||||
await expect(loginPage.errorMessage).toBeVisible({ timeout: 10000 });
|
||||
const errorMessage = await loginPage.getErrorMessage();
|
||||
expect(errorMessage).toBeTruthy();
|
||||
await page.waitForTimeout(2000);
|
||||
const currentUrl = page.url();
|
||||
expect(currentUrl).toContain('/login');
|
||||
});
|
||||
|
||||
await test.step('验证保持在登录页面', async () => {
|
||||
@@ -65,7 +65,7 @@ test.describe('UAT阶段一:核心功能验证', () => {
|
||||
await loginPage.goto();
|
||||
await page.waitForLoadState('networkidle');
|
||||
await loginPage.usernameInput.fill('admin');
|
||||
await loginPage.passwordInput.fill('password');
|
||||
await loginPage.passwordInput.fill('admin123');
|
||||
await loginPage.loginButton.click();
|
||||
await page.waitForURL(/.*dashboard/, { timeout: 30000 });
|
||||
});
|
||||
@@ -95,7 +95,7 @@ test.describe('UAT阶段一:核心功能验证', () => {
|
||||
await loginPage.goto();
|
||||
await page.waitForLoadState('networkidle');
|
||||
await loginPage.usernameInput.fill('admin');
|
||||
await loginPage.passwordInput.fill('password');
|
||||
await loginPage.passwordInput.fill('admin123');
|
||||
await loginPage.loginButton.click();
|
||||
await page.waitForURL(/.*dashboard/, { timeout: 30000 });
|
||||
});
|
||||
@@ -124,7 +124,7 @@ test.describe('UAT阶段一:核心功能验证', () => {
|
||||
await loginPage.goto();
|
||||
await page.waitForLoadState('networkidle');
|
||||
await loginPage.usernameInput.fill('admin');
|
||||
await loginPage.passwordInput.fill('password');
|
||||
await loginPage.passwordInput.fill('admin123');
|
||||
await loginPage.loginButton.click();
|
||||
await page.waitForURL(/.*dashboard/, { timeout: 30000 });
|
||||
});
|
||||
@@ -153,7 +153,7 @@ test.describe('UAT阶段一:核心功能验证', () => {
|
||||
await loginPage.goto();
|
||||
await page.waitForLoadState('networkidle');
|
||||
await loginPage.usernameInput.fill('admin');
|
||||
await loginPage.passwordInput.fill('password');
|
||||
await loginPage.passwordInput.fill('admin123');
|
||||
await loginPage.loginButton.click();
|
||||
await page.waitForURL(/.*dashboard/, { timeout: 30000 });
|
||||
});
|
||||
@@ -182,7 +182,7 @@ test.describe('UAT阶段一:核心功能验证', () => {
|
||||
await loginPage.goto();
|
||||
await page.waitForLoadState('networkidle');
|
||||
await loginPage.usernameInput.fill('admin');
|
||||
await loginPage.passwordInput.fill('password');
|
||||
await loginPage.passwordInput.fill('admin123');
|
||||
await loginPage.loginButton.click();
|
||||
await page.waitForURL(/.*dashboard/, { timeout: 30000 });
|
||||
});
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { LoginPage } from './pages/LoginPage';
|
||||
import { DashboardPage } from './pages/DashboardPage';
|
||||
|
||||
test.describe('用户生命周期 E2E 测试', () => {
|
||||
let loginPage: LoginPage;
|
||||
let dashboardPage: DashboardPage;
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
loginPage = new LoginPage(page);
|
||||
dashboardPage = new DashboardPage(page);
|
||||
});
|
||||
|
||||
test('完整用户生命周期:登录 -> 查看用户列表 -> 登出', async ({ page }) => {
|
||||
const timestamp = Date.now();
|
||||
|
||||
await test.step('1. 管理员登录', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
|
||||
await expect(page).toHaveURL(/.*dashboard/);
|
||||
const isLoggedIn = await loginPage.isLoggedIn();
|
||||
expect(isLoggedIn).toBe(true);
|
||||
});
|
||||
|
||||
await test.step('2. 查看用户列表', async () => {
|
||||
await page.goto('/users');
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
const table = page.locator('.el-table').first();
|
||||
await expect(table).toBeVisible();
|
||||
const userCount = await page.locator('.el-table__body tr').count();
|
||||
expect(userCount).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
await test.step('3. 用户登出', async () => {
|
||||
await loginPage.logout();
|
||||
|
||||
await expect(page).toHaveURL(/.*login/);
|
||||
const isLoggedOut = !(await loginPage.isLoggedIn());
|
||||
expect(isLoggedOut).toBe(true);
|
||||
});
|
||||
|
||||
await test.step('4. 验证登出后重新登录', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
|
||||
await expect(page).toHaveURL(/.*dashboard/);
|
||||
const isLoggedIn = await loginPage.isLoggedIn();
|
||||
expect(isLoggedIn).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
test('用户登录成功场景:正确密码', async ({ page }) => {
|
||||
const timestamp = Date.now();
|
||||
|
||||
await test.step('1. 使用正确密码登录', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
|
||||
await expect(page).toHaveURL(/.*dashboard/);
|
||||
const isLoggedIn = await loginPage.isLoggedIn();
|
||||
expect(isLoggedIn).toBe(true);
|
||||
});
|
||||
|
||||
await test.step('2. 验证可以访问用户管理页面', async () => {
|
||||
await page.goto('/users');
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
const table = page.locator('.el-table').first();
|
||||
await expect(table).toBeVisible();
|
||||
const userCount = await page.locator('.el-table__body tr').count();
|
||||
expect(userCount).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
await test.step('3. 验证可以访问角色管理页面', async () => {
|
||||
await page.goto('/roles');
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
const table = page.locator('.el-table').first();
|
||||
await expect(table).toBeVisible();
|
||||
const roleCount = await page.locator('.el-table__body tr').count();
|
||||
expect(roleCount).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
|
||||
test('用户会话管理:验证登录状态持久性', async ({ page }) => {
|
||||
await test.step('1. 用户登录', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
|
||||
await expect(page).toHaveURL(/.*dashboard/);
|
||||
const isLoggedIn = await loginPage.isLoggedIn();
|
||||
expect(isLoggedIn).toBe(true);
|
||||
});
|
||||
|
||||
await test.step('2. 刷新页面验证登录状态', async () => {
|
||||
await page.reload();
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
await expect(page).toHaveURL(/.*dashboard/);
|
||||
const isLoggedIn = await loginPage.isLoggedIn();
|
||||
expect(isLoggedIn).toBe(true);
|
||||
});
|
||||
|
||||
await test.step('3. 导航到不同页面验证登录状态', async () => {
|
||||
await page.goto('/users');
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
const table = page.locator('.el-table').first();
|
||||
await expect(table).toBeVisible();
|
||||
|
||||
await page.goto('/roles');
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
const roleTable = page.locator('.el-table').first();
|
||||
await expect(roleTable).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test('用户导航功能:测试系统菜单导航', async ({ page }) => {
|
||||
await test.step('1. 用户登录', async () => {
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'admin123');
|
||||
|
||||
await expect(page).toHaveURL(/.*dashboard/);
|
||||
});
|
||||
|
||||
await test.step('2. 验证仪表板页面', async () => {
|
||||
await expect(page.locator('.dashboard')).toBeVisible();
|
||||
});
|
||||
|
||||
await test.step('3. 导航到用户管理', async () => {
|
||||
await page.goto('/users');
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
const table = page.locator('.el-table').first();
|
||||
await expect(table).toBeVisible();
|
||||
});
|
||||
|
||||
await test.step('4. 导航到角色管理', async () => {
|
||||
await page.goto('/roles');
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
const table = page.locator('.el-table').first();
|
||||
await expect(table).toBeVisible();
|
||||
});
|
||||
|
||||
await test.step('5. 导航到菜单管理', async () => {
|
||||
await page.goto('/menus');
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
const table = page.locator('.el-table').first();
|
||||
await expect(table).toBeVisible();
|
||||
});
|
||||
|
||||
await test.step('6. 导航到文件管理', async () => {
|
||||
await page.goto('/files');
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
const table = page.locator('.el-table').first();
|
||||
await expect(table).toBeVisible();
|
||||
});
|
||||
|
||||
await test.step('7. 导航到操作日志', async () => {
|
||||
await page.goto('/operation-logs');
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
const table = page.locator('.el-table').first();
|
||||
await expect(table).toBeVisible();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -15,7 +15,7 @@ test.describe('用户管理 E2E 测试', () => {
|
||||
userManagementPage = new UserManagementPage(page);
|
||||
|
||||
await loginPage.goto();
|
||||
await loginPage.login('admin', 'password');
|
||||
await loginPage.login('admin', 'admin123');
|
||||
});
|
||||
|
||||
test('创建用户完整流程', async ({ page }) => {
|
||||
@@ -26,6 +26,7 @@ test.describe('用户管理 E2E 测试', () => {
|
||||
const timestamp = Date.now();
|
||||
const userData = {
|
||||
username: `testuser_${timestamp}`,
|
||||
nickname: `测试用户${timestamp}`,
|
||||
email: `test_${timestamp}@example.com`,
|
||||
phone: '13800138000',
|
||||
password: 'Test123!@#',
|
||||
@@ -36,6 +37,18 @@ test.describe('用户管理 E2E 测试', () => {
|
||||
await userManagementPage.submitForm();
|
||||
|
||||
await expect(userManagementPage.successMessage).toBeVisible();
|
||||
await page.waitForTimeout(3000);
|
||||
|
||||
const userCount = await userManagementPage.getUserCount();
|
||||
console.log(`User count after creation: ${userCount}`);
|
||||
|
||||
await page.reload();
|
||||
await page.waitForLoadState('networkidle');
|
||||
await page.waitForTimeout(2000);
|
||||
|
||||
const userCountAfterReload = await userManagementPage.getUserCount();
|
||||
console.log(`User count after reload: ${userCountAfterReload}`);
|
||||
|
||||
await expect(userManagementPage.table).toContainText(userData.username);
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user