| @@ -138,6 +138,52 @@ test('管理员可以登录、进入个人菜单并安全退出', async ({ page | |||||
| await expect(page.getByRole('heading', { name: '登录数字人平台', exact: true })).toBeVisible() | await expect(page.getByRole('heading', { name: '登录数字人平台', exact: true })).toBeVisible() | ||||
| }) | }) | ||||
| test('智能体配置未保存时取消退出应保留完整登录态', async ({ page }) => { | |||||
| await loginAsAdmin(page) | |||||
| await page.goto('/agents/manage') | |||||
| const configure = page.getByRole('button', { name: '配置', exact: true }).first() | |||||
| await expect(configure).toBeVisible() | |||||
| await configure.click() | |||||
| await expect(page).toHaveURL(/\/agents\/[^/?]+(?:\?|$)/) | |||||
| const tabs = page.getByRole('navigation', { name: '配置分区' }) | |||||
| await tabs.getByRole('button', { name: /^身份设置/ }).click() | |||||
| const prompt = page.locator('.detail-body:visible .identity-editor textarea') | |||||
| await expect(prompt).toBeVisible() | |||||
| await prompt.fill(`${await prompt.inputValue()} 临时未保存内容`) | |||||
| await expect(page.getByText('有未保存的修改', { exact: true })).toBeVisible() | |||||
| let logoutRequests = 0 | |||||
| page.on('request', (request) => { | |||||
| if (request.method() === 'POST' && new URL(request.url()).pathname.endsWith('/auth/logout')) logoutRequests += 1 | |||||
| }) | |||||
| const detailUrl = page.url() | |||||
| await page.locator('.user-menu-trigger').click() | |||||
| await page.getByRole('menuitem', { name: /退出登录/ }).click() | |||||
| await confirmMessageBox(page, '退出登录') | |||||
| const unsavedDialog = page.getByRole('dialog', { name: '未保存的修改' }) | |||||
| await expect(unsavedDialog).toBeVisible() | |||||
| await unsavedDialog.getByRole('button', { name: '返回继续编辑' }).click() | |||||
| await expect(page).toHaveURL(detailUrl) | |||||
| await expect(page.locator('.app-shell')).toBeVisible() | |||||
| await expect(page.locator('#main-sidebar')).toBeVisible() | |||||
| await expect(page.locator('.user-menu-trigger')).not.toContainText('当前用户') | |||||
| await expect(page.getByText('有未保存的修改', { exact: true })).toBeVisible() | |||||
| expect(logoutRequests, '取消离开时不应调用注销接口').toBe(0) | |||||
| // 再次退出并明确放弃改动,验证正常退出链路仍然可用。 | |||||
| await page.locator('.user-menu-trigger').click() | |||||
| await page.getByRole('menuitem', { name: /退出登录/ }).click() | |||||
| await confirmMessageBox(page, '退出登录') | |||||
| await page.getByRole('dialog', { name: '未保存的修改' }) | |||||
| .getByRole('button', { name: '放弃修改并离开' }).click() | |||||
| await expect(page).toHaveURL(/\/login(?:\?|$)/) | |||||
| expect(logoutRequests, '确认离开后应且仅应注销一次').toBe(1) | |||||
| }) | |||||
| test('兼容入口和未知地址重定向到正式路由', async ({ page }) => { | test('兼容入口和未知地址重定向到正式路由', async ({ page }) => { | ||||
| await loginAsAdmin(page) | await loginAsAdmin(page) | ||||
| @@ -190,7 +190,7 @@ test('IAM 真实生命周期:部门、角色、用户、授权、首次改密 | |||||
| await page.getByPlaceholder('请输入新密码').fill(data.finalPassword) | await page.getByPlaceholder('请输入新密码').fill(data.finalPassword) | ||||
| await page.getByPlaceholder('请再次输入').fill(data.finalPassword) | await page.getByPlaceholder('请再次输入').fill(data.finalPassword) | ||||
| await captureScreenshot(page, testInfo, '05-initial-password-form', [page.locator('input[type="password"]')]) | await captureScreenshot(page, testInfo, '05-initial-password-form', [page.locator('input[type="password"]')]) | ||||
| await page.getByRole('button', { name: '保存并进入平台' }).click() | |||||
| await page.getByRole('button', { name: '保存新密码并进入平台' }).click() | |||||
| await expect(page).toHaveURL(/\/overview(?:\?|$)/) | await expect(page).toHaveURL(/\/overview(?:\?|$)/) | ||||
| await expect(page.locator('.user-menu-trigger')).toContainText(data.editedDisplayName) | await expect(page.locator('.user-menu-trigger')).toContainText(data.editedDisplayName) | ||||
| await captureScreenshot(page, testInfo, '06-student-login-success') | await captureScreenshot(page, testInfo, '06-student-login-success') | ||||
| @@ -279,7 +279,7 @@ async function completeInitialPasswordInBrowser( | |||||
| response.request().method() === 'PUT' | response.request().method() === 'PUT' | ||||
| && new URL(response.url()).pathname === '/api/auth/v1/auth/initial-password' | && new URL(response.url()).pathname === '/api/auth/v1/auth/initial-password' | ||||
| )) | )) | ||||
| await page.getByRole('button', { name: '保存并进入平台' }).click() | |||||
| await page.getByRole('button', { name: '保存新密码并进入平台' }).click() | |||||
| const response = await changedResponsePromise | const response = await changedResponsePromise | ||||
| assertStatus(response, 200, '浏览器完成首次改密') | assertStatus(response, 200, '浏览器完成首次改密') | ||||
| await expect(page).toHaveURL(/\/overview(?:\?|$)/) | await expect(page).toHaveURL(/\/overview(?:\?|$)/) | ||||
| @@ -112,16 +112,33 @@ test('861:首次改密两次输入不一致时立即显示字段提示且不 | |||||
| await seedSession(page) | await seedSession(page) | ||||
| const mocked = await mockAuth(page, { me: 'forced' }) | const mocked = await mockAuth(page, { me: 'forced' }) | ||||
| await page.goto('/change-password') | await page.goto('/change-password') | ||||
| await expect(page.getByText('首次登录安全验证', { exact: true })).toBeVisible() | |||||
| await expect(page.getByRole('heading', { name: '请先设置新密码' })).toBeVisible() | |||||
| await expect(page.getByText('为保护账号安全,初始密码不可继续使用。此步骤仅需完成一次。')).toBeVisible() | |||||
| const inputs = page.locator('input[autocomplete="new-password"]') | const inputs = page.locator('input[autocomplete="new-password"]') | ||||
| await inputs.nth(0).fill('Strong#Password8') | await inputs.nth(0).fill('Strong#Password8') | ||||
| await inputs.nth(1).fill('Different#Pass9') | await inputs.nth(1).fill('Different#Pass9') | ||||
| await expect(page.locator('.password-requirements .is-passed')).toHaveCount(4) | |||||
| await expect(page.getByText('两次输入的新密码不一致', { exact: true })).toBeVisible() | await expect(page.getByText('两次输入的新密码不一致', { exact: true })).toBeVisible() | ||||
| await expect(page.getByRole('button', { name: '保存并进入平台' })).toBeDisabled() | |||||
| await expect(page.getByRole('button', { name: '保存新密码并进入平台' })).toBeDisabled() | |||||
| expect(mocked.initialPasswordRequests()).toBe(0) | expect(mocked.initialPasswordRequests()).toBe(0) | ||||
| await captureScreenshot(page, testInfo, '861-initial-password-mismatch', [inputs]) | await captureScreenshot(page, testInfo, '861-initial-password-mismatch', [inputs]) | ||||
| }) | }) | ||||
| test('首次登录改密页在 375px 窄屏保持可读且无横向滚动', async ({ page }, testInfo) => { | |||||
| await page.setViewportSize({ width: 375, height: 812 }) | |||||
| await seedSession(page) | |||||
| await mockAuth(page, { me: 'forced' }) | |||||
| await page.goto('/change-password') | |||||
| await expect(page.getByRole('heading', { name: '请先设置新密码' })).toBeVisible() | |||||
| await expect(page.getByText('首次登录 · 安全设置', { exact: true })).toBeVisible() | |||||
| const horizontalOverflow = await page.evaluate(() => document.documentElement.scrollWidth - window.innerWidth) | |||||
| expect(horizontalOverflow).toBeLessThanOrEqual(1) | |||||
| await captureScreenshot(page, testInfo, 'initial-password-mobile-375', [page.locator('input[autocomplete="new-password"]')]) | |||||
| }) | |||||
| test('864:首次改密期间账号停用后仅显示一次固定登录提示且不刷新令牌', async ({ page }, testInfo) => { | test('864:首次改密期间账号停用后仅显示一次固定登录提示且不刷新令牌', async ({ page }, testInfo) => { | ||||
| await seedSession(page, true) | await seedSession(page, true) | ||||
| const mocked = await mockAuth(page, { me: 'forced', initialPassword401: true }) | const mocked = await mockAuth(page, { me: 'forced', initialPassword401: true }) | ||||
| @@ -129,7 +146,7 @@ test('864:首次改密期间账号停用后仅显示一次固定登录提示 | |||||
| const inputs = page.locator('input[autocomplete="new-password"]') | const inputs = page.locator('input[autocomplete="new-password"]') | ||||
| await inputs.nth(0).fill('Strong#Password8') | await inputs.nth(0).fill('Strong#Password8') | ||||
| await inputs.nth(1).fill('Strong#Password8') | await inputs.nth(1).fill('Strong#Password8') | ||||
| await page.getByRole('button', { name: '保存并进入平台' }).click() | |||||
| await page.getByRole('button', { name: '保存新密码并进入平台' }).click() | |||||
| await expect(page).toHaveURL(/\/login\?redirect=/) | await expect(page).toHaveURL(/\/login\?redirect=/) | ||||
| await expect(page.locator('#login-error')).toHaveText('账号、密码或登录身份不匹配') | await expect(page.locator('#login-error')).toHaveText('账号、密码或登录身份不匹配') | ||||