You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

224 lines
9.9 KiB

  1. import type { Page } from '@playwright/test'
  2. import { captureScreenshot, expect, test } from './fixtures'
  3. import {
  4. chooseSelectOption,
  5. confirmMessageBox,
  6. dismissOverlays,
  7. fillFormItem,
  8. loginAsAdmin,
  9. roleCardByText,
  10. tableRowByText,
  11. visibleDialog,
  12. } from './helpers'
  13. const runToken = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 7)}`
  14. const upperToken = runToken.replace(/[^a-z0-9]/gi, '').toUpperCase().slice(-14)
  15. const lowerToken = runToken.replace(/[^a-z0-9]/gi, '').toLowerCase().slice(-18)
  16. const data = {
  17. prefix: `UTE2E-${upperToken}`,
  18. departmentName: `UTE2E-${upperToken}-部门`,
  19. departmentCode: `E2E_${upperToken}`.slice(0, 32),
  20. roleName: `UTE2E-${upperToken}-角色`,
  21. roleCode: `e2e_${lowerToken}`.slice(0, 48),
  22. roleShort: '测',
  23. userName: `e2e_${lowerToken}`.slice(0, 32),
  24. displayName: `UTE2E-${upperToken}-用户`,
  25. editedDisplayName: `UTE2E-${upperToken}-已编辑`,
  26. password: `Tmp@${upperToken}a1`,
  27. }
  28. async function createDepartment(page: Page) {
  29. await page.goto('/system/departments')
  30. await page.getByRole('button', { name: '新增部门', exact: true }).click()
  31. const dialog = visibleDialog(page)
  32. await fillFormItem(dialog, '部门名称', data.departmentName)
  33. await fillFormItem(dialog, '部门编码', data.departmentCode)
  34. await fillFormItem(dialog, '负责人', data.prefix)
  35. await fillFormItem(dialog, '部门职责', `${data.prefix} 自动化测试数据`)
  36. await dialog.getByRole('button', { name: '创建部门' }).click()
  37. await expect(tableRowByText(page, data.departmentName)).toBeVisible()
  38. }
  39. async function createRole(page: Page) {
  40. await page.goto('/system/roles')
  41. await page.getByRole('button', { name: '新增角色', exact: true }).click()
  42. const dialog = visibleDialog(page)
  43. await fillFormItem(dialog, '角色名称', data.roleName)
  44. await fillFormItem(dialog, '角色编码', data.roleCode)
  45. await fillFormItem(dialog, '界面简称', data.roleShort)
  46. await fillFormItem(dialog, '角色说明', `${data.prefix} 自动化角色`)
  47. await dialog.getByRole('button', { name: '创建角色' }).click()
  48. await expect(roleCardByText(page, data.roleName)).toBeVisible()
  49. }
  50. async function createUser(page: Page) {
  51. await page.goto('/system/users')
  52. await page.getByRole('button', { name: '新增用户', exact: true }).click()
  53. const dialog = visibleDialog(page)
  54. await fillFormItem(dialog, '姓名', data.displayName)
  55. await fillFormItem(dialog, '登录账号', data.userName)
  56. await chooseSelectOption(page, dialog, '所属部门', data.departmentName)
  57. await fillFormItem(dialog, '初始密码', data.password)
  58. const roleCheckbox = dialog.locator('.el-checkbox').filter({ hasText: data.roleName })
  59. await roleCheckbox.click()
  60. await chooseSelectOption(page, dialog, '登录默认身份', data.roleName)
  61. await dialog.getByRole('button', { name: '创建用户' }).click()
  62. await expect(tableRowByText(page, data.userName)).toBeVisible()
  63. }
  64. async function editAndSearchData(page: Page) {
  65. await page.goto('/system/users')
  66. await page.getByPlaceholder('请输入姓名、账号、部门或角色').fill(data.userName)
  67. await page.getByRole('button', { name: '搜索', exact: true }).click()
  68. const userRow = tableRowByText(page, data.userName)
  69. await expect(userRow).toBeVisible()
  70. await userRow.getByRole('button', { name: '编辑' }).click()
  71. const userDialog = visibleDialog(page)
  72. await fillFormItem(userDialog, '姓名', data.editedDisplayName)
  73. await userDialog.getByRole('button', { name: '保存修改' }).click()
  74. await expect(tableRowByText(page, data.editedDisplayName)).toBeVisible()
  75. await page.goto('/system/roles')
  76. await page.getByPlaceholder('请输入角色名称、编码或说明').fill(data.roleCode)
  77. await page.getByRole('button', { name: '搜索', exact: true }).click()
  78. const roleCard = roleCardByText(page, data.roleName)
  79. await expect(roleCard).toBeVisible()
  80. await roleCard.getByRole('button', { name: '编辑' }).click()
  81. const roleDialog = visibleDialog(page)
  82. await fillFormItem(roleDialog, '角色说明', `${data.prefix} 已编辑`)
  83. await roleDialog.getByRole('button', { name: '保存修改' }).click()
  84. await expect(roleCardByText(page, `${data.prefix} 已编辑`)).toBeVisible()
  85. await page.goto('/system/departments')
  86. await page.getByPlaceholder('请输入部门、编码、负责人或层级路径').fill(data.departmentCode)
  87. await page.getByRole('button', { name: '搜索', exact: true }).click()
  88. await expect(tableRowByText(page, data.departmentName)).toBeVisible()
  89. }
  90. async function grantPermission(page: Page) {
  91. await page.goto('/system/permissions')
  92. const roleButton = page.locator('.role-selector > button').filter({ hasText: data.roleName })
  93. await Promise.all([
  94. page.waitForResponse((response) => response.request().method() === 'GET' && /\/permissions\/roles\/[^/?]+/.test(response.url()) && response.ok()),
  95. roleButton.click(),
  96. ])
  97. await expect(roleButton).toHaveClass(/active/)
  98. await expect(page.locator('.selected-role-copy h2')).toHaveText(data.roleName)
  99. const auditCode = page.locator('code').filter({ hasText: /^system\.audit$/ })
  100. const auditPermission = page.locator('.permission-item').filter({ has: auditCode })
  101. await expect(auditPermission).toHaveAttribute('aria-checked', 'false')
  102. await auditPermission.click()
  103. const saveButton = page.getByRole('button', { name: '保存权限配置' })
  104. await expect(saveButton).toBeEnabled()
  105. await saveButton.click()
  106. await expect(page.getByText(/权限已保存|当前权限配置已保存/).last()).toBeVisible()
  107. }
  108. async function verifyAudit(page: Page) {
  109. await page.goto('/system/audit')
  110. const search = page.getByPlaceholder('请输入用户、模块、操作或地址')
  111. await search.fill(data.prefix)
  112. await page.getByRole('button', { name: '搜索', exact: true }).click()
  113. await expect.poll(async () => page.locator('.el-table__body-wrapper .el-table__row').count(), {
  114. message: '等待与本次唯一前缀相关的审计记录可见',
  115. timeout: 10_000,
  116. }).toBeGreaterThan(0)
  117. }
  118. async function deleteUserIfPresent(page: Page) {
  119. await dismissOverlays(page)
  120. await page.goto('/system/users')
  121. await page.getByPlaceholder('请输入姓名、账号、部门或角色').fill(data.userName)
  122. await page.getByRole('button', { name: '搜索', exact: true }).click()
  123. const row = tableRowByText(page, data.userName)
  124. if (!(await row.waitFor({ state: 'visible', timeout: 5_000 }).then(() => true).catch(() => false))) return false
  125. await row.getByRole('button', { name: '删除' }).click()
  126. await confirmMessageBox(page, '确认删除')
  127. await expect(row).toHaveCount(0)
  128. return true
  129. }
  130. async function deleteRoleIfPresent(page: Page) {
  131. await dismissOverlays(page)
  132. await page.goto('/system/roles')
  133. await page.getByPlaceholder('请输入角色名称、编码或说明').fill(data.roleCode)
  134. await page.getByRole('button', { name: '搜索', exact: true }).click()
  135. const card = roleCardByText(page, data.roleName)
  136. if (!(await card.waitFor({ state: 'visible', timeout: 5_000 }).then(() => true).catch(() => false))) return false
  137. await card.getByRole('button', { name: '删除' }).click()
  138. await confirmMessageBox(page, '删除角色')
  139. await expect(card).toHaveCount(0)
  140. return true
  141. }
  142. async function deleteDepartmentIfPresent(page: Page) {
  143. await dismissOverlays(page)
  144. await page.goto('/system/departments')
  145. await page.getByPlaceholder('请输入部门、编码、负责人或层级路径').fill(data.departmentCode)
  146. await page.getByRole('button', { name: '搜索', exact: true }).click()
  147. const row = tableRowByText(page, data.departmentName)
  148. if (!(await row.waitFor({ state: 'visible', timeout: 5_000 }).then(() => true).catch(() => false))) return false
  149. await row.getByRole('button', { name: '删除' }).click()
  150. const dialog = visibleDialog(page)
  151. await dialog.getByRole('button', { name: '确认删除并迁移' }).click()
  152. await expect(row).toHaveCount(0)
  153. return true
  154. }
  155. test('系统管理测试数据可创建、编辑、搜索、授权、审计并完整清理', async ({ page }, testInfo) => {
  156. await loginAsAdmin(page)
  157. let testFailure: unknown
  158. try {
  159. await test.step('创建测试部门', async () => {
  160. await createDepartment(page)
  161. await captureScreenshot(page, testInfo, 'crud-department-created')
  162. })
  163. await test.step('创建测试角色', async () => {
  164. await createRole(page)
  165. await captureScreenshot(page, testInfo, 'crud-role-created')
  166. })
  167. await test.step('创建测试用户', async () => {
  168. await createUser(page)
  169. await captureScreenshot(page, testInfo, 'crud-user-created')
  170. })
  171. await test.step('编辑并搜索测试数据', async () => {
  172. await editAndSearchData(page)
  173. await captureScreenshot(page, testInfo, 'crud-edit-search-success')
  174. })
  175. await test.step('配置角色权限', async () => {
  176. await grantPermission(page)
  177. await captureScreenshot(page, testInfo, 'crud-permission-saved')
  178. })
  179. await test.step('审计记录可检索', async () => {
  180. await verifyAudit(page)
  181. await captureScreenshot(page, testInfo, 'crud-audit-found')
  182. })
  183. } catch (error) {
  184. testFailure = error
  185. } finally {
  186. const cleanupErrors: unknown[] = []
  187. for (const [name, screenshotLabel, cleanup] of [
  188. ['用户', 'crud-user-deleted', deleteUserIfPresent],
  189. ['角色', 'crud-role-deleted', deleteRoleIfPresent],
  190. ['部门', 'crud-department-deleted', deleteDepartmentIfPresent],
  191. ] as const) {
  192. try {
  193. const deleted = await cleanup(page)
  194. if (!testFailure && deleted) await captureScreenshot(page, testInfo, screenshotLabel)
  195. } catch (error) {
  196. cleanupErrors.push(new Error(`${name}清理失败`, { cause: error }))
  197. }
  198. }
  199. if (testFailure || cleanupErrors.length) {
  200. throw new AggregateError(
  201. [testFailure, ...cleanupErrors].filter((error) => error !== undefined),
  202. cleanupErrors.length ? '系统管理 E2E 失败,且已执行全部清理步骤;请检查聚合错误。' : '系统管理 E2E 失败。',
  203. )
  204. }
  205. }
  206. })