import type { Page, Route } from '@playwright/test' import { captureScreenshot, expect, test } from './fixtures' const profile = { user: { id: '1', username: 'admin', displayName: '系统管理员', departmentId: '10', departmentName: '信息中心', mustChangePassword: false, version: 3, }, activeRoleId: '100', activeRole: { id: '100', code: 'admin', name: '管理员' }, roles: [ { id: '100', code: 'admin', name: '管理员', shortName: '管', status: 1, builtIn: 1, isSuperAdmin: 1, dataScopeCode: 'ALL', }, ], permissions: ['dashboard.view'], authorizationMode: 'SINGLE_ACTIVE', loginTime: 1786387200, } as const const envelope = (data: unknown, requestId: string) => JSON.stringify({ code: 200, message: '成功', data, timestamp: '2026-08-11T10:00:00+08:00', requestId, }) const fulfillJson = (route: Route, data: unknown, requestId: string, status = 200) => route.fulfill({ status, contentType: 'application/json', body: envelope(data, requestId), }) const prepareAuthenticatedProfile = async (page: Page) => { await page.addInitScript(() => { sessionStorage.setItem('unreal-tran:web:access-token:v1', 'contract-access-token') }) await page.route('**/api/auth/v1/auth/me', (route) => fulfillJson(route, profile, 'ute2e-profile-me')) await page.route('**/api/auth/v1/menus/navigation', (route) => fulfillJson(route, [], 'ute2e-profile-menu')) } test.describe('平台账号与个人中心', () => { test('页头精简且账号菜单仅保留个人中心和退出登录', async ({ page }, testInfo) => { await prepareAuthenticatedProfile(page) await page.goto('/profile') const header = page.locator('.platform-header') await expect(header).toBeVisible() expect((await header.boundingBox())?.height).toBeLessThanOrEqual(62) await expect(header).not.toContainText('Maintenance Digital Workshop') await page.getByRole('button', { name: '账号菜单:系统管理员' }).click() const dropdown = page.locator('.user-dropdown:visible') await expect(dropdown).toBeVisible() await expect(dropdown.locator('.el-dropdown-menu__item')).toHaveCount(2) await expect(dropdown.getByText('个人中心', { exact: true })).toBeVisible() await expect(dropdown.getByText('退出登录', { exact: true })).toBeVisible() await expect(dropdown).not.toContainText('修改密码') await expect(dropdown).not.toContainText('角色切换') await page.keyboard.press('Escape') await captureScreenshot(page, testInfo, 'profile-header-account-menu') }) test('个人中心展示真实资料并通过接口修改密码', async ({ page }, testInfo) => { await prepareAuthenticatedProfile(page) let passwordRequest: Record | null = null await page.route('**/api/auth/v1/auth/password', async (route) => { passwordRequest = route.request().postDataJSON() as Record await fulfillJson(route, { reauthenticationRequired: false }, 'ute2e-profile-password') }) await page.goto('/profile') await expect(page.getByRole('heading', { name: '个人中心' })).toBeVisible() await expect(page.getByText('系统管理员 · admin', { exact: true })).toBeVisible() const descriptions = page.locator('.profile-descriptions') await expect(descriptions).toHaveClass(/el-descriptions/) await expect(descriptions.locator('.el-descriptions__table')).toBeVisible() await expect(descriptions).toContainText('信息中心') await expect(descriptions).toContainText('管理员') const columnWidths = await page.locator('.profile-grid > .system-panel').evaluateAll((panels) => panels.map((panel) => panel.getBoundingClientRect().width)) expect(columnWidths).toHaveLength(2) expect(Math.abs(columnWidths[0]! - columnWidths[1]!)).toBeLessThanOrEqual(1) const currentPassword = page.locator('input[name="currentPassword"]') const newPassword = page.locator('input[name="newPassword"]') const confirmPassword = page.locator('input[name="confirmPassword"]') await expect(currentPassword).toHaveAttribute('placeholder', '请输入当前密码') await expect(newPassword).toHaveAttribute('placeholder', '请输入符合全部规则的新密码') await expect(confirmPassword).toHaveAttribute('placeholder', '请再次输入新密码') await currentPassword.fill('Current-Password-1') await expect(page.locator('.profile-password .password-requirements .is-unmet')).toHaveCount(5) await newPassword.fill('Updated-Password-2') await expect(page.locator('.profile-password .password-requirements .is-met')).toHaveCount(5) await confirmPassword.fill('Updated-Password-2') const passwordToggles = page.locator('.profile-password .el-input__password') await expect(passwordToggles).toHaveCount(3) await expect(passwordToggles.locator('svg')).toHaveCount(3) await passwordToggles.first().click() await expect(currentPassword).toHaveAttribute('type', 'text') await passwordToggles.first().click() await expect(currentPassword).toHaveAttribute('type', 'password') await page.getByRole('button', { name: '修改密码' }).click() await expect.poll(() => passwordRequest).toEqual({ oldPassword: 'Current-Password-1', newPassword: 'Updated-Password-2', }) await expect(page.getByText('密码修改成功', { exact: true })).toBeVisible() await expect(currentPassword).toHaveValue('') await expect(newPassword).toHaveValue('') await expect(confirmPassword).toHaveValue('') await captureScreenshot(page, testInfo, 'profile-information-and-password') await page.setViewportSize({ width: 375, height: 812 }) await expect(descriptions).toBeVisible() const hasHorizontalOverflow = await page.locator('.profile-page').evaluate((element) => element.scrollWidth > element.clientWidth + 1) expect(hasHorizontalOverflow).toBe(false) await captureScreenshot(page, testInfo, 'profile-mobile-descriptions') }) test('首次强制改密复用相同的五项密码规则', async ({ page }, testInfo) => { await page.addInitScript(() => { sessionStorage.setItem('unreal-tran:web:access-token:v1', 'contract-access-token') }) let mustChangePassword = true let passwordChanged = false await page.route('**/api/auth/v1/auth/me', (route) => { if (passwordChanged) { return route.fulfill({ status: 403, contentType: 'application/json', body: JSON.stringify({ code: 40302, message: '首次登录必须先修改密码', data: null, timestamp: '2026-08-11T10:00:00+08:00', requestId: 'ute2e-forced-password-stale-me', }), }) } return fulfillJson(route, { ...profile, user: { ...profile.user, mustChangePassword }, mustChangePassword, }, 'ute2e-forced-password-me') }) await page.route('**/api/auth/v1/menus/navigation', (route) => fulfillJson(route, [], 'ute2e-forced-password-menu')) let passwordRequest: Record | null = null await page.route('**/api/auth/v1/auth/initial-password', async (route) => { passwordRequest = route.request().postDataJSON() as Record mustChangePassword = false passwordChanged = true await fulfillJson(route, { accessToken: 'fresh-access-token', refreshToken: 'fresh-refresh-token', tokenType: 'Bearer', accessExpiresTime: 1786177800, refreshExpiresTime: 1786780800, user: { ...profile, user: { ...profile.user, mustChangePassword: false }, mustChangePassword: false, }, }, 'ute2e-forced-password-change') }) await page.goto('/change-password') await expect(page.getByRole('heading', { name: '首次登录修改密码' })).toBeVisible() const inputs = page.locator('.password-card input') await expect(inputs).toHaveCount(2) const newPassword = inputs.nth(0) const confirmation = inputs.nth(1) const submit = page.getByRole('button', { name: '确认修改并登录' }) await expect(page.getByLabel('当前密码')).toHaveCount(0) await expect(page.locator('.password-requirements .is-unmet')).toHaveCount(5) await newPassword.fill('MissingCategories') await confirmation.fill('MissingCategories') await expect(submit).toBeDisabled() await newPassword.fill('Forced#Change8') await confirmation.fill('Forced#Change8') await expect(page.locator('.password-requirements .is-met')).toHaveCount(5) await expect(submit).toBeEnabled() await captureScreenshot(page, testInfo, 'forced-password-requirements') await submit.click() await expect.poll(() => passwordRequest).toEqual({ newPassword: 'Forced#Change8', }) await expect(page).toHaveURL(/\/dashboard/) await expect(page.getByText('首次登录必须先修改密码')).toHaveCount(0) }) })