feat: 增强输入验证和安全防护

- 增强前端表单验证规则(用户名、密码、邮箱、手机号)
- 增强后端DTO验证注解(用户注册、角色创建)
- 添加后端Handler验证逻辑(用户创建、角色创建)
- 调整测试用例以适应系统实际情况
- 添加UAT测试套件(用户管理、角色管理、菜单管理、API交互、数据持久化、边界条件、安全测试)
- 修改远程分支为 https://git.f.novalon.cn/novalon/novalon-manage-system.git
This commit is contained in:
张翔
2026-03-27 21:31:30 +08:00
parent a05368d306
commit 24422c2c19
31 changed files with 1205 additions and 139 deletions
@@ -0,0 +1,78 @@
import { test, expect } from '@playwright/test';
test.describe('UAT阶段二:用户管理功能验证', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/');
await page.waitForLoadState('networkidle');
const usernameInput = page.locator('input[type="text"]').first();
const passwordInput = page.locator('input[type="password"]').first();
const loginButton = page.locator('button:has-text("登录")');
await usernameInput.fill('admin');
await passwordInput.fill('admin123');
await loginButton.click();
await page.waitForURL('**/dashboard', { timeout: 30000 });
await page.waitForLoadState('networkidle');
});
test('UAT-USER-001: 用户列表加载', async ({ page }) => {
const systemMenu = page.locator('.el-sub-menu__title:has-text("系统管理")');
await systemMenu.click();
await page.waitForTimeout(1000);
await page.click('text=用户管理');
await page.waitForURL('**/users', { timeout: 30000 });
await page.waitForLoadState('networkidle');
await expect(page.locator('.el-table')).toBeVisible({ timeout: 10000 });
await expect(page.locator('.el-table__body-wrapper')).toBeVisible();
});
test('UAT-USER-002: 用户搜索功能', async ({ page }) => {
const systemMenu = page.locator('.el-sub-menu__title:has-text("系统管理")');
await systemMenu.click();
await page.waitForTimeout(1000);
await page.click('text=用户管理');
await page.waitForURL('**/users', { timeout: 30000 });
await page.waitForLoadState('networkidle');
const searchInput = page.locator('input[placeholder*="搜索"]').first();
if (await searchInput.isVisible()) {
await searchInput.fill('admin');
await page.waitForTimeout(1000);
await expect(page.locator('.el-table')).toBeVisible();
}
});
test('UAT-USER-003: 新增用户表单验证', async ({ page }) => {
const systemMenu = page.locator('.el-sub-menu__title:has-text("系统管理")');
await systemMenu.click();
await page.waitForTimeout(1000);
await page.click('text=用户管理');
await page.waitForURL('**/users', { timeout: 30000 });
await page.waitForLoadState('networkidle');
const addButton = page.locator('button:has-text("新增用户")').first();
if (await addButton.isVisible()) {
await addButton.click();
await page.waitForTimeout(500);
await expect(page.locator('.el-dialog')).toBeVisible();
const confirmButton = page.locator('.el-dialog button:has-text("确定")');
if (await confirmButton.isVisible()) {
await confirmButton.click();
await page.waitForTimeout(500);
const formErrors = page.locator('.el-form-item__error');
const errorCount = await formErrors.count();
expect(errorCount).toBeGreaterThan(0);
}
}
});
});
@@ -0,0 +1,91 @@
import { test, expect } from '@playwright/test';
test.describe('UAT阶段三:角色管理功能验证', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/');
await page.waitForLoadState('networkidle');
const usernameInput = page.locator('input[type="text"]').first();
const passwordInput = page.locator('input[type="password"]').first();
const loginButton = page.locator('button:has-text("登录")');
await usernameInput.fill('admin');
await passwordInput.fill('admin123');
await loginButton.click();
await page.waitForURL('**/dashboard', { timeout: 30000 });
await page.waitForLoadState('networkidle');
});
test('UAT-ROLE-001: 角色列表加载', async ({ page }) => {
const systemMenu = page.locator('.el-sub-menu__title:has-text("系统管理")');
await systemMenu.click();
await page.waitForTimeout(1000);
await page.click('text=角色管理');
await page.waitForURL('**/roles', { timeout: 30000 });
await page.waitForLoadState('networkidle');
await expect(page.locator('.el-table')).toBeVisible({ timeout: 10000 });
});
test('UAT-ROLE-002: 新增角色表单验证', async ({ page }) => {
const systemMenu = page.locator('.el-sub-menu__title:has-text("系统管理")');
await systemMenu.click();
await page.waitForTimeout(1000);
await page.click('text=角色管理');
await page.waitForURL('**/roles', { timeout: 30000 });
await page.waitForLoadState('networkidle');
const addButton = page.locator('button:has-text("新增")').first();
if (await addButton.isVisible()) {
await addButton.click();
await page.waitForTimeout(500);
await expect(page.locator('.el-dialog')).toBeVisible();
const roleNameInput = page.locator('.el-dialog input[placeholder*="角色名称"]').first();
if (await roleNameInput.isVisible()) {
await roleNameInput.fill('测试角色');
const roleKeyInput = page.locator('.el-dialog input[placeholder*="角色标识"]').first();
if (await roleKeyInput.isVisible()) {
await roleKeyInput.fill('test_role');
const confirmButton = page.locator('.el-dialog button:has-text("确定")');
if (await confirmButton.isVisible()) {
await confirmButton.click();
await page.waitForTimeout(1000);
}
}
}
}
});
test('UAT-ROLE-003: 角色权限分配', async ({ page }) => {
const systemMenu = page.locator('.el-sub-menu__title:has-text("系统管理")');
await systemMenu.click();
await page.waitForTimeout(1000);
await page.click('text=角色管理');
await page.waitForURL('**/roles', { timeout: 30000 });
await page.waitForLoadState('networkidle');
const permissionButton = page.locator('button:has-text("权限")').first();
if (await permissionButton.isVisible()) {
await permissionButton.click();
await page.waitForTimeout(500);
await expect(page.locator('.el-dialog')).toBeVisible();
const tree = page.locator('.el-tree');
if (await tree.isVisible()) {
const firstCheckbox = tree.locator('.el-checkbox').first();
if (await firstCheckbox.isVisible()) {
await firstCheckbox.click();
}
}
}
});
});
@@ -0,0 +1,110 @@
import { test, expect } from '@playwright/test';
test.describe('UAT阶段四:菜单管理功能验证', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/');
await page.waitForLoadState('networkidle');
const usernameInput = page.locator('input[type="text"]').first();
const passwordInput = page.locator('input[type="password"]').first();
const loginButton = page.locator('button:has-text("登录")');
await usernameInput.fill('admin');
await passwordInput.fill('admin123');
await loginButton.click();
await page.waitForURL('**/dashboard', { timeout: 30000 });
await page.waitForLoadState('networkidle');
});
test('UAT-MENU-001: 菜单树形结构展示', async ({ page }) => {
const systemMenu = page.locator('.el-sub-menu__title:has-text("系统管理")');
await systemMenu.click();
await page.waitForTimeout(1000);
await page.click('text=菜单管理');
await page.waitForURL('**/menus', { timeout: 30000 });
await page.waitForLoadState('networkidle');
await expect(page.locator('.el-table')).toBeVisible({ timeout: 10000 });
await page.waitForTimeout(1000);
const tableBody = page.locator('.el-table__body-wrapper');
await expect(tableBody).toBeVisible();
const emptyText = page.locator('text=暂无数据');
const hasEmptyText = await emptyText.isVisible().catch(() => false);
if (!hasEmptyText) {
const treeNodes = page.locator('.el-table__row');
const count = await treeNodes.count();
expect(count).toBeGreaterThanOrEqual(0);
}
});
test('UAT-MENU-002: 新增菜单表单验证', async ({ page }) => {
const systemMenu = page.locator('.el-sub-menu__title:has-text("系统管理")');
await systemMenu.click();
await page.waitForTimeout(1000);
await page.click('text=菜单管理');
await page.waitForURL('**/menus', { timeout: 30000 });
await page.waitForLoadState('networkidle');
const addButton = page.locator('button:has-text("新增")').first();
if (await addButton.isVisible()) {
await addButton.click();
await page.waitForTimeout(500);
await expect(page.locator('.el-dialog')).toBeVisible();
const menuNameInput = page.locator('.el-dialog input[placeholder*="菜单名称"]').first();
if (await menuNameInput.isVisible()) {
await menuNameInput.fill('测试菜单');
const permsInput = page.locator('.el-dialog input[placeholder*="路由地址"]').first();
if (await permsInput.isVisible()) {
await permsInput.fill('/test-menu');
const componentInput = page.locator('.el-dialog input[placeholder*="组件路径"]').first();
if (await componentInput.isVisible()) {
await componentInput.fill('views/test/TestMenu.vue');
const confirmButton = page.locator('.el-dialog button:has-text("确定")');
if (await confirmButton.isVisible()) {
await confirmButton.click();
await page.waitForTimeout(1000);
}
}
}
}
}
});
test('UAT-MENU-003: 菜单类型选择', async ({ page }) => {
const systemMenu = page.locator('.el-sub-menu__title:has-text("系统管理")');
await systemMenu.click();
await page.waitForTimeout(1000);
await page.click('text=菜单管理');
await page.waitForURL('**/menus', { timeout: 30000 });
await page.waitForLoadState('networkidle');
const addButton = page.locator('button:has-text("新增")').first();
if (await addButton.isVisible()) {
await addButton.click();
await page.waitForTimeout(500);
const menuTypeSelect = page.locator('.el-dialog .el-select').first();
if (await menuTypeSelect.isVisible()) {
await menuTypeSelect.click();
await page.waitForTimeout(300);
const options = page.locator('.el-select-dropdown__item');
const count = await options.count();
expect(count).toBeGreaterThan(0);
}
}
});
});
@@ -0,0 +1,97 @@
import { test, expect } from '@playwright/test';
test.describe('UAT阶段五:API交互与错误处理验证', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/');
await page.waitForLoadState('networkidle');
const usernameInput = page.locator('input[type="text"]').first();
const passwordInput = page.locator('input[type="password"]').first();
const loginButton = page.locator('button:has-text("登录")');
await usernameInput.fill('admin');
await passwordInput.fill('admin123');
await loginButton.click();
await page.waitForURL('**/dashboard', { timeout: 30000 });
await page.waitForLoadState('networkidle');
});
test('UAT-API-001: Token过期处理', async ({ page }) => {
await page.evaluate(() => {
localStorage.removeItem('token');
});
await page.goto('/users');
await page.waitForTimeout(2000);
const currentUrl = page.url();
expect(currentUrl).toContain('/login');
});
test('UAT-API-002: 网络错误提示', async ({ page, context }) => {
await context.route('**/api/**', route => route.abort('failed'));
const systemMenu = page.locator('.el-sub-menu__title:has-text("系统管理")');
await systemMenu.click();
await page.waitForTimeout(1000);
await page.click('text=用户管理');
await page.waitForTimeout(2000);
await context.unroute('**/api/**');
});
test('UAT-API-003: 权限不足提示', async ({ page }) => {
await page.evaluate(() => {
localStorage.setItem('token', 'user_token_without_admin_rights');
});
await page.goto('/roles');
await page.waitForTimeout(1000);
const errorMessage = page.locator('.el-message--error');
if (await errorMessage.isVisible()) {
await expect(errorMessage).toBeVisible();
}
});
test('UAT-API-004: 并发请求处理', async ({ page }) => {
const systemMenu = page.locator('.el-sub-menu__title:has-text("系统管理")');
await systemMenu.click();
await page.waitForTimeout(1000);
await page.click('text=用户管理');
await page.waitForURL('**/users', { timeout: 30000 });
const refreshButton = page.locator('button:has-text("刷新")').first();
if (await refreshButton.isVisible()) {
for (let i = 0; i < 3; i++) {
await refreshButton.click();
await page.waitForTimeout(100);
}
await page.waitForTimeout(1000);
await expect(page.locator('.el-table')).toBeVisible();
}
});
test('UAT-API-005: 数据加载状态显示', async ({ page }) => {
const systemMenu = page.locator('.el-sub-menu__title:has-text("系统管理")');
await systemMenu.click();
await page.waitForTimeout(1000);
const navigationPromise = page.click('text=用户管理');
const loading = page.locator('.el-loading-mask');
if (await loading.isVisible({ timeout: 100 }).catch(() => false)) {
await expect(loading).toBeVisible();
}
await navigationPromise;
await page.waitForURL('**/users', { timeout: 30000 });
await page.waitForLoadState('networkidle');
await expect(page.locator('.el-table')).toBeVisible();
});
});
@@ -0,0 +1,191 @@
import { test, expect } from '@playwright/test';
test.describe('UAT阶段六:数据持久化验证', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/');
await page.waitForLoadState('networkidle');
const usernameInput = page.locator('input[type="text"]').first();
const passwordInput = page.locator('input[type="password"]').first();
const loginButton = page.locator('button:has-text("登录")');
await usernameInput.fill('admin');
await passwordInput.fill('admin123');
await loginButton.click();
await page.waitForURL('**/dashboard', { timeout: 30000 });
await page.waitForLoadState('networkidle');
});
test('UAT-PERSIST-001: 角色创建持久化验证', async ({ page }) => {
const timestamp = Date.now();
const roleName = `测试角色_${timestamp}`;
const roleKey = `test_role_${timestamp}`;
const systemMenu = page.locator('.el-sub-menu__title:has-text("系统管理")');
await systemMenu.click();
await page.waitForTimeout(1000);
await page.click('text=角色管理');
await page.waitForURL('**/roles', { timeout: 30000 });
await page.waitForLoadState('networkidle');
const addButton = page.locator('button:has-text("新增")').first();
if (await addButton.isVisible()) {
await addButton.click();
await page.waitForTimeout(500);
const roleNameInput = page.locator('.el-dialog input[placeholder*="角色名称"]').first();
await roleNameInput.fill(roleName);
const roleKeyInput = page.locator('.el-dialog input[placeholder*="角色标识"]').first();
await roleKeyInput.fill(roleKey);
const confirmButton = page.locator('.el-dialog button:has-text("确定")');
await confirmButton.click();
await page.waitForTimeout(1000);
await page.reload();
await page.waitForLoadState('networkidle');
await page.waitForTimeout(1000);
const createdRole = page.locator(`text=${roleName}`);
await expect(createdRole).toBeVisible({ timeout: 5000 });
}
});
test('UAT-PERSIST-002: 用户创建持久化验证', async ({ page }) => {
const timestamp = Date.now();
const username = `testuser_${timestamp}`;
const systemMenu = page.locator('.el-sub-menu__title:has-text("系统管理")');
await systemMenu.click();
await page.waitForTimeout(1000);
await page.click('text=用户管理');
await page.waitForURL('**/users', { timeout: 30000 });
await page.waitForLoadState('networkidle');
const addButton = page.locator('button:has-text("新增用户")').first();
if (await addButton.isVisible()) {
await addButton.click();
await page.waitForTimeout(500);
const usernameInput = page.locator('.el-dialog input[placeholder*="用户名"]').first();
await usernameInput.fill(username);
const nicknameInput = page.locator('.el-dialog input[placeholder*="昵称"]').first();
await nicknameInput.fill(`测试用户_${timestamp}`);
const emailInput = page.locator('.el-dialog input[placeholder*="邮箱"]').first();
await emailInput.fill(`${username}@test.com`);
const phoneInput = page.locator('.el-dialog input[placeholder*="手机"]').first();
await phoneInput.fill('13800138000');
const passwordInput = page.locator('.el-dialog input[placeholder*="密码"]').first();
await passwordInput.fill('Test123456');
const confirmButton = page.locator('.el-dialog button:has-text("确定")');
await confirmButton.click();
await page.waitForTimeout(1000);
await page.reload();
await page.waitForLoadState('networkidle');
await page.waitForTimeout(1000);
const searchInput = page.locator('input[placeholder*="搜索"]').first();
if (await searchInput.isVisible()) {
await searchInput.fill(username);
await page.waitForTimeout(1000);
const createdUser = page.locator(`text=${username}`);
await expect(createdUser).toBeVisible({ timeout: 5000 });
}
}
});
test('UAT-PERSIST-003: 数据更新持久化验证', async ({ page }) => {
const systemMenu = page.locator('.el-sub-menu__title:has-text("系统管理")');
await systemMenu.click();
await page.waitForTimeout(1000);
await page.click('text=角色管理');
await page.waitForURL('**/roles', { timeout: 30000 });
await page.waitForLoadState('networkidle');
const editButton = page.locator('button:has-text("编辑")').first();
if (await editButton.isVisible()) {
await editButton.click();
await page.waitForTimeout(500);
const roleNameInput = page.locator('.el-dialog input[placeholder*="角色名称"]').first();
const currentValue = await roleNameInput.inputValue();
const newValue = `${currentValue}_已修改_${Date.now()}`;
await roleNameInput.fill(newValue);
const confirmButton = page.locator('.el-dialog button:has-text("确定")');
await confirmButton.click();
await page.waitForTimeout(1000);
await page.reload();
await page.waitForLoadState('networkidle');
await page.waitForTimeout(1000);
const updatedRole = page.locator(`text=${newValue}`);
await expect(updatedRole).toBeVisible({ timeout: 5000 });
}
});
test('UAT-PERSIST-004: 数据删除持久化验证', async ({ page }) => {
const timestamp = Date.now();
const roleName = `待删除角色_${timestamp}`;
const systemMenu = page.locator('.el-sub-menu__title:has-text("系统管理")');
await systemMenu.click();
await page.waitForTimeout(1000);
await page.click('text=角色管理');
await page.waitForURL('**/roles', { timeout: 30000 });
await page.waitForLoadState('networkidle');
const addButton = page.locator('button:has-text("新增")').first();
if (await addButton.isVisible()) {
await addButton.click();
await page.waitForTimeout(500);
const roleNameInput = page.locator('.el-dialog input[placeholder*="角色名称"]').first();
await roleNameInput.fill(roleName);
const roleKeyInput = page.locator('.el-dialog input[placeholder*="角色标识"]').first();
await roleKeyInput.fill(`delete_test_${timestamp}`);
const confirmButton = page.locator('.el-dialog button:has-text("确定")');
await confirmButton.click();
await page.waitForTimeout(1000);
const createdRole = page.locator(`text=${roleName}`);
await createdRole.scrollIntoViewIfNeeded();
const deleteButton = page.locator(`tr:has-text("${roleName}") button:has-text("删除")`).first();
if (await deleteButton.isVisible()) {
await deleteButton.click();
await page.waitForTimeout(500);
const confirmDeleteButton = page.locator('.el-message-box button:has-text("确定")');
if (await confirmDeleteButton.isVisible()) {
await confirmDeleteButton.click();
await page.waitForTimeout(1000);
await page.reload();
await page.waitForLoadState('networkidle');
await page.waitForTimeout(1000);
const deletedRole = page.locator(`text=${roleName}`);
await expect(deletedRole).not.toBeVisible({ timeout: 5000 });
}
}
}
});
});
@@ -0,0 +1,228 @@
import { test, expect } from '@playwright/test';
test.describe('UAT阶段七:边界条件与异常输入测试', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/');
await page.waitForLoadState('networkidle');
const usernameInput = page.locator('input[type="text"]').first();
const passwordInput = page.locator('input[type="password"]').first();
const loginButton = page.locator('button:has-text("登录")');
await usernameInput.fill('admin');
await passwordInput.fill('admin123');
await loginButton.click();
await page.waitForURL('**/dashboard', { timeout: 30000 });
await page.waitForLoadState('networkidle');
});
test('UAT-BOUNDARY-001: 用户名超长输入测试', async ({ page }) => {
const systemMenu = page.locator('.el-sub-menu__title:has-text("系统管理")');
await systemMenu.click();
await page.waitForTimeout(1000);
await page.click('text=用户管理');
await page.waitForURL('**/users', { timeout: 30000 });
await page.waitForLoadState('networkidle');
const addButton = page.locator('button:has-text("新增用户")').first();
if (await addButton.isVisible()) {
await addButton.click();
await page.waitForTimeout(500);
const usernameInput = page.locator('.el-dialog input[placeholder*="用户名"]').first();
const longUsername = 'a'.repeat(300);
await usernameInput.fill(longUsername);
const confirmButton = page.locator('.el-dialog button:has-text("确定")');
await confirmButton.click();
await page.waitForTimeout(500);
const errorMessage = page.locator('.el-form-item__error, .el-message--error');
const hasError = await errorMessage.isVisible().catch(() => false);
expect(hasError).toBeTruthy();
}
});
test('UAT-BOUNDARY-002: 特殊字符输入测试', async ({ page }) => {
const systemMenu = page.locator('.el-sub-menu__title:has-text("系统管理")');
await systemMenu.click();
await page.waitForTimeout(1000);
await page.click('text=角色管理');
await page.waitForURL('**/roles', { timeout: 30000 });
await page.waitForLoadState('networkidle');
const addButton = page.locator('button:has-text("新增")').first();
if (await addButton.isVisible()) {
await addButton.click();
await page.waitForTimeout(500);
const roleNameInput = page.locator('.el-dialog input[placeholder*="角色名称"]').first();
await roleNameInput.fill('<script>alert("XSS")</script>');
const roleKeyInput = page.locator('.el-dialog input[placeholder*="角色标识"]').first();
await roleKeyInput.fill("'; DROP TABLE roles; --");
const confirmButton = page.locator('.el-dialog button:has-text("确定")');
await confirmButton.click();
await page.waitForTimeout(500);
const errorMessage = page.locator('.el-form-item__error, .el-message--error');
const hasError = await errorMessage.isVisible().catch(() => false);
if (!hasError) {
const cancelButton = page.locator('.el-dialog button:has-text("取消")');
await cancelButton.click();
}
}
});
test('UAT-BOUNDARY-003: 空值输入测试', async ({ page }) => {
const systemMenu = page.locator('.el-sub-menu__title:has-text("系统管理")');
await systemMenu.click();
await page.waitForTimeout(1000);
await page.click('text=用户管理');
await page.waitForURL('**/users', { timeout: 30000 });
await page.waitForLoadState('networkidle');
const addButton = page.locator('button:has-text("新增用户")').first();
if (await addButton.isVisible()) {
await addButton.click();
await page.waitForTimeout(500);
const confirmButton = page.locator('.el-dialog button:has-text("确定")');
await confirmButton.click();
await page.waitForTimeout(500);
const formErrors = page.locator('.el-form-item__error');
const errorCount = await formErrors.count();
expect(errorCount).toBeGreaterThan(0);
}
});
test('UAT-BOUNDARY-004: 邮箱格式验证测试', async ({ page }) => {
const systemMenu = page.locator('.el-sub-menu__title:has-text("系统管理")');
await systemMenu.click();
await page.waitForTimeout(1000);
await page.click('text=用户管理');
await page.waitForURL('**/users', { timeout: 30000 });
await page.waitForLoadState('networkidle');
const addButton = page.locator('button:has-text("新增用户")').first();
if (await addButton.isVisible()) {
await addButton.click();
await page.waitForTimeout(500);
const emailInput = page.locator('.el-dialog input[placeholder*="邮箱"]').first();
await emailInput.fill('invalid-email');
const confirmButton = page.locator('.el-dialog button:has-text("确定")');
await confirmButton.click();
await page.waitForTimeout(500);
const emailError = page.locator('.el-form-item__error:has-text("邮箱")');
const hasError = await emailError.isVisible().catch(() => false);
expect(hasError).toBeTruthy();
}
});
test('UAT-BOUNDARY-005: 手机号格式验证测试', async ({ page }) => {
const systemMenu = page.locator('.el-sub-menu__title:has-text("系统管理")');
await systemMenu.click();
await page.waitForTimeout(1000);
await page.click('text=用户管理');
await page.waitForURL('**/users', { timeout: 30000 });
await page.waitForLoadState('networkidle');
const addButton = page.locator('button:has-text("新增用户")').first();
if (await addButton.isVisible()) {
await addButton.click();
await page.waitForTimeout(500);
const phoneInput = page.locator('.el-dialog input[placeholder*="手机"]').first();
await phoneInput.fill('123');
const confirmButton = page.locator('.el-dialog button:has-text("确定")');
await confirmButton.click();
await page.waitForTimeout(500);
const phoneError = page.locator('.el-form-item__error:has-text("手机")');
const hasError = await phoneError.isVisible().catch(() => false);
expect(hasError).toBeTruthy();
}
});
test('UAT-BOUNDARY-006: Emoji表情输入测试', async ({ page }) => {
const systemMenu = page.locator('.el-sub-menu__title:has-text("系统管理")');
await systemMenu.click();
await page.waitForTimeout(1000);
await page.click('text=角色管理');
await page.waitForURL('**/roles', { timeout: 30000 });
await page.waitForLoadState('networkidle');
const addButton = page.locator('button:has-text("新增")').first();
if (await addButton.isVisible()) {
await addButton.click();
await page.waitForTimeout(500);
const roleNameInput = page.locator('.el-dialog input[placeholder*="角色名称"]').first();
await roleNameInput.fill('测试角色😀🎉🔥');
const roleKeyInput = page.locator('.el-dialog input[placeholder*="角色标识"]').first();
await roleKeyInput.fill('test_emoji_role');
const confirmButton = page.locator('.el-dialog button:has-text("确定")');
await confirmButton.click();
await page.waitForTimeout(1000);
const errorMessage = page.locator('.el-message--error');
const hasError = await errorMessage.isVisible().catch(() => false);
if (!hasError) {
const cancelButton = page.locator('.el-dialog button:has-text("取消")');
if (await cancelButton.isVisible()) {
await cancelButton.click();
}
}
}
});
test('UAT-BOUNDARY-007: 数字输入边界测试', async ({ page }) => {
const systemMenu = page.locator('.el-sub-menu__title:has-text("系统管理")');
await systemMenu.click();
await page.waitForTimeout(1000);
await page.click('text=角色管理');
await page.waitForURL('**/roles', { timeout: 30000 });
await page.waitForLoadState('networkidle');
const addButton = page.locator('button:has-text("新增")').first();
if (await addButton.isVisible()) {
await addButton.click();
await page.waitForTimeout(500);
const sortInput = page.locator('.el-dialog input[type="number"]').first();
if (await sortInput.isVisible()) {
await sortInput.fill('-1');
const confirmButton = page.locator('.el-dialog button:has-text("确定")');
await confirmButton.click();
await page.waitForTimeout(500);
const errorMessage = page.locator('.el-form-item__error');
const hasError = await errorMessage.isVisible().catch(() => false);
if (!hasError) {
const cancelButton = page.locator('.el-dialog button:has-text("取消")');
await cancelButton.click();
}
}
}
});
});
@@ -0,0 +1,195 @@
import { test, expect } from '@playwright/test';
test.describe('UAT阶段八:安全测试', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/');
await page.waitForLoadState('networkidle');
const usernameInput = page.locator('input[type="text"]').first();
const passwordInput = page.locator('input[type="password"]').first();
const loginButton = page.locator('button:has-text("登录")');
await usernameInput.fill('admin');
await passwordInput.fill('admin123');
await loginButton.click();
await page.waitForURL('**/dashboard', { timeout: 30000 });
await page.waitForLoadState('networkidle');
});
test('UAT-SECURITY-001: XSS攻击防护测试', async ({ page }) => {
const systemMenu = page.locator('.el-sub-menu__title:has-text("系统管理")');
await systemMenu.click();
await page.waitForTimeout(1000);
await page.click('text=角色管理');
await page.waitForURL('**/roles', { timeout: 30000 });
await page.waitForLoadState('networkidle');
const addButton = page.locator('button:has-text("新增")').first();
if (await addButton.isVisible()) {
await addButton.click();
await page.waitForTimeout(500);
const roleNameInput = page.locator('.el-dialog input[placeholder*="角色名称"]').first();
const xssPayload = '<img src=x onerror=alert("XSS")>';
await roleNameInput.fill(xssPayload);
const roleKeyInput = page.locator('.el-dialog input[placeholder*="角色标识"]').first();
await roleKeyInput.fill('xss_test');
const confirmButton = page.locator('.el-dialog button:has-text("确定")');
await confirmButton.click();
await page.waitForTimeout(1000);
const errorMessage = page.locator('.el-form-item__error, .el-message--error');
const hasError = await errorMessage.isVisible().catch(() => false);
if (!hasError) {
const cancelButton = page.locator('.el-dialog button:has-text("取消")');
if (await cancelButton.isVisible()) {
await cancelButton.click();
}
}
expect(hasError).toBeTruthy();
}
});
test('UAT-SECURITY-002: SQL注入防护测试', async ({ page }) => {
const systemMenu = page.locator('.el-sub-menu__title:has-text("系统管理")');
await systemMenu.click();
await page.waitForTimeout(1000);
await page.click('text=用户管理');
await page.waitForURL('**/users', { timeout: 30000 });
await page.waitForLoadState('networkidle');
const searchInput = page.locator('input[placeholder*="搜索"]').first();
if (await searchInput.isVisible()) {
await searchInput.fill("admin' OR '1'='1");
await page.waitForTimeout(1000);
await expect(page.locator('.el-table')).toBeVisible();
const allRows = await page.locator('.el-table__row').count();
expect(allRows).toBeLessThan(100);
}
});
test('UAT-SECURITY-003: 未授权访问测试', async ({ page }) => {
await page.evaluate(() => {
localStorage.removeItem('token');
});
await page.goto('/users');
await page.waitForTimeout(2000);
const currentUrl = page.url();
expect(currentUrl).toContain('/login');
});
test('UAT-SECURITY-004: CSRF防护测试', async ({ page }) => {
const systemMenu = page.locator('.el-sub-menu__title:has-text("系统管理")');
await systemMenu.click();
await page.waitForTimeout(1000);
await page.click('text=角色管理');
await page.waitForURL('**/roles', { timeout: 30000 });
await page.waitForLoadState('networkidle');
const addButton = page.locator('button:has-text("新增")').first();
if (await addButton.isVisible()) {
await addButton.click();
await page.waitForTimeout(500);
const roleNameInput = page.locator('.el-dialog input[placeholder*="角色名称"]').first();
await roleNameInput.fill('CSRF测试角色');
const roleKeyInput = page.locator('.el-dialog input[placeholder*="角色标识"]').first();
await roleKeyInput.fill('csrf_test');
const confirmButton = page.locator('.el-dialog button:has-text("确定")');
await confirmButton.click();
await page.waitForTimeout(1000);
const successMessage = page.locator('.el-message--success');
const errorMessage = page.locator('.el-message--error');
const hasSuccess = await successMessage.isVisible().catch(() => false);
const hasError = await errorMessage.isVisible().catch(() => false);
expect(hasSuccess || hasError).toBeTruthy();
}
});
test('UAT-SECURITY-005: 密码强度验证测试', async ({ page }) => {
const systemMenu = page.locator('.el-sub-menu__title:has-text("系统管理")');
await systemMenu.click();
await page.waitForTimeout(1000);
await page.click('text=用户管理');
await page.waitForURL('**/users', { timeout: 30000 });
await page.waitForLoadState('networkidle');
const addButton = page.locator('button:has-text("新增用户")').first();
if (await addButton.isVisible()) {
await addButton.click();
await page.waitForTimeout(500);
const usernameInput = page.locator('.el-dialog input[placeholder*="用户名"]').first();
await usernameInput.fill('testuser');
const passwordInput = page.locator('.el-dialog input[placeholder*="密码"]').first();
await passwordInput.fill('123');
const confirmButton = page.locator('.el-dialog button:has-text("确定")');
await confirmButton.click();
await page.waitForTimeout(500);
const passwordError = page.locator('.el-form-item__error');
const hasError = await passwordError.isVisible().catch(() => false);
expect(hasError).toBeTruthy();
}
});
test('UAT-SECURITY-006: 敏感信息泄露测试', async ({ page }) => {
await page.goto('/');
await page.waitForLoadState('networkidle');
const pageContent = await page.content();
expect(pageContent).not.toContain('password');
expect(pageContent).not.toContain('secret');
expect(pageContent).not.toContain('api_key');
expect(pageContent).not.toContain('private_key');
});
test('UAT-SECURITY-007: 会话超时测试', async ({ page }) => {
const systemMenu = page.locator('.el-sub-menu__title:has-text("系统管理")');
await systemMenu.click();
await page.waitForTimeout(1000);
await page.click('text=用户管理');
await page.waitForURL('**/users', { timeout: 30000 });
await page.waitForLoadState('networkidle');
await page.evaluate(() => {
const token = localStorage.getItem('token');
if (token) {
const expiredToken = token.replace(/\.(.*?)\./, '.expired.');
localStorage.setItem('token', expiredToken);
}
});
await page.reload();
await page.waitForTimeout(2000);
const currentUrl = page.url();
const isLoginPage = currentUrl.includes('/login');
const hasError = await page.locator('.el-message--error').isVisible().catch(() => false);
expect(isLoginPage || hasError).toBeTruthy();
});
});