50c59ca9d4
Co-Authored-By: Claude Code <noreply@anthropic.com>
86 lines
4.3 KiB
TypeScript
86 lines
4.3 KiB
TypeScript
// 登录/会话 e2e:真实后端(8080)+ admin dev(5176,/v1 代理)。
|
||
// 覆盖:路由守卫重定向、错误密码本地化报错、正确登录落到相册管理 stub、
|
||
// 令牌跨刷新持久(pinia persist → localStorage)、用户菜单登出(尽力吊销会话)。
|
||
// 注意:后端登录限速 15 分钟窗口 5 次失败——本文件只做一次错误密码尝试,
|
||
// 短时间内反复全量重跑可能触发 AUTH_RATE_LIMITED(属预期行为,等待窗口过去即可)。
|
||
import { expect, test } from '@playwright/test'
|
||
import { adminCredentials } from './helpers'
|
||
|
||
test.beforeEach(async ({ page }) => {
|
||
// 每个用例从干净会话开始(pinia persist 落在 localStorage)
|
||
await page.goto('/')
|
||
await page.evaluate(() => localStorage.clear())
|
||
})
|
||
|
||
test('未登录访问受保护路由 → 重定向登录页并携带 redirect', async ({ page }) => {
|
||
await page.goto('/#/albums/manage')
|
||
await expect(page.getByText('欢迎回来')).toBeVisible()
|
||
await expect(page).toHaveURL(/\/auth\/login\?redirect=/)
|
||
})
|
||
|
||
test('错误密码 → 本地化错误提示(AUTH_INVALID_CREDENTIALS)', async ({ page }) => {
|
||
const { account } = adminCredentials()
|
||
await page.goto('/#/auth/login')
|
||
await page.getByPlaceholder('请输入账号').fill(account)
|
||
await page.getByPlaceholder('请输入密码', { exact: false }).fill('wrong-password-e2e')
|
||
await page.getByRole('button', { name: '登录' }).click()
|
||
await expect(page.getByText('账号或密码错误')).toBeVisible()
|
||
// 仍停留在登录页
|
||
await expect(page).toHaveURL(/\/auth\/login/)
|
||
})
|
||
|
||
test('正确登录 → 落到相册管理列表页', async ({ page }) => {
|
||
const { account, password } = adminCredentials()
|
||
await page.goto('/#/auth/login')
|
||
await page.getByPlaceholder('请输入账号').fill(account)
|
||
await page.getByPlaceholder('请输入密码', { exact: false }).fill(password)
|
||
await page.getByRole('button', { name: '登录' }).click()
|
||
|
||
// 首页 '/' 重定向到菜单第一个有效路径(/albums/manage)
|
||
await expect(page).toHaveURL(/\/albums\/manage/)
|
||
await expect(page.getByText('相册管理').first()).toBeVisible()
|
||
await expect(page.getByRole('button', { name: '新建相册' })).toBeVisible()
|
||
})
|
||
|
||
test('登录后刷新页面 → 会话令牌持久,受保护路由仍可达', async ({ page }) => {
|
||
const { account, password } = adminCredentials()
|
||
await page.goto('/#/auth/login')
|
||
await page.getByPlaceholder('请输入账号').fill(account)
|
||
await page.getByPlaceholder('请输入密码', { exact: false }).fill(password)
|
||
await page.getByRole('button', { name: '登录' }).click()
|
||
await expect(page).toHaveURL(/\/albums\/manage/)
|
||
|
||
// 会话令牌已持久化(pinia-plugin-persistedstate → localStorage,键含版本前缀)
|
||
const storage = await page.evaluate(() => JSON.stringify(localStorage))
|
||
expect(storage).toContain('sessionToken')
|
||
|
||
// 刷新后不弹回登录页:动态路由重建,受保护页仍加载
|
||
await page.reload()
|
||
await expect(page).toHaveURL(/\/albums\/manage/)
|
||
await expect(page.getByRole('button', { name: '新建相册' })).toBeVisible()
|
||
})
|
||
|
||
test('用户菜单登出 → 尽力吊销会话并回到登录页', async ({ page }) => {
|
||
const { account, password } = adminCredentials()
|
||
await page.goto('/#/auth/login')
|
||
await page.getByPlaceholder('请输入账号').fill(account)
|
||
await page.getByPlaceholder('请输入密码', { exact: false }).fill(password)
|
||
await page.getByRole('button', { name: '登录' }).click()
|
||
await expect(page).toHaveURL(/\/albums\/manage/)
|
||
|
||
// 头像 hover 弹出用户菜单 → 退出登录 → 确认
|
||
await page.getByAltText('avatar').hover()
|
||
await page.getByText('退出登录').click()
|
||
await page.getByRole('button', { name: '确定' }).click()
|
||
|
||
// 用 heading 断言:页面还残留登录成功的 ElNotification(「欢迎回来, ...」文案)
|
||
await expect(page.getByRole('heading', { name: '欢迎回来' })).toBeVisible({ timeout: 10_000 })
|
||
await expect(page).toHaveURL(/\/auth\/login/)
|
||
// 本地会话已清空(user store 持久化键以 -user 结尾;字段保留但令牌为空串)
|
||
const storedToken = await page.evaluate(() => {
|
||
const entry = Object.entries(localStorage).find(([key]) => key.endsWith('-user'))
|
||
return entry ? String(JSON.parse(entry[1]).sessionToken ?? '') : '__missing__'
|
||
})
|
||
expect(storedToken).toBe('')
|
||
})
|