import type { Page } from '@playwright/test' import { captureScreenshot, expect, test } from './fixtures' import { chooseSelectOption, confirmMessageBox, dismissOverlays, fillFormItem, loginAsAdmin, roleCardByText, tableRowByText, visibleDialog, } from './helpers' const runToken = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 7)}` const upperToken = runToken.replace(/[^a-z0-9]/gi, '').toUpperCase().slice(-14) const lowerToken = runToken.replace(/[^a-z0-9]/gi, '').toLowerCase().slice(-18) const data = { prefix: `UTE2E-${upperToken}`, departmentName: `UTE2E-${upperToken}-部门`, departmentCode: `E2E_${upperToken}`.slice(0, 32), roleName: `UTE2E-${upperToken}-角色`, roleCode: `e2e_${lowerToken}`.slice(0, 48), roleShort: '测', userName: `e2e_${lowerToken}`.slice(0, 32), displayName: `UTE2E-${upperToken}-用户`, editedDisplayName: `UTE2E-${upperToken}-已编辑`, password: `Tmp@${upperToken}a1`, } async function createDepartment(page: Page) { await page.goto('/system/departments') await page.getByRole('button', { name: '新增部门', exact: true }).click() const dialog = visibleDialog(page) await fillFormItem(dialog, '部门名称', data.departmentName) await fillFormItem(dialog, '部门编码', data.departmentCode) await fillFormItem(dialog, '负责人', data.prefix) await fillFormItem(dialog, '部门职责', `${data.prefix} 自动化测试数据`) await dialog.getByRole('button', { name: '创建部门' }).click() await expect(tableRowByText(page, data.departmentName)).toBeVisible() } async function createRole(page: Page) { await page.goto('/system/roles') await page.getByRole('button', { name: '新增角色', exact: true }).click() const dialog = visibleDialog(page) await fillFormItem(dialog, '角色名称', data.roleName) await fillFormItem(dialog, '角色编码', data.roleCode) await fillFormItem(dialog, '界面简称', data.roleShort) await fillFormItem(dialog, '角色说明', `${data.prefix} 自动化角色`) await dialog.getByRole('button', { name: '创建角色' }).click() await expect(roleCardByText(page, data.roleName)).toBeVisible() } async function createUser(page: Page) { await page.goto('/system/users') await page.getByRole('button', { name: '新增用户', exact: true }).click() const dialog = visibleDialog(page) await fillFormItem(dialog, '姓名', data.displayName) await fillFormItem(dialog, '登录账号', data.userName) await chooseSelectOption(page, dialog, '所属部门', data.departmentName) await fillFormItem(dialog, '初始密码', data.password) const roleCheckbox = dialog.locator('.el-checkbox').filter({ hasText: data.roleName }) await roleCheckbox.click() await chooseSelectOption(page, dialog, '登录默认身份', data.roleName) await dialog.getByRole('button', { name: '创建用户' }).click() await expect(tableRowByText(page, data.userName)).toBeVisible() } async function editAndSearchData(page: Page) { await page.goto('/system/users') await page.getByPlaceholder('请输入姓名、账号、部门或角色').fill(data.userName) await page.getByRole('button', { name: '搜索', exact: true }).click() const userRow = tableRowByText(page, data.userName) await expect(userRow).toBeVisible() await userRow.getByRole('button', { name: '编辑' }).click() const userDialog = visibleDialog(page) await fillFormItem(userDialog, '姓名', data.editedDisplayName) await userDialog.getByRole('button', { name: '保存修改' }).click() await expect(tableRowByText(page, data.editedDisplayName)).toBeVisible() await page.goto('/system/roles') await page.getByPlaceholder('请输入角色名称、编码或说明').fill(data.roleCode) await page.getByRole('button', { name: '搜索', exact: true }).click() const roleCard = roleCardByText(page, data.roleName) await expect(roleCard).toBeVisible() await roleCard.getByRole('button', { name: '编辑' }).click() const roleDialog = visibleDialog(page) await fillFormItem(roleDialog, '角色说明', `${data.prefix} 已编辑`) await roleDialog.getByRole('button', { name: '保存修改' }).click() await expect(roleCardByText(page, `${data.prefix} 已编辑`)).toBeVisible() await page.goto('/system/departments') await page.getByPlaceholder('请输入部门、编码或负责人').fill(data.departmentCode) await page.getByRole('button', { name: '搜索', exact: true }).click() await expect(tableRowByText(page, data.departmentName)).toBeVisible() } async function grantPermission(page: Page) { await page.goto('/system/roles') await page.getByPlaceholder('请输入角色名称、编码或说明').fill(data.roleCode) await page.getByRole('button', { name: '搜索', exact: true }).click() const roleRow = roleCardByText(page, data.roleName) await Promise.all([ page.waitForResponse((response) => response.request().method() === 'GET' && /\/permissions\/roles\/[^/?]+/.test(response.url()) && response.ok()), roleRow.getByRole('button', { name: '配置权限' }).click(), ]) const drawer = page.locator('.role-permission-drawer') await expect(drawer).toBeVisible() await expect(drawer.locator('.role-permission-summary h3')).toHaveText(data.roleName) const auditCode = page.locator('code').filter({ hasText: /^system\.audit$/ }) const auditPermission = drawer.locator('.el-tree-node').filter({ has: auditCode }).last() await auditPermission.locator('.el-checkbox').click() const saveButton = drawer.getByRole('button', { name: '保存授权' }) await expect(saveButton).toBeEnabled() await saveButton.click() await expect(page.getByText(/权限已保存|当前权限配置已保存/).last()).toBeVisible() } async function verifyAudit(page: Page) { await page.goto('/system/audit') const search = page.getByPlaceholder('请输入用户、模块、操作或地址') await search.fill(data.prefix) await page.getByRole('button', { name: '搜索', exact: true }).click() await expect.poll(async () => page.locator('.el-table__body-wrapper .el-table__row').count(), { message: '等待与本次唯一前缀相关的审计记录可见', timeout: 10_000, }).toBeGreaterThan(0) } async function deleteUserIfPresent(page: Page) { await dismissOverlays(page) await page.goto('/system/users') await page.getByPlaceholder('请输入姓名、账号、部门或角色').fill(data.userName) await page.getByRole('button', { name: '搜索', exact: true }).click() const row = tableRowByText(page, data.userName) if (!(await row.waitFor({ state: 'visible', timeout: 5_000 }).then(() => true).catch(() => false))) return false await row.getByRole('button', { name: '删除' }).click() await confirmMessageBox(page, '确认删除') await expect(row).toHaveCount(0) return true } async function deleteRoleIfPresent(page: Page) { await dismissOverlays(page) await page.goto('/system/roles') await page.getByPlaceholder('请输入角色名称、编码或说明').fill(data.roleCode) await page.getByRole('button', { name: '搜索', exact: true }).click() const card = roleCardByText(page, data.roleName) if (!(await card.waitFor({ state: 'visible', timeout: 5_000 }).then(() => true).catch(() => false))) return false await card.getByRole('button', { name: '删除' }).click() await confirmMessageBox(page, '删除角色') await expect(card).toHaveCount(0) return true } async function deleteDepartmentIfPresent(page: Page) { await dismissOverlays(page) await page.goto('/system/departments') await page.getByPlaceholder('请输入部门、编码或负责人').fill(data.departmentCode) await page.getByRole('button', { name: '搜索', exact: true }).click() const row = tableRowByText(page, data.departmentName) if (!(await row.waitFor({ state: 'visible', timeout: 5_000 }).then(() => true).catch(() => false))) return false await row.getByRole('button', { name: '删除' }).click() const dialog = visibleDialog(page) await dialog.getByRole('button', { name: '确认删除并迁移' }).click() await expect(row).toHaveCount(0) return true } test('系统管理测试数据可创建、编辑、搜索、授权、审计并完整清理', async ({ page }, testInfo) => { await loginAsAdmin(page) let testFailure: unknown try { await test.step('创建测试部门', async () => { await createDepartment(page) await captureScreenshot(page, testInfo, 'crud-department-created') }) await test.step('创建测试角色', async () => { await createRole(page) await captureScreenshot(page, testInfo, 'crud-role-created') }) await test.step('创建测试用户', async () => { await createUser(page) await captureScreenshot(page, testInfo, 'crud-user-created') }) await test.step('编辑并搜索测试数据', async () => { await editAndSearchData(page) await captureScreenshot(page, testInfo, 'crud-edit-search-success') }) await test.step('配置角色权限', async () => { await grantPermission(page) await captureScreenshot(page, testInfo, 'crud-permission-saved') }) await test.step('审计记录可检索', async () => { await verifyAudit(page) await captureScreenshot(page, testInfo, 'crud-audit-found') }) } catch (error) { testFailure = error } finally { const cleanupErrors: unknown[] = [] for (const [name, screenshotLabel, cleanup] of [ ['用户', 'crud-user-deleted', deleteUserIfPresent], ['角色', 'crud-role-deleted', deleteRoleIfPresent], ['部门', 'crud-department-deleted', deleteDepartmentIfPresent], ] as const) { try { const deleted = await cleanup(page) if (!testFailure && deleted) await captureScreenshot(page, testInfo, screenshotLabel) } catch (error) { cleanupErrors.push(new Error(`${name}清理失败`, { cause: error })) } } if (testFailure || cleanupErrors.length) { throw new AggregateError( [testFailure, ...cleanupErrors].filter((error) => error !== undefined), cleanupErrors.length ? '系统管理 E2E 失败,且已执行全部清理步骤;请检查聚合错误。' : '系统管理 E2E 失败。', ) } } })