Files
photo-live/apps/admin/e2e/login.spec.ts
T
2026-09-18 12:33:46 +08:00

86 lines
4.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 登录/会话 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('')
})