test(e2e): 改进 Playwright 测试框架和 E2E 测试
- 更新 Playwright 配置,添加认证状态管理和 setup 项目 - 优化 E2E 测试用例,简化测试流程 - 添加 auth-debug.spec.ts 用于调试认证问题 - 添加 playwright/.auth/user.json 认证状态文件
This commit is contained in:
@@ -7,7 +7,7 @@ setup('authenticate', async ({ page }) => {
|
|||||||
await page.waitForLoadState('networkidle');
|
await page.waitForLoadState('networkidle');
|
||||||
|
|
||||||
await page.locator('input[placeholder*="用户名"]').fill('admin');
|
await page.locator('input[placeholder*="用户名"]').fill('admin');
|
||||||
await page.locator('input[placeholder*="密码"]').fill('admin123');
|
await page.locator('input[placeholder*="密码"]').fill('Test@123');
|
||||||
await page.locator('button:has-text("登录")').click();
|
await page.locator('button:has-text("登录")').click();
|
||||||
|
|
||||||
await page.waitForURL('**/dashboard', { timeout: 30000 });
|
await page.waitForURL('**/dashboard', { timeout: 30000 });
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
|
||||||
|
test.describe('认证状态测试', () => {
|
||||||
|
test('验证认证状态是否正常', async ({ page }) => {
|
||||||
|
await page.goto('/dashboard');
|
||||||
|
await page.waitForLoadState('networkidle');
|
||||||
|
|
||||||
|
const token = await page.evaluate(() => localStorage.getItem('token'));
|
||||||
|
console.log('Token:', token);
|
||||||
|
|
||||||
|
await expect(page).not.toHaveURL(/.*login/);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import { test, expect } from '@playwright/test';
|
import { test, expect } from '@playwright/test';
|
||||||
|
|
||||||
test.describe('管理员完整工作流', () => {
|
test.describe('管理员完整工作流', () => {
|
||||||
|
test.use({ storageState: 'playwright/.auth/user.json' });
|
||||||
test.describe.configure({ mode: 'serial' });
|
test.describe.configure({ mode: 'serial' });
|
||||||
|
|
||||||
const timestamp = Date.now();
|
const timestamp = Date.now();
|
||||||
@@ -11,9 +12,24 @@ test.describe('管理员完整工作流', () => {
|
|||||||
test('创建角色并分配权限', async ({ page }) => {
|
test('创建角色并分配权限', async ({ page }) => {
|
||||||
await test.step('导航到角色管理', async () => {
|
await test.step('导航到角色管理', async () => {
|
||||||
await page.goto('/dashboard');
|
await page.goto('/dashboard');
|
||||||
await page.waitForLoadState('networkidle');
|
await page.waitForLoadState('domcontentloaded');
|
||||||
|
await page.waitForTimeout(1000);
|
||||||
|
const token = await page.evaluate(() => localStorage.getItem('token'));
|
||||||
|
console.log('Token in journey test:', token ? 'exists' : 'missing');
|
||||||
|
|
||||||
|
const permission = await page.evaluate(() => localStorage.getItem('permission'));
|
||||||
|
console.log('Permission in journey test:', permission ? 'exists' : 'missing');
|
||||||
|
if (permission) {
|
||||||
|
const permData = JSON.parse(permission);
|
||||||
|
console.log('Has system:role:add:', permData.permissions?.includes('system:role:add'));
|
||||||
|
}
|
||||||
|
|
||||||
|
await page.waitForTimeout(2000);
|
||||||
|
|
||||||
|
await page.waitForSelector('text=系统管理', { state: 'visible', timeout: 10000 });
|
||||||
await page.locator('text=系统管理').click();
|
await page.locator('text=系统管理').click();
|
||||||
await page.waitForTimeout(500);
|
await page.waitForTimeout(500);
|
||||||
|
await page.waitForSelector('text=角色管理', { state: 'visible', timeout: 5000 });
|
||||||
await page.locator('text=角色管理').click();
|
await page.locator('text=角色管理').click();
|
||||||
await page.waitForLoadState('networkidle');
|
await page.waitForLoadState('networkidle');
|
||||||
await expect(page).toHaveURL(/.*roles/, { timeout: 10000 });
|
await expect(page).toHaveURL(/.*roles/, { timeout: 10000 });
|
||||||
@@ -32,9 +48,30 @@ test.describe('管理员完整工作流', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await test.step('提交表单', async () => {
|
await test.step('提交表单', async () => {
|
||||||
await page.locator('.el-dialog button:has-text("确定")').click();
|
const [response] = await Promise.all([
|
||||||
|
page.waitForResponse(resp =>
|
||||||
|
resp.url().includes('/api/roles') && resp.request().method() === 'POST',
|
||||||
|
{ timeout: 10000 }
|
||||||
|
).catch(() => null),
|
||||||
|
page.locator('.el-dialog button:has-text("确定")').click()
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (response) {
|
||||||
|
console.log('Response status:', response.status());
|
||||||
|
console.log('Response URL:', response.url());
|
||||||
|
} else {
|
||||||
|
console.log('No response received - request may have been blocked by frontend');
|
||||||
|
}
|
||||||
|
|
||||||
await page.waitForSelector('.el-dialog', { state: 'hidden', timeout: 10000 });
|
await page.waitForSelector('.el-dialog', { state: 'hidden', timeout: 10000 });
|
||||||
await expect(page.locator('.el-message--success')).toBeVisible({ timeout: 5000 });
|
|
||||||
|
if (response && response.ok()) {
|
||||||
|
await expect(page.locator('.el-message--success')).toBeVisible({ timeout: 5000 });
|
||||||
|
} else {
|
||||||
|
const errorMsg = await page.locator('.el-message--error').textContent().catch(() => 'Unknown error');
|
||||||
|
console.log('Error message:', errorMsg);
|
||||||
|
throw new Error(`创建角色失败: ${errorMsg}`);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -42,8 +79,10 @@ test.describe('管理员完整工作流', () => {
|
|||||||
await test.step('导航到用户管理', async () => {
|
await test.step('导航到用户管理', async () => {
|
||||||
await page.goto('/dashboard');
|
await page.goto('/dashboard');
|
||||||
await page.waitForLoadState('networkidle');
|
await page.waitForLoadState('networkidle');
|
||||||
|
await page.waitForSelector('text=系统管理', { state: 'visible', timeout: 10000 });
|
||||||
await page.locator('text=系统管理').click();
|
await page.locator('text=系统管理').click();
|
||||||
await page.waitForTimeout(500);
|
await page.waitForTimeout(500);
|
||||||
|
await page.waitForSelector('text=用户管理', { state: 'visible', timeout: 5000 });
|
||||||
await page.locator('text=用户管理').click();
|
await page.locator('text=用户管理').click();
|
||||||
await page.waitForLoadState('networkidle');
|
await page.waitForLoadState('networkidle');
|
||||||
await expect(page).toHaveURL(/.*users/, { timeout: 10000 });
|
await expect(page).toHaveURL(/.*users/, { timeout: 10000 });
|
||||||
@@ -138,7 +177,8 @@ test.describe('管理员完整工作流', () => {
|
|||||||
test('验证新用户登录', async ({ page }) => {
|
test('验证新用户登录', async ({ page }) => {
|
||||||
await test.step('管理员登出', async () => {
|
await test.step('管理员登出', async () => {
|
||||||
await page.goto('/dashboard');
|
await page.goto('/dashboard');
|
||||||
await page.waitForLoadState('networkidle');
|
await page.waitForLoadState('domcontentloaded');
|
||||||
|
await page.waitForTimeout(1000);
|
||||||
|
|
||||||
const avatarButton = page.locator('.el-avatar').first();
|
const avatarButton = page.locator('.el-avatar').first();
|
||||||
await avatarButton.click({ timeout: 10000 });
|
await avatarButton.click({ timeout: 10000 });
|
||||||
|
|||||||
@@ -19,13 +19,16 @@ test.describe('审计工作流', () => {
|
|||||||
|
|
||||||
await test.step('导航到操作日志', async () => {
|
await test.step('导航到操作日志', async () => {
|
||||||
await page.goto('/dashboard');
|
await page.goto('/dashboard');
|
||||||
await page.waitForLoadState('networkidle');
|
await page.waitForLoadState('domcontentloaded');
|
||||||
|
await page.waitForTimeout(1000);
|
||||||
|
|
||||||
|
await page.locator('text=审计日志').waitFor({ state: 'visible', timeout: 5000 });
|
||||||
await page.locator('text=审计日志').click();
|
await page.locator('text=审计日志').click();
|
||||||
await page.waitForTimeout(1000);
|
await page.waitForTimeout(1000);
|
||||||
|
|
||||||
|
await page.locator('.el-menu-item:has-text("操作日志")').waitFor({ state: 'visible', timeout: 5000 });
|
||||||
await page.locator('.el-menu-item:has-text("操作日志")').click();
|
await page.locator('.el-menu-item:has-text("操作日志")').click();
|
||||||
await page.waitForLoadState('networkidle');
|
await page.waitForLoadState('domcontentloaded');
|
||||||
await page.waitForTimeout(1000);
|
await page.waitForTimeout(1000);
|
||||||
|
|
||||||
await expect(page).toHaveURL(/.*oplog/, { timeout: 10000 });
|
await expect(page).toHaveURL(/.*oplog/, { timeout: 10000 });
|
||||||
@@ -42,16 +45,19 @@ test.describe('审计工作流', () => {
|
|||||||
test('查看登录日志', async ({ page }) => {
|
test('查看登录日志', async ({ page }) => {
|
||||||
await test.step('导航到登录日志', async () => {
|
await test.step('导航到登录日志', async () => {
|
||||||
await page.goto('/dashboard');
|
await page.goto('/dashboard');
|
||||||
await page.waitForLoadState('networkidle');
|
await page.waitForLoadState('domcontentloaded');
|
||||||
|
await page.waitForTimeout(1000);
|
||||||
|
|
||||||
|
await page.locator('text=审计日志').waitFor({ state: 'visible', timeout: 5000 });
|
||||||
await page.locator('text=审计日志').click();
|
await page.locator('text=审计日志').click();
|
||||||
await page.waitForTimeout(1000);
|
await page.waitForTimeout(1000);
|
||||||
|
|
||||||
|
await page.locator('.el-menu-item:has-text("登录日志")').waitFor({ state: 'visible', timeout: 5000 });
|
||||||
await page.locator('.el-menu-item:has-text("登录日志")').click();
|
await page.locator('.el-menu-item:has-text("登录日志")').click();
|
||||||
await page.waitForLoadState('networkidle');
|
await page.waitForLoadState('domcontentloaded');
|
||||||
await page.waitForTimeout(1000);
|
await page.waitForTimeout(1000);
|
||||||
|
|
||||||
await expect(page).toHaveURL(/.*loginlog/, { timeout: 10000 });
|
await expect(page).toHaveURL(/.*loginlog/, { timeout: 15000 });
|
||||||
});
|
});
|
||||||
|
|
||||||
await test.step('验证登录日志显示', async () => {
|
await test.step('验证登录日志显示', async () => {
|
||||||
@@ -65,13 +71,16 @@ test.describe('审计工作流', () => {
|
|||||||
test('搜索和筛选日志', async ({ page }) => {
|
test('搜索和筛选日志', async ({ page }) => {
|
||||||
await test.step('导航到操作日志', async () => {
|
await test.step('导航到操作日志', async () => {
|
||||||
await page.goto('/dashboard');
|
await page.goto('/dashboard');
|
||||||
await page.waitForLoadState('networkidle');
|
await page.waitForLoadState('domcontentloaded');
|
||||||
|
await page.waitForTimeout(1000);
|
||||||
|
|
||||||
|
await page.locator('text=审计日志').waitFor({ state: 'visible', timeout: 5000 });
|
||||||
await page.locator('text=审计日志').click();
|
await page.locator('text=审计日志').click();
|
||||||
await page.waitForTimeout(1000);
|
await page.waitForTimeout(1000);
|
||||||
|
|
||||||
|
await page.locator('.el-menu-item:has-text("操作日志")').waitFor({ state: 'visible', timeout: 5000 });
|
||||||
await page.locator('.el-menu-item:has-text("操作日志")').click();
|
await page.locator('.el-menu-item:has-text("操作日志")').click();
|
||||||
await page.waitForLoadState('networkidle');
|
await page.waitForLoadState('domcontentloaded');
|
||||||
await page.waitForTimeout(1000);
|
await page.waitForTimeout(1000);
|
||||||
|
|
||||||
await expect(page.locator('.el-table')).toBeVisible({ timeout: 10000 });
|
await expect(page.locator('.el-table')).toBeVisible({ timeout: 10000 });
|
||||||
|
|||||||
@@ -1,138 +1,73 @@
|
|||||||
import { test, expect } from '@playwright/test';
|
import { test, expect } from '@playwright/test';
|
||||||
|
|
||||||
test.describe('数据字典管理完整工作流', () => {
|
test.describe('字典管理完整工作流', () => {
|
||||||
test.describe.configure({ mode: 'serial' });
|
test.describe.configure({ mode: 'serial' });
|
||||||
|
|
||||||
const timestamp = Date.now();
|
const timestamp = Date.now();
|
||||||
const dictType = `test_dict_type_${timestamp}`;
|
|
||||||
const dictName = `测试字典_${timestamp}`;
|
const dictName = `测试字典_${timestamp}`;
|
||||||
const dictCode = `test_dict_code_${timestamp}`;
|
const dictType = `test_dict_${timestamp}`;
|
||||||
|
|
||||||
test('创建字典类型', async ({ page }) => {
|
test('创建字典', async ({ page }) => {
|
||||||
await test.step('导航到数据字典管理', async () => {
|
await test.step('导航到字典管理', async () => {
|
||||||
await page.goto('/dashboard');
|
await page.goto('/dashboard');
|
||||||
await page.waitForLoadState('networkidle');
|
await page.waitForLoadState('domcontentloaded');
|
||||||
|
await page.waitForTimeout(1000);
|
||||||
|
await page.locator('text=系统管理').waitFor({ state: 'visible', timeout: 5000 });
|
||||||
await page.locator('text=系统管理').click();
|
await page.locator('text=系统管理').click();
|
||||||
await page.waitForTimeout(500);
|
await page.waitForTimeout(500);
|
||||||
await page.locator('text=数据字典').click();
|
await page.locator('text=字典管理').waitFor({ state: 'visible', timeout: 5000 });
|
||||||
await page.waitForLoadState('networkidle');
|
await page.locator('text=字典管理').click();
|
||||||
await expect(page).toHaveURL(/.*dicts/, { timeout: 10000 });
|
await page.waitForLoadState('domcontentloaded');
|
||||||
|
await expect(page).toHaveURL(/.*dict/, { timeout: 15000 });
|
||||||
});
|
});
|
||||||
|
|
||||||
await test.step('切换到字典类型标签页', async () => {
|
await test.step('点击新增字典按钮', async () => {
|
||||||
await page.locator('.el-tabs__item:has-text("字典类型")').click();
|
await page.locator('button:has-text("新增字典")').click();
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
});
|
|
||||||
|
|
||||||
await test.step('点击新增字典类型按钮', async () => {
|
|
||||||
await page.locator('button:has-text("新增字典类型")').click();
|
|
||||||
await page.waitForSelector('.el-dialog', { state: 'visible', timeout: 5000 });
|
await page.waitForSelector('.el-dialog', { state: 'visible', timeout: 5000 });
|
||||||
});
|
});
|
||||||
|
|
||||||
await test.step('填写字典类型信息', async () => {
|
await test.step('填写字典信息', async () => {
|
||||||
const dialog = page.locator('.el-dialog');
|
const dialog = page.locator('.el-dialog');
|
||||||
await dialog.locator('input').first().fill(dictType);
|
await dialog.locator('input').first().fill(dictName);
|
||||||
await dialog.locator('input').nth(1).fill(`测试字典类型_${timestamp}`);
|
await dialog.locator('input').nth(1).fill(dictType);
|
||||||
await dialog.locator('textarea').fill(`这是测试字典类型的备注信息,时间戳:${timestamp}`);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
await test.step('提交字典类型表单', async () => {
|
await test.step('提交表单', async () => {
|
||||||
await page.locator('.el-dialog button:has-text("确定")').click();
|
await page.locator('.el-dialog button:has-text("确定")').click();
|
||||||
await page.waitForSelector('.el-dialog', { state: 'hidden', timeout: 10000 });
|
await page.waitForSelector('.el-dialog', { state: 'hidden', timeout: 10000 });
|
||||||
await expect(page.locator('.el-message--success')).toBeVisible({ timeout: 5000 });
|
await expect(page.locator('.el-message--success')).toBeVisible({ timeout: 5000 });
|
||||||
});
|
});
|
||||||
|
|
||||||
await test.step('验证字典类型已创建', async () => {
|
await test.step('验证字典已创建', async () => {
|
||||||
await page.locator('input[placeholder="请输入字典类型"]').fill(dictType);
|
await page.waitForTimeout(2000);
|
||||||
await page.locator('button:has-text("查询")').click();
|
const dictRow = page.locator(`tr:has-text("${dictName}")`);
|
||||||
await page.waitForLoadState('networkidle');
|
await expect(dictRow).toBeVisible({ timeout: 15000 });
|
||||||
|
|
||||||
const dictTypeRow = page.locator(`tr:has-text("${dictType}")`);
|
|
||||||
await expect(dictTypeRow).toBeVisible({ timeout: 10000 });
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('创建字典数据', async ({ page }) => {
|
test('编辑字典', async ({ page }) => {
|
||||||
await test.step('导航到数据字典管理', async () => {
|
|
||||||
await page.goto('/dashboard');
|
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
await page.locator('text=系统管理').click();
|
|
||||||
await page.waitForTimeout(500);
|
|
||||||
await page.locator('text=数据字典').click();
|
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
});
|
|
||||||
|
|
||||||
await test.step('切换到字典数据标签页', async () => {
|
|
||||||
await page.locator('.el-tabs__item:has-text("字典数据")').click();
|
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
});
|
|
||||||
|
|
||||||
await test.step('点击新增字典数据按钮', async () => {
|
|
||||||
await page.locator('button:has-text("新增字典数据")').click();
|
|
||||||
await page.waitForSelector('.el-dialog', { state: 'visible', timeout: 5000 });
|
|
||||||
});
|
|
||||||
|
|
||||||
await test.step('填写字典数据信息', async () => {
|
|
||||||
const dialog = page.locator('.el-dialog');
|
|
||||||
|
|
||||||
// 选择字典类型
|
|
||||||
await dialog.locator('.el-select').first().click();
|
|
||||||
await page.locator(`.el-select-dropdown:visible .el-select-dropdown__item:has-text("${dictType}")`).click();
|
|
||||||
|
|
||||||
await dialog.locator('input').nth(1).fill(dictName);
|
|
||||||
await dialog.locator('input').nth(2).fill(dictCode);
|
|
||||||
await dialog.locator('.el-input-number .el-input__inner').fill('99');
|
|
||||||
await dialog.locator('textarea').fill(`这是测试字典数据的备注信息,时间戳:${timestamp}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
await test.step('提交字典数据表单', async () => {
|
|
||||||
await page.locator('.el-dialog button:has-text("确定")').click();
|
|
||||||
await page.waitForSelector('.el-dialog', { state: 'hidden', timeout: 10000 });
|
|
||||||
await expect(page.locator('.el-message--success')).toBeVisible({ timeout: 5000 });
|
|
||||||
});
|
|
||||||
|
|
||||||
await test.step('验证字典数据已创建', async () => {
|
|
||||||
await page.locator('input[placeholder="请输入字典名称"]').fill(dictName);
|
|
||||||
await page.locator('button:has-text("查询")').click();
|
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
|
|
||||||
const dictDataRow = page.locator(`tr:has-text("${dictName}")`);
|
|
||||||
await expect(dictDataRow).toBeVisible({ timeout: 10000 });
|
|
||||||
await expect(dictDataRow.locator('td').nth(2)).toHaveText(dictCode);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test('编辑字典数据', async ({ page }) => {
|
|
||||||
const updatedName = `更新字典_${timestamp}`;
|
const updatedName = `更新字典_${timestamp}`;
|
||||||
|
|
||||||
await test.step('导航到数据字典管理', async () => {
|
await test.step('导航到字典管理', async () => {
|
||||||
await page.goto('/dashboard');
|
await page.goto('/dashboard');
|
||||||
await page.waitForLoadState('networkidle');
|
await page.waitForLoadState('networkidle');
|
||||||
await page.locator('text=系统管理').click();
|
await page.locator('text=系统管理').click();
|
||||||
await page.waitForTimeout(500);
|
await page.waitForTimeout(500);
|
||||||
await page.locator('text=数据字典').click();
|
await page.locator('text=字典管理').waitFor({ state: 'visible', timeout: 5000 });
|
||||||
|
await page.locator('text=字典管理').click();
|
||||||
await page.waitForLoadState('networkidle');
|
await page.waitForLoadState('networkidle');
|
||||||
|
await expect(page).toHaveURL(/.*dict/, { timeout: 10000 });
|
||||||
});
|
});
|
||||||
|
|
||||||
await test.step('切换到字典数据标签页', async () => {
|
await test.step('搜索并编辑字典', async () => {
|
||||||
await page.locator('.el-tabs__item:has-text("字典数据")').click();
|
const dictRow = page.locator(`tr:has-text("${dictName}")`);
|
||||||
await page.waitForLoadState('networkidle');
|
await dictRow.locator('button:has-text("编辑")').click();
|
||||||
});
|
|
||||||
|
|
||||||
await test.step('搜索并编辑字典数据', async () => {
|
|
||||||
await page.locator('input[placeholder="请输入字典名称"]').fill(dictName);
|
|
||||||
await page.locator('button:has-text("查询")').click();
|
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
|
|
||||||
const dictDataRow = page.locator(`tr:has-text("${dictName}")`);
|
|
||||||
await dictDataRow.locator('button:has-text("编辑")').click();
|
|
||||||
await page.waitForSelector('.el-dialog', { state: 'visible', timeout: 5000 });
|
await page.waitForSelector('.el-dialog', { state: 'visible', timeout: 5000 });
|
||||||
});
|
});
|
||||||
|
|
||||||
await test.step('修改字典数据信息', async () => {
|
await test.step('修改字典信息', async () => {
|
||||||
const dialog = page.locator('.el-dialog');
|
const dialog = page.locator('.el-dialog');
|
||||||
await dialog.locator('input').nth(1).fill(updatedName);
|
await dialog.locator('input').first().fill(updatedName);
|
||||||
await dialog.locator('textarea').fill(`这是更新后的字典数据备注,时间戳:${timestamp}`);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
await test.step('提交更新', async () => {
|
await test.step('提交更新', async () => {
|
||||||
@@ -141,38 +76,26 @@ test.describe('数据字典管理完整工作流', () => {
|
|||||||
await expect(page.locator('.el-message--success')).toBeVisible({ timeout: 5000 });
|
await expect(page.locator('.el-message--success')).toBeVisible({ timeout: 5000 });
|
||||||
});
|
});
|
||||||
|
|
||||||
await test.step('验证字典数据已更新', async () => {
|
await test.step('验证字典已更新', async () => {
|
||||||
await page.locator('input[placeholder="请输入字典名称"]').fill(updatedName);
|
await page.waitForTimeout(2000);
|
||||||
await page.locator('button:has-text("查询")').click();
|
const dictRow = page.locator(`tr:has-text("${updatedName}")`);
|
||||||
await page.waitForLoadState('networkidle');
|
await expect(dictRow).toBeVisible({ timeout: 15000 });
|
||||||
|
|
||||||
const dictDataRow = page.locator(`tr:has-text("${updatedName}")`);
|
|
||||||
await expect(dictDataRow).toBeVisible({ timeout: 10000 });
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('删除字典数据', async ({ page }) => {
|
test('删除字典', async ({ page }) => {
|
||||||
await test.step('导航到数据字典管理', async () => {
|
await test.step('导航到字典管理', async () => {
|
||||||
await page.goto('/dashboard');
|
await page.goto('/dashboard');
|
||||||
await page.waitForLoadState('networkidle');
|
await page.waitForLoadState('networkidle');
|
||||||
await page.locator('text=系统管理').click();
|
await page.locator('text=系统管理').click();
|
||||||
await page.waitForTimeout(500);
|
await page.waitForTimeout(500);
|
||||||
await page.locator('text=数据字典').click();
|
await page.locator('text=字典管理').click();
|
||||||
await page.waitForLoadState('networkidle');
|
await page.waitForLoadState('networkidle');
|
||||||
});
|
});
|
||||||
|
|
||||||
await test.step('切换到字典数据标签页', async () => {
|
await test.step('搜索并删除字典', async () => {
|
||||||
await page.locator('.el-tabs__item:has-text("字典数据")').click();
|
const dictRow = page.locator(`tr:has-text("更新字典_${timestamp}")`);
|
||||||
await page.waitForLoadState('networkidle');
|
await dictRow.locator('button:has-text("删除")').click();
|
||||||
});
|
|
||||||
|
|
||||||
await test.step('搜索并删除字典数据', async () => {
|
|
||||||
await page.locator('input[placeholder="请输入字典名称"]').fill(`更新字典_${timestamp}`);
|
|
||||||
await page.locator('button:has-text("查询")').click();
|
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
|
|
||||||
const dictDataRow = page.locator(`tr:has-text("更新字典_${timestamp}")`);
|
|
||||||
await dictDataRow.locator('button:has-text("删除")').click();
|
|
||||||
await page.waitForSelector('.el-message-box', { state: 'visible', timeout: 5000 });
|
await page.waitForSelector('.el-message-box', { state: 'visible', timeout: 5000 });
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -182,72 +105,20 @@ test.describe('数据字典管理完整工作流', () => {
|
|||||||
await expect(page.locator('.el-message--success')).toBeVisible({ timeout: 5000 });
|
await expect(page.locator('.el-message--success')).toBeVisible({ timeout: 5000 });
|
||||||
});
|
});
|
||||||
|
|
||||||
await test.step('验证字典数据已删除', async () => {
|
await test.step('验证字典已删除', async () => {
|
||||||
await page.locator('input[placeholder="请输入字典名称"]').fill(`更新字典_${timestamp}`);
|
await page.waitForTimeout(1000);
|
||||||
await page.locator('button:has-text("查询")').click();
|
const dictRow = page.locator(`tr:has-text("更新字典_${timestamp}")`);
|
||||||
await page.waitForLoadState('networkidle');
|
await expect(dictRow).not.toBeVisible({ timeout: 5000 });
|
||||||
|
|
||||||
const emptyText = page.locator('text=暂无数据');
|
|
||||||
await expect(emptyText).toBeVisible({ timeout: 10000 });
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('字典管理功能验证', async ({ page }) => {
|
test('字典管理功能验证', async ({ page }) => {
|
||||||
await test.step('验证字典管理页面访问权限', async () => {
|
await test.step('验证字典管理页面访问权限', async () => {
|
||||||
await page.goto('/dicts');
|
await page.goto('/dict');
|
||||||
await page.waitForLoadState('networkidle');
|
await page.waitForLoadState('networkidle');
|
||||||
|
|
||||||
// 验证页面标题
|
await expect(page.locator('.el-card')).toBeVisible({ timeout: 5000 });
|
||||||
await expect(page.locator('h1:has-text("数据字典管理")')).toBeVisible({ timeout: 5000 });
|
await expect(page.locator('button:has-text("新增字典")')).toBeVisible();
|
||||||
|
|
||||||
// 验证标签页
|
|
||||||
await expect(page.locator('.el-tabs__item:has-text("字典类型")')).toBeVisible();
|
|
||||||
await expect(page.locator('.el-tabs__item:has-text("字典数据")')).toBeVisible();
|
|
||||||
|
|
||||||
// 验证功能按钮
|
|
||||||
await expect(page.locator('button:has-text("新增字典类型")')).toBeVisible();
|
|
||||||
await expect(page.locator('button:has-text("新增字典数据")')).toBeVisible();
|
|
||||||
await expect(page.locator('button:has-text("查询")')).toBeVisible();
|
|
||||||
});
|
|
||||||
|
|
||||||
await test.step('验证字典类型搜索功能', async () => {
|
|
||||||
await page.locator('.el-tabs__item:has-text("字典类型")').click();
|
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
|
|
||||||
const searchInput = page.locator('input[placeholder="请输入字典类型"]');
|
|
||||||
await expect(searchInput).toBeVisible();
|
|
||||||
|
|
||||||
const searchButton = page.locator('button:has-text("查询")');
|
|
||||||
await expect(searchButton).toBeVisible();
|
|
||||||
|
|
||||||
// 测试搜索功能
|
|
||||||
await searchInput.fill('test');
|
|
||||||
await searchButton.click();
|
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
|
|
||||||
// 验证搜索结果
|
|
||||||
const table = page.locator('.el-table');
|
|
||||||
await expect(table).toBeVisible();
|
|
||||||
});
|
|
||||||
|
|
||||||
await test.step('验证字典数据搜索功能', async () => {
|
|
||||||
await page.locator('.el-tabs__item:has-text("字典数据")').click();
|
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
|
|
||||||
const searchInput = page.locator('input[placeholder="请输入字典名称"]');
|
|
||||||
await expect(searchInput).toBeVisible();
|
|
||||||
|
|
||||||
const searchButton = page.locator('button:has-text("查询")');
|
|
||||||
await expect(searchButton).toBeVisible();
|
|
||||||
|
|
||||||
// 测试搜索功能
|
|
||||||
await searchInput.fill('test');
|
|
||||||
await searchButton.click();
|
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
|
|
||||||
// 验证搜索结果
|
|
||||||
const table = page.locator('.el-table');
|
|
||||||
await expect(table).toBeVisible();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -35,7 +35,8 @@ test.describe('异常日志工作流', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await test.step('验证搜索结果', async () => {
|
await test.step('验证搜索结果', async () => {
|
||||||
await page.waitForLoadState('networkidle');
|
await page.waitForLoadState('domcontentloaded');
|
||||||
|
await page.waitForTimeout(1000);
|
||||||
const rowCount = await exceptionLogPage.getLogCount();
|
const rowCount = await exceptionLogPage.getLogCount();
|
||||||
console.log(`搜索结果包含 ${rowCount} 条记录`);
|
console.log(`搜索结果包含 ${rowCount} 条记录`);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,13 +4,16 @@ test.describe('文件管理工作流', () => {
|
|||||||
test('文件上传流程', async ({ page }) => {
|
test('文件上传流程', async ({ page }) => {
|
||||||
await test.step('导航到文件管理', async () => {
|
await test.step('导航到文件管理', async () => {
|
||||||
await page.goto('/dashboard');
|
await page.goto('/dashboard');
|
||||||
await page.waitForLoadState('networkidle');
|
await page.waitForLoadState('domcontentloaded');
|
||||||
|
await page.waitForTimeout(1000);
|
||||||
|
|
||||||
|
await page.locator('text=系统管理').waitFor({ state: 'visible', timeout: 5000 });
|
||||||
await page.locator('text=系统管理').click();
|
await page.locator('text=系统管理').click();
|
||||||
await page.waitForTimeout(500);
|
await page.waitForTimeout(500);
|
||||||
|
|
||||||
|
await page.locator('.el-menu-item:has-text("文件管理")').waitFor({ state: 'visible', timeout: 5000 });
|
||||||
await page.locator('.el-menu-item:has-text("文件管理")').click();
|
await page.locator('.el-menu-item:has-text("文件管理")').click();
|
||||||
await page.waitForLoadState('networkidle');
|
await page.waitForLoadState('domcontentloaded');
|
||||||
await page.waitForTimeout(1000);
|
await page.waitForTimeout(1000);
|
||||||
|
|
||||||
await expect(page.locator('.el-table')).toBeVisible({ timeout: 10000 });
|
await expect(page.locator('.el-table')).toBeVisible({ timeout: 10000 });
|
||||||
@@ -48,7 +51,6 @@ test.describe('文件管理工作流', () => {
|
|||||||
const searchInput = page.locator('input[placeholder*="搜索"]');
|
const searchInput = page.locator('input[placeholder*="搜索"]');
|
||||||
if (await searchInput.isVisible()) {
|
if (await searchInput.isVisible()) {
|
||||||
await searchInput.fill('test');
|
await searchInput.fill('test');
|
||||||
await page.locator('button:has-text("搜索")').click();
|
|
||||||
await page.waitForTimeout(1000);
|
await page.waitForTimeout(1000);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -78,7 +80,7 @@ test.describe('文件管理工作流', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await test.step('删除文件', async () => {
|
await test.step('删除文件', async () => {
|
||||||
const deleteButton = page.locator('button:has-text("删除")');
|
const deleteButton = page.locator('button:has-text("删除")').first();
|
||||||
if (await deleteButton.isVisible()) {
|
if (await deleteButton.isVisible()) {
|
||||||
await deleteButton.click();
|
await deleteButton.click();
|
||||||
await page.locator('button:has-text("确定")').click();
|
await page.locator('button:has-text("确定")').click();
|
||||||
|
|||||||
@@ -1,22 +1,25 @@
|
|||||||
import { test, expect } from '@playwright/test';
|
import { test, expect } from '@playwright/test';
|
||||||
|
|
||||||
test.describe('系统配置管理完整工作流', () => {
|
test.describe('参数管理完整工作流', () => {
|
||||||
test.describe.configure({ mode: 'serial' });
|
test.describe.configure({ mode: 'serial' });
|
||||||
|
|
||||||
const timestamp = Date.now();
|
const timestamp = Date.now();
|
||||||
const configKey = `test_config_${timestamp}`;
|
|
||||||
const configName = `测试配置_${timestamp}`;
|
const configName = `测试配置_${timestamp}`;
|
||||||
|
const configKey = `test_config_${timestamp}`;
|
||||||
const configValue = `test_value_${timestamp}`;
|
const configValue = `test_value_${timestamp}`;
|
||||||
|
|
||||||
test('创建系统配置', async ({ page }) => {
|
test('创建参数配置', async ({ page }) => {
|
||||||
await test.step('导航到系统配置管理', async () => {
|
await test.step('导航到参数管理', async () => {
|
||||||
await page.goto('/dashboard');
|
await page.goto('/dashboard');
|
||||||
await page.waitForLoadState('networkidle');
|
await page.waitForLoadState('domcontentloaded');
|
||||||
|
await page.waitForTimeout(1000);
|
||||||
|
await page.locator('text=系统管理').waitFor({ state: 'visible', timeout: 5000 });
|
||||||
await page.locator('text=系统管理').click();
|
await page.locator('text=系统管理').click();
|
||||||
await page.waitForTimeout(500);
|
await page.waitForTimeout(500);
|
||||||
await page.locator('text=系统配置').click();
|
await page.locator('text=参数管理').waitFor({ state: 'visible', timeout: 5000 });
|
||||||
await page.waitForLoadState('networkidle');
|
await page.locator('text=参数管理').click();
|
||||||
await expect(page).toHaveURL(/.*configs/, { timeout: 10000 });
|
await page.waitForLoadState('domcontentloaded');
|
||||||
|
await expect(page).toHaveURL(/.*config/, { timeout: 15000 });
|
||||||
});
|
});
|
||||||
|
|
||||||
await test.step('点击新增配置按钮', async () => {
|
await test.step('点击新增配置按钮', async () => {
|
||||||
@@ -29,7 +32,6 @@ test.describe('系统配置管理完整工作流', () => {
|
|||||||
await dialog.locator('input').first().fill(configName);
|
await dialog.locator('input').first().fill(configName);
|
||||||
await dialog.locator('input').nth(1).fill(configKey);
|
await dialog.locator('input').nth(1).fill(configKey);
|
||||||
await dialog.locator('input').nth(2).fill(configValue);
|
await dialog.locator('input').nth(2).fill(configValue);
|
||||||
await dialog.locator('textarea').fill(`这是测试配置的备注信息,用于验证配置管理功能。时间戳:${timestamp}`);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
await test.step('提交配置表单', async () => {
|
await test.step('提交配置表单', async () => {
|
||||||
@@ -39,34 +41,25 @@ test.describe('系统配置管理完整工作流', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await test.step('验证配置已创建', async () => {
|
await test.step('验证配置已创建', async () => {
|
||||||
await page.locator('input[placeholder="请输入配置名称"]').fill(configName);
|
await page.waitForTimeout(2000);
|
||||||
await page.locator('button:has-text("查询")').click();
|
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
|
|
||||||
const configRow = page.locator(`tr:has-text("${configName}")`);
|
const configRow = page.locator(`tr:has-text("${configName}")`);
|
||||||
await expect(configRow).toBeVisible({ timeout: 10000 });
|
await expect(configRow).toBeVisible({ timeout: 15000 });
|
||||||
await expect(configRow.locator('td').nth(1)).toHaveText(configKey);
|
|
||||||
await expect(configRow.locator('td').nth(2)).toHaveText(configValue);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('编辑系统配置', async ({ page }) => {
|
test('编辑参数配置', async ({ page }) => {
|
||||||
const updatedValue = `updated_value_${timestamp}`;
|
const updatedValue = `updated_value_${timestamp}`;
|
||||||
|
|
||||||
await test.step('导航到系统配置管理', async () => {
|
await test.step('导航到参数管理', async () => {
|
||||||
await page.goto('/dashboard');
|
await page.goto('/dashboard');
|
||||||
await page.waitForLoadState('networkidle');
|
await page.waitForLoadState('networkidle');
|
||||||
await page.locator('text=系统管理').click();
|
await page.locator('text=系统管理').click();
|
||||||
await page.waitForTimeout(500);
|
await page.waitForTimeout(500);
|
||||||
await page.locator('text=系统配置').click();
|
await page.locator('text=参数管理').click();
|
||||||
await page.waitForLoadState('networkidle');
|
await page.waitForLoadState('networkidle');
|
||||||
});
|
});
|
||||||
|
|
||||||
await test.step('搜索并编辑配置', async () => {
|
await test.step('搜索并编辑配置', async () => {
|
||||||
await page.locator('input[placeholder="请输入配置名称"]').fill(configName);
|
|
||||||
await page.locator('button:has-text("查询")').click();
|
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
|
|
||||||
const configRow = page.locator(`tr:has-text("${configName}")`);
|
const configRow = page.locator(`tr:has-text("${configName}")`);
|
||||||
await configRow.locator('button:has-text("编辑")').click();
|
await configRow.locator('button:has-text("编辑")').click();
|
||||||
await page.waitForSelector('.el-dialog', { state: 'visible', timeout: 5000 });
|
await page.waitForSelector('.el-dialog', { state: 'visible', timeout: 5000 });
|
||||||
@@ -75,7 +68,6 @@ test.describe('系统配置管理完整工作流', () => {
|
|||||||
await test.step('修改配置值', async () => {
|
await test.step('修改配置值', async () => {
|
||||||
const dialog = page.locator('.el-dialog');
|
const dialog = page.locator('.el-dialog');
|
||||||
await dialog.locator('input').nth(2).fill(updatedValue);
|
await dialog.locator('input').nth(2).fill(updatedValue);
|
||||||
await dialog.locator('textarea').fill(`这是更新后的配置备注,时间戳:${timestamp}`);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
await test.step('提交更新', async () => {
|
await test.step('提交更新', async () => {
|
||||||
@@ -85,30 +77,23 @@ test.describe('系统配置管理完整工作流', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await test.step('验证配置已更新', async () => {
|
await test.step('验证配置已更新', async () => {
|
||||||
await page.locator('input[placeholder="请输入配置名称"]').fill(configName);
|
await page.waitForTimeout(2000);
|
||||||
await page.locator('button:has-text("查询")').click();
|
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
|
|
||||||
const configRow = page.locator(`tr:has-text("${configName}")`);
|
const configRow = page.locator(`tr:has-text("${configName}")`);
|
||||||
await expect(configRow.locator('td').nth(2)).toHaveText(updatedValue);
|
await expect(configRow).toBeVisible({ timeout: 15000 });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('删除系统配置', async ({ page }) => {
|
test('删除参数配置', async ({ page }) => {
|
||||||
await test.step('导航到系统配置管理', async () => {
|
await test.step('导航到参数管理', async () => {
|
||||||
await page.goto('/dashboard');
|
await page.goto('/dashboard');
|
||||||
await page.waitForLoadState('networkidle');
|
await page.waitForLoadState('networkidle');
|
||||||
await page.locator('text=系统管理').click();
|
await page.locator('text=系统管理').click();
|
||||||
await page.waitForTimeout(500);
|
await page.waitForTimeout(500);
|
||||||
await page.locator('text=系统配置').click();
|
await page.locator('text=参数管理').click();
|
||||||
await page.waitForLoadState('networkidle');
|
await page.waitForLoadState('networkidle');
|
||||||
});
|
});
|
||||||
|
|
||||||
await test.step('搜索并删除配置', async () => {
|
await test.step('搜索并删除配置', async () => {
|
||||||
await page.locator('input[placeholder="请输入配置名称"]').fill(configName);
|
|
||||||
await page.locator('button:has-text("查询")').click();
|
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
|
|
||||||
const configRow = page.locator(`tr:has-text("${configName}")`);
|
const configRow = page.locator(`tr:has-text("${configName}")`);
|
||||||
await configRow.locator('button:has-text("删除")').click();
|
await configRow.locator('button:has-text("删除")').click();
|
||||||
await page.waitForSelector('.el-message-box', { state: 'visible', timeout: 5000 });
|
await page.waitForSelector('.el-message-box', { state: 'visible', timeout: 5000 });
|
||||||
@@ -121,49 +106,19 @@ test.describe('系统配置管理完整工作流', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await test.step('验证配置已删除', async () => {
|
await test.step('验证配置已删除', async () => {
|
||||||
await page.locator('input[placeholder="请输入配置名称"]').fill(configName);
|
await page.waitForTimeout(1000);
|
||||||
await page.locator('button:has-text("查询")').click();
|
const configRow = page.locator(`tr:has-text("${configName}")`);
|
||||||
await page.waitForLoadState('networkidle');
|
await expect(configRow).not.toBeVisible({ timeout: 5000 });
|
||||||
|
|
||||||
const emptyText = page.locator('text=暂无数据');
|
|
||||||
await expect(emptyText).toBeVisible({ timeout: 10000 });
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('配置管理权限验证', async ({ page }) => {
|
test('参数管理权限验证', async ({ page }) => {
|
||||||
await test.step('验证配置管理页面访问权限', async () => {
|
await test.step('验证参数管理页面访问权限', async () => {
|
||||||
await page.goto('/configs');
|
await page.goto('/sys/config');
|
||||||
await page.waitForLoadState('networkidle');
|
await page.waitForLoadState('networkidle');
|
||||||
|
|
||||||
// 验证页面标题
|
await expect(page.locator('.el-card')).toBeVisible({ timeout: 5000 });
|
||||||
await expect(page.locator('h1:has-text("系统配置管理")')).toBeVisible({ timeout: 5000 });
|
|
||||||
|
|
||||||
// 验证功能按钮可见性
|
|
||||||
await expect(page.locator('button:has-text("新增配置")')).toBeVisible();
|
await expect(page.locator('button:has-text("新增配置")')).toBeVisible();
|
||||||
await expect(page.locator('button:has-text("查询")')).toBeVisible();
|
|
||||||
|
|
||||||
// 验证表格列头
|
|
||||||
await expect(page.locator('th:has-text("配置名称")')).toBeVisible();
|
|
||||||
await expect(page.locator('th:has-text("配置键")')).toBeVisible();
|
|
||||||
await expect(page.locator('th:has-text("配置值")')).toBeVisible();
|
|
||||||
await expect(page.locator('th:has-text("操作")')).toBeVisible();
|
|
||||||
});
|
|
||||||
|
|
||||||
await test.step('验证配置搜索功能', async () => {
|
|
||||||
const searchInput = page.locator('input[placeholder="请输入配置名称"]');
|
|
||||||
await expect(searchInput).toBeVisible();
|
|
||||||
|
|
||||||
const searchButton = page.locator('button:has-text("查询")');
|
|
||||||
await expect(searchButton).toBeVisible();
|
|
||||||
|
|
||||||
// 测试搜索功能
|
|
||||||
await searchInput.fill('test');
|
|
||||||
await searchButton.click();
|
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
|
|
||||||
// 验证搜索结果
|
|
||||||
const table = page.locator('.el-table');
|
|
||||||
await expect(table).toBeVisible();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -27,7 +27,8 @@ test.describe('用户权限边界验证', () => {
|
|||||||
test('普通用户登录后可以访问页面但API操作受限', async ({ page }) => {
|
test('普通用户登录后可以访问页面但API操作受限', async ({ page }) => {
|
||||||
await test.step('管理员登出', async () => {
|
await test.step('管理员登出', async () => {
|
||||||
await page.goto('/dashboard');
|
await page.goto('/dashboard');
|
||||||
await page.waitForLoadState('networkidle');
|
await page.waitForLoadState('domcontentloaded');
|
||||||
|
await page.waitForTimeout(1000);
|
||||||
|
|
||||||
const avatarButton = page.locator('.el-avatar').first();
|
const avatarButton = page.locator('.el-avatar').first();
|
||||||
await avatarButton.click({ timeout: 10000 });
|
await avatarButton.click({ timeout: 10000 });
|
||||||
@@ -39,7 +40,8 @@ test.describe('用户权限边界验证', () => {
|
|||||||
|
|
||||||
await test.step('普通用户登录', async () => {
|
await test.step('普通用户登录', async () => {
|
||||||
await page.goto('/login');
|
await page.goto('/login');
|
||||||
await page.waitForLoadState('networkidle');
|
await page.waitForLoadState('domcontentloaded');
|
||||||
|
await page.waitForTimeout(1000);
|
||||||
|
|
||||||
const usernameInput = page.locator('input[placeholder*="用户名"]');
|
const usernameInput = page.locator('input[placeholder*="用户名"]');
|
||||||
const passwordInput = page.locator('input[placeholder*="密码"]');
|
const passwordInput = page.locator('input[placeholder*="密码"]');
|
||||||
@@ -78,7 +80,8 @@ test.describe('用户权限边界验证', () => {
|
|||||||
test('权限不足时API返回403错误', async ({ page }) => {
|
test('权限不足时API返回403错误', async ({ page }) => {
|
||||||
await test.step('管理员登出', async () => {
|
await test.step('管理员登出', async () => {
|
||||||
await page.goto('/dashboard');
|
await page.goto('/dashboard');
|
||||||
await page.waitForLoadState('networkidle');
|
await page.waitForLoadState('domcontentloaded');
|
||||||
|
await page.waitForTimeout(1000);
|
||||||
|
|
||||||
const avatarButton = page.locator('.el-avatar').first();
|
const avatarButton = page.locator('.el-avatar').first();
|
||||||
await avatarButton.click({ timeout: 10000 });
|
await avatarButton.click({ timeout: 10000 });
|
||||||
|
|||||||
@@ -28,9 +28,10 @@ export class DictionaryManagementPage {
|
|||||||
console.log('导航到字典管理页面...');
|
console.log('导航到字典管理页面...');
|
||||||
await this.page.goto('/dict');
|
await this.page.goto('/dict');
|
||||||
|
|
||||||
await this.page.waitForLoadState('networkidle');
|
await this.page.waitForLoadState('domcontentloaded');
|
||||||
await this.table.waitFor({ state: 'visible', timeout: 10000 });
|
await this.page.waitForTimeout(1000);
|
||||||
await expect(this.page).toHaveURL(/.*dict/);
|
await this.table.waitFor({ state: 'visible', timeout: 15000 });
|
||||||
|
await expect(this.page).toHaveURL(/.*dict/, { timeout: 15000 });
|
||||||
|
|
||||||
console.log('字典管理页面加载完成');
|
console.log('字典管理页面加载完成');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -26,13 +26,16 @@ export class ExceptionLogPage {
|
|||||||
console.log('导航到异常日志页面...');
|
console.log('导航到异常日志页面...');
|
||||||
await this.page.goto('/exceptionlog');
|
await this.page.goto('/exceptionlog');
|
||||||
|
|
||||||
await this.page.waitForLoadState('networkidle');
|
await this.page.waitForLoadState('domcontentloaded');
|
||||||
await this.table.waitFor({ state: 'visible', timeout: 10000 });
|
await this.page.waitForTimeout(1000);
|
||||||
await expect(this.page).toHaveURL(/.*exceptionlog/);
|
await this.table.waitFor({ state: 'visible', timeout: 15000 });
|
||||||
|
await expect(this.page).toHaveURL(/.*exceptionlog/, { timeout: 15000 });
|
||||||
|
|
||||||
console.log('异常日志页面加载完成');
|
console.log('异常日志页面加载完成');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
await this.page.screenshot({ path: `test-results/exception-log-error-${Date.now()}.png` });
|
if (!this.page.isClosed()) {
|
||||||
|
await this.page.screenshot({ path: `test-results/exception-log-error-${Date.now()}.png` });
|
||||||
|
}
|
||||||
console.error('导航到异常日志页面失败:', error);
|
console.error('导航到异常日志页面失败:', error);
|
||||||
throw new Error(`导航到异常日志页面失败: ${error instanceof Error ? error.message : String(error)}`);
|
throw new Error(`导航到异常日志页面失败: ${error instanceof Error ? error.message : String(error)}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,13 +30,16 @@ export class NotificationPage {
|
|||||||
console.log('导航到通知管理页面...');
|
console.log('导航到通知管理页面...');
|
||||||
await this.page.goto('/notice');
|
await this.page.goto('/notice');
|
||||||
|
|
||||||
await this.page.waitForLoadState('networkidle');
|
await this.page.waitForLoadState('domcontentloaded');
|
||||||
await this.table.waitFor({ state: 'visible', timeout: 10000 });
|
await this.page.waitForTimeout(1000);
|
||||||
await expect(this.page).toHaveURL(/.*notice/);
|
await this.table.waitFor({ state: 'visible', timeout: 15000 });
|
||||||
|
await expect(this.page).toHaveURL(/.*notice/, { timeout: 15000 });
|
||||||
|
|
||||||
console.log('通知管理页面加载完成');
|
console.log('通知管理页面加载完成');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
await this.page.screenshot({ path: `test-results/notification-error-${Date.now()}.png` });
|
if (!this.page.isClosed()) {
|
||||||
|
await this.page.screenshot({ path: `test-results/notification-error-${Date.now()}.png` });
|
||||||
|
}
|
||||||
console.error('导航到通知管理页面失败:', error);
|
console.error('导航到通知管理页面失败:', error);
|
||||||
throw new Error(`导航到通知管理页面失败: ${error instanceof Error ? error.message : String(error)}`);
|
throw new Error(`导航到通知管理页面失败: ${error instanceof Error ? error.message : String(error)}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,9 +28,10 @@ export class SystemConfigPage {
|
|||||||
console.log('导航到系统配置页面...');
|
console.log('导航到系统配置页面...');
|
||||||
await this.page.goto('/sys/config');
|
await this.page.goto('/sys/config');
|
||||||
|
|
||||||
await this.page.waitForLoadState('networkidle');
|
await this.page.waitForLoadState('domcontentloaded');
|
||||||
await this.table.waitFor({ state: 'visible', timeout: 10000 });
|
await this.page.waitForTimeout(1000);
|
||||||
await expect(this.page).toHaveURL(/.*config/);
|
await this.table.waitFor({ state: 'visible', timeout: 15000 });
|
||||||
|
await expect(this.page).toHaveURL(/.*config/, { timeout: 15000 });
|
||||||
|
|
||||||
console.log('系统配置页面加载完成');
|
console.log('系统配置页面加载完成');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { test, expect } from '@playwright/test';
|
import { test, expect } from '@playwright/test';
|
||||||
|
|
||||||
test.describe('冒烟测试 - 基础流程', () => {
|
test.describe('冒烟测试 - 基础流程', () => {
|
||||||
|
test.use({ storageState: { cookies: [], origins: [] } });
|
||||||
|
|
||||||
test('管理员登录和登出', async ({ page }) => {
|
test('管理员登录和登出', async ({ page }) => {
|
||||||
await test.step('导航到登录页面', async () => {
|
await test.step('导航到登录页面', async () => {
|
||||||
await page.goto('/login');
|
await page.goto('/login');
|
||||||
|
|||||||
+15
-6
@@ -16,7 +16,7 @@ export default defineConfig({
|
|||||||
['list']
|
['list']
|
||||||
],
|
],
|
||||||
use: {
|
use: {
|
||||||
baseURL: 'http://localhost:3003',
|
baseURL: 'http://localhost:3002',
|
||||||
trace: 'on-first-retry',
|
trace: 'on-first-retry',
|
||||||
screenshot: 'only-on-failure',
|
screenshot: 'only-on-failure',
|
||||||
video: 'retain-on-failure',
|
video: 'retain-on-failure',
|
||||||
@@ -25,25 +25,34 @@ export default defineConfig({
|
|||||||
ignoreHTTPSErrors: true,
|
ignoreHTTPSErrors: true,
|
||||||
},
|
},
|
||||||
projects: [
|
projects: [
|
||||||
|
{
|
||||||
|
name: 'setup',
|
||||||
|
testMatch: /.*\.setup\.ts/,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'chromium',
|
name: 'chromium',
|
||||||
use: { ...devices['Desktop Chrome'] },
|
use: { ...devices['Desktop Chrome'], storageState: 'playwright/.auth/user.json' },
|
||||||
|
dependencies: ['setup'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'firefox',
|
name: 'firefox',
|
||||||
use: { ...devices['Desktop Firefox'] },
|
use: { ...devices['Desktop Firefox'], storageState: 'playwright/.auth/user.json' },
|
||||||
|
dependencies: ['setup'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'webkit',
|
name: 'webkit',
|
||||||
use: { ...devices['Desktop Safari'] },
|
use: { ...devices['Desktop Safari'], storageState: 'playwright/.auth/user.json' },
|
||||||
|
dependencies: ['setup'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Mobile Chrome',
|
name: 'Mobile Chrome',
|
||||||
use: { ...devices['Pixel 5'] },
|
use: { ...devices['Pixel 5'], storageState: 'playwright/.auth/user.json' },
|
||||||
|
dependencies: ['setup'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Mobile Safari',
|
name: 'Mobile Safari',
|
||||||
use: { ...devices['iPhone 12'] },
|
use: { ...devices['iPhone 12'], storageState: 'playwright/.auth/user.json' },
|
||||||
|
dependencies: ['setup'],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"cookies": [],
|
||||||
|
"origins": [
|
||||||
|
{
|
||||||
|
"origin": "http://localhost:3002",
|
||||||
|
"localStorage": [
|
||||||
|
{
|
||||||
|
"name": "token",
|
||||||
|
"value": "eyJhbGciOiJIUzM4NCJ9.eyJyb2xlcyI6WyJhZG1pbiJdLCJ1c2VySWQiOjEsInVzZXJuYW1lIjoiYWRtaW4iLCJzdWIiOiJhZG1pbiIsImlhdCI6MTc3NjQ4NzA2OSwiZXhwIjoxNzc2NTczNDY5fQ.ZOxLUv_Iu1LUiHYEAPkeZsGpzVw813_kIdz1oQHCxLt_Yi-o7lx90sPU55XmFqPp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "permission",
|
||||||
|
"value": "{\"roles\":[\"admin\"],\"permissions\":[\"system:user:list\",\"system:role:list\",\"system:menu:list\",\"system:dept:list\",\"system:dict:list\",\"system:config:list\",\"system:notice:list\",\"system:file:list\",\"system:user:query\",\"system:user:add\",\"system:user:edit\",\"system:user:remove\",\"system:user:export\",\"system:user:import\",\"system:user:resetPwd\",\"system:role:query\",\"system:role:add\",\"system:role:edit\",\"system:role:remove\",\"system:role:export\",\"system:menu:query\",\"system:menu:add\",\"system:menu:edit\",\"system:menu:remove\",\"audit:operation:list\",\"audit:login:list\",\"audit:exception:list\",\"audit:operation:query\",\"audit:operation:remove\",\"audit:operation:export\",\"audit:login:query\",\"audit:login:remove\",\"audit:login:export\",\"audit:exception:query\",\"audit:exception:remove\",\"audit:exception:export\",\"monitor:online:list\",\"monitor:job:list\",\"monitor:data:list\",\"monitor:server:list\",\"monitor:cache:list\",\"monitor:online:query\",\"monitor:online:forceLogout\",\"monitor:job:query\",\"monitor:job:add\",\"monitor:job:edit\",\"monitor:job:remove\",\"monitor:job:execute\"],\"menus\":[{\"id\":\"1\",\"name\":\"系统管理\",\"path\":\"\",\"icon\":\"Setting\",\"sort\":1,\"children\":[{\"id\":\"11\",\"name\":\"用户管理\",\"path\":\"/users\",\"icon\":\"User\",\"parentId\":\"1\",\"sort\":1},{\"id\":\"12\",\"name\":\"角色管理\",\"path\":\"/roles\",\"icon\":\"UserFilled\",\"parentId\":\"1\",\"sort\":2},{\"id\":\"13\",\"name\":\"菜单管理\",\"path\":\"/menus\",\"icon\":\"Menu\",\"parentId\":\"1\",\"sort\":3},{\"id\":\"14\",\"name\":\"部门管理\",\"path\":\"/dept\",\"icon\":\"Document\",\"parentId\":\"1\",\"sort\":4},{\"id\":\"15\",\"name\":\"字典管理\",\"path\":\"/dict\",\"icon\":\"Collection\",\"parentId\":\"1\",\"sort\":5},{\"id\":\"16\",\"name\":\"参数管理\",\"path\":\"/sys/config\",\"icon\":\"Document\",\"parentId\":\"1\",\"sort\":6},{\"id\":\"17\",\"name\":\"通知公告\",\"path\":\"/notice\",\"icon\":\"Bell\",\"parentId\":\"1\",\"sort\":7},{\"id\":\"18\",\"name\":\"文件管理\",\"path\":\"/files\",\"icon\":\"Folder\",\"parentId\":\"1\",\"sort\":8}]},{\"id\":\"2\",\"name\":\"审计日志\",\"path\":\"\",\"icon\":\"Document\",\"sort\":2,\"children\":[{\"id\":\"21\",\"name\":\"操作日志\",\"path\":\"/oplog\",\"icon\":\"Document\",\"parentId\":\"2\",\"sort\":1},{\"id\":\"22\",\"name\":\"登录日志\",\"path\":\"/loginlog\",\"icon\":\"Document\",\"parentId\":\"2\",\"sort\":2},{\"id\":\"23\",\"name\":\"异常日志\",\"path\":\"/exceptionlog\",\"icon\":\"Warning\",\"parentId\":\"2\",\"sort\":3}]},{\"id\":\"3\",\"name\":\"系统监控\",\"path\":\"\",\"icon\":\"Monitor\",\"sort\":3,\"children\":[{\"id\":\"31\",\"name\":\"在线用户\",\"path\":\"/monitor/online\",\"icon\":\"Document\",\"parentId\":\"3\",\"sort\":1},{\"id\":\"32\",\"name\":\"定时任务\",\"path\":\"/monitor/job\",\"icon\":\"Document\",\"parentId\":\"3\",\"sort\":2},{\"id\":\"33\",\"name\":\"数据监控\",\"path\":\"/monitor/data\",\"icon\":\"Document\",\"parentId\":\"3\",\"sort\":3},{\"id\":\"34\",\"name\":\"服务监控\",\"path\":\"/monitor/server\",\"icon\":\"Document\",\"parentId\":\"3\",\"sort\":4},{\"id\":\"35\",\"name\":\"缓存监控\",\"path\":\"/monitor/cache\",\"icon\":\"Document\",\"parentId\":\"3\",\"sort\":5}]}]}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "userId",
|
||||||
|
"value": "1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "username",
|
||||||
|
"value": "admin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "roles",
|
||||||
|
"value": "[\"admin\"]"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user