Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 

205 строки
8.9 KiB

  1. import type { Page, Route } from '@playwright/test'
  2. import { captureScreenshot, expect, test } from './fixtures'
  3. const profile = {
  4. user: {
  5. id: '1',
  6. username: 'admin',
  7. displayName: '系统管理员',
  8. departmentId: '10',
  9. departmentName: '信息中心',
  10. mustChangePassword: false,
  11. version: 3,
  12. },
  13. activeRoleId: '100',
  14. activeRole: { id: '100', code: 'admin', name: '管理员' },
  15. roles: [
  16. {
  17. id: '100',
  18. code: 'admin',
  19. name: '管理员',
  20. shortName: '管',
  21. status: 1,
  22. builtIn: 1,
  23. isSuperAdmin: 1,
  24. dataScopeCode: 'ALL',
  25. },
  26. ],
  27. permissions: ['dashboard.view'],
  28. authorizationMode: 'SINGLE_ACTIVE',
  29. loginTime: 1786387200,
  30. } as const
  31. const envelope = (data: unknown, requestId: string) => JSON.stringify({
  32. code: 200,
  33. message: '成功',
  34. data,
  35. timestamp: '2026-08-11T10:00:00+08:00',
  36. requestId,
  37. })
  38. const fulfillJson = (route: Route, data: unknown, requestId: string, status = 200) => route.fulfill({
  39. status,
  40. contentType: 'application/json',
  41. body: envelope(data, requestId),
  42. })
  43. const prepareAuthenticatedProfile = async (page: Page) => {
  44. await page.addInitScript(() => {
  45. sessionStorage.setItem('unreal-tran:web:access-token:v1', 'contract-access-token')
  46. })
  47. await page.route('**/api/auth/v1/auth/me', (route) => fulfillJson(route, profile, 'ute2e-profile-me'))
  48. await page.route('**/api/auth/v1/menus/navigation', (route) => fulfillJson(route, [], 'ute2e-profile-menu'))
  49. }
  50. test.describe('平台账号与个人中心', () => {
  51. test('页头精简且账号菜单仅保留个人中心和退出登录', async ({ page }, testInfo) => {
  52. await prepareAuthenticatedProfile(page)
  53. await page.goto('/profile')
  54. const header = page.locator('.platform-header')
  55. await expect(header).toBeVisible()
  56. expect((await header.boundingBox())?.height).toBeLessThanOrEqual(62)
  57. await expect(header).not.toContainText('Maintenance Digital Workshop')
  58. await page.getByRole('button', { name: '账号菜单:系统管理员' }).click()
  59. const dropdown = page.locator('.user-dropdown:visible')
  60. await expect(dropdown).toBeVisible()
  61. await expect(dropdown.locator('.el-dropdown-menu__item')).toHaveCount(2)
  62. await expect(dropdown.getByText('个人中心', { exact: true })).toBeVisible()
  63. await expect(dropdown.getByText('退出登录', { exact: true })).toBeVisible()
  64. await expect(dropdown).not.toContainText('修改密码')
  65. await expect(dropdown).not.toContainText('角色切换')
  66. await page.keyboard.press('Escape')
  67. await captureScreenshot(page, testInfo, 'profile-header-account-menu')
  68. })
  69. test('个人中心展示真实资料并通过接口修改密码', async ({ page }, testInfo) => {
  70. await prepareAuthenticatedProfile(page)
  71. let passwordRequest: Record<string, unknown> | null = null
  72. await page.route('**/api/auth/v1/auth/password', async (route) => {
  73. passwordRequest = route.request().postDataJSON() as Record<string, unknown>
  74. await fulfillJson(route, { reauthenticationRequired: false }, 'ute2e-profile-password')
  75. })
  76. await page.goto('/profile')
  77. await expect(page.getByRole('heading', { name: '个人中心' })).toBeVisible()
  78. await expect(page.getByText('系统管理员 · admin', { exact: true })).toBeVisible()
  79. const descriptions = page.locator('.profile-descriptions')
  80. await expect(descriptions).toHaveClass(/el-descriptions/)
  81. await expect(descriptions.locator('.el-descriptions__table')).toBeVisible()
  82. await expect(descriptions).toContainText('信息中心')
  83. await expect(descriptions).toContainText('管理员')
  84. const columnWidths = await page.locator('.profile-grid > .system-panel').evaluateAll((panels) => panels.map((panel) => panel.getBoundingClientRect().width))
  85. expect(columnWidths).toHaveLength(2)
  86. expect(Math.abs(columnWidths[0]! - columnWidths[1]!)).toBeLessThanOrEqual(1)
  87. const currentPassword = page.locator('input[name="currentPassword"]')
  88. const newPassword = page.locator('input[name="newPassword"]')
  89. const confirmPassword = page.locator('input[name="confirmPassword"]')
  90. await expect(currentPassword).toHaveAttribute('placeholder', '请输入当前密码')
  91. await expect(newPassword).toHaveAttribute('placeholder', '请输入符合全部规则的新密码')
  92. await expect(confirmPassword).toHaveAttribute('placeholder', '请再次输入新密码')
  93. await currentPassword.fill('Current-Password-1')
  94. await expect(page.locator('.profile-password .password-requirements .is-unmet')).toHaveCount(5)
  95. await newPassword.fill('Updated-Password-2')
  96. await expect(page.locator('.profile-password .password-requirements .is-met')).toHaveCount(5)
  97. await confirmPassword.fill('Updated-Password-2')
  98. const passwordToggles = page.locator('.profile-password .el-input__password')
  99. await expect(passwordToggles).toHaveCount(3)
  100. await expect(passwordToggles.locator('svg')).toHaveCount(3)
  101. await passwordToggles.first().click()
  102. await expect(currentPassword).toHaveAttribute('type', 'text')
  103. await passwordToggles.first().click()
  104. await expect(currentPassword).toHaveAttribute('type', 'password')
  105. await page.getByRole('button', { name: '修改密码' }).click()
  106. await expect.poll(() => passwordRequest).toEqual({
  107. oldPassword: 'Current-Password-1',
  108. newPassword: 'Updated-Password-2',
  109. })
  110. await expect(page.getByText('密码修改成功', { exact: true })).toBeVisible()
  111. await expect(currentPassword).toHaveValue('')
  112. await expect(newPassword).toHaveValue('')
  113. await expect(confirmPassword).toHaveValue('')
  114. await captureScreenshot(page, testInfo, 'profile-information-and-password')
  115. await page.setViewportSize({ width: 375, height: 812 })
  116. await expect(descriptions).toBeVisible()
  117. const hasHorizontalOverflow = await page.locator('.profile-page').evaluate((element) => element.scrollWidth > element.clientWidth + 1)
  118. expect(hasHorizontalOverflow).toBe(false)
  119. await captureScreenshot(page, testInfo, 'profile-mobile-descriptions')
  120. })
  121. test('首次强制改密复用相同的五项密码规则', async ({ page }, testInfo) => {
  122. await page.addInitScript(() => {
  123. sessionStorage.setItem('unreal-tran:web:access-token:v1', 'contract-access-token')
  124. })
  125. let mustChangePassword = true
  126. let passwordChanged = false
  127. await page.route('**/api/auth/v1/auth/me', (route) => {
  128. if (passwordChanged) {
  129. return route.fulfill({
  130. status: 403,
  131. contentType: 'application/json',
  132. body: JSON.stringify({
  133. code: 40302,
  134. message: '首次登录必须先修改密码',
  135. data: null,
  136. timestamp: '2026-08-11T10:00:00+08:00',
  137. requestId: 'ute2e-forced-password-stale-me',
  138. }),
  139. })
  140. }
  141. return fulfillJson(route, {
  142. ...profile,
  143. user: { ...profile.user, mustChangePassword },
  144. mustChangePassword,
  145. }, 'ute2e-forced-password-me')
  146. })
  147. await page.route('**/api/auth/v1/menus/navigation', (route) => fulfillJson(route, [], 'ute2e-forced-password-menu'))
  148. let passwordRequest: Record<string, unknown> | null = null
  149. await page.route('**/api/auth/v1/auth/initial-password', async (route) => {
  150. passwordRequest = route.request().postDataJSON() as Record<string, unknown>
  151. mustChangePassword = false
  152. passwordChanged = true
  153. await fulfillJson(route, {
  154. accessToken: 'fresh-access-token',
  155. refreshToken: 'fresh-refresh-token',
  156. tokenType: 'Bearer',
  157. accessExpiresTime: 1786177800,
  158. refreshExpiresTime: 1786780800,
  159. user: {
  160. ...profile,
  161. user: { ...profile.user, mustChangePassword: false },
  162. mustChangePassword: false,
  163. },
  164. }, 'ute2e-forced-password-change')
  165. })
  166. await page.goto('/change-password')
  167. await expect(page.getByRole('heading', { name: '首次登录修改密码' })).toBeVisible()
  168. const inputs = page.locator('.password-card input')
  169. await expect(inputs).toHaveCount(2)
  170. const newPassword = inputs.nth(0)
  171. const confirmation = inputs.nth(1)
  172. const submit = page.getByRole('button', { name: '确认修改并登录' })
  173. await expect(page.getByLabel('当前密码')).toHaveCount(0)
  174. await expect(page.locator('.password-requirements .is-unmet')).toHaveCount(5)
  175. await newPassword.fill('MissingCategories')
  176. await confirmation.fill('MissingCategories')
  177. await expect(submit).toBeDisabled()
  178. await newPassword.fill('Forced#Change8')
  179. await confirmation.fill('Forced#Change8')
  180. await expect(page.locator('.password-requirements .is-met')).toHaveCount(5)
  181. await expect(submit).toBeEnabled()
  182. await captureScreenshot(page, testInfo, 'forced-password-requirements')
  183. await submit.click()
  184. await expect.poll(() => passwordRequest).toEqual({
  185. newPassword: 'Forced#Change8',
  186. })
  187. await expect(page).toHaveURL(/\/dashboard/)
  188. await expect(page.getByText('首次登录必须先修改密码')).toHaveCount(0)
  189. })
  190. })