library-picturebook-activity/frontend/e2e/fixtures/auth.fixture.ts
En 922f650365 feat: 添加乐读派(leai)集成模块及E2E测试基础设施
后端:
- 新增 leai 模块:认证、Webhook、数据同步、定时对账
- 新增 LeaiConfig/RestTemplateConfig/SchedulingConfig 配置
- 新增 FlywayRepairConfig 处理迁移修复
- 新增 V5__leai_integration.sql 迁移脚本
- 扩展所有实体类添加 tenantId 等字段
- 更新 SecurityConfig 放行 leai 公开接口
- 添加 application-test.yml 测试环境配置

前端:
- 添加乐读派认证 API (public.ts)
- 优化 Generating.vue 生成页
- 添加 Playwright E2E 测试配置及依赖
- 添加测试 fixtures、utils、mock-h5.html
- 添加 leai 模块完整 E2E 测试套件

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-07 21:52:32 +08:00

124 lines
3.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.

import { test as base, expect, request as requestFactory, type Page, type APIRequestContext } from '@playwright/test'
/**
* 认证 Fixture
* 提供已登录的浏览器上下文和 JWT Token
*/
/** 测试账户(通过环境变量覆盖) */
export const AUTH_CONFIG = {
username: process.env.TEST_USERNAME || 'demo',
password: process.env.TEST_PASSWORD || 'demo123456',
tenantCode: process.env.TEST_TENANT_CODE || 'gdlib',
/** 后端 API 地址 */
apiBase: process.env.API_BASE_URL || 'http://localhost:8580/api',
/** 登录接口路径 */
loginPath: '/public/auth/login',
}
/** 登录页路径 */
export function loginPath() {
return '/p/login'
}
/** 创作页路径 */
export function createPath() {
return '/p/create'
}
/** 作品列表路径 */
export function worksPath() {
return '/p/works'
}
/**
* 通过 API 直接获取 JWT Token绕过 UI 登录)
*/
export async function fetchJwtToken(request: APIRequestContext): Promise<string> {
const resp = await request.post(`${AUTH_CONFIG.apiBase}${AUTH_CONFIG.loginPath}`, {
data: {
username: AUTH_CONFIG.username,
password: AUTH_CONFIG.password,
tenantCode: AUTH_CONFIG.tenantCode,
},
})
const json = await resp.json()
if (json.code !== 200 || !json.data?.token) {
throw new Error(`登录API失败: ${JSON.stringify(json)}`)
}
return json.data.token as string
}
/**
* 在浏览器页面中执行 UI 登录
*/
export async function doLogin(page: Page): Promise<Page> {
await page.goto(loginPath())
// 等待登录表单渲染
await page.waitForSelector('input[type="password"]', { timeout: 10_000 })
// 填写用户名(尝试多种选择器兼容不同 UI
const usernameSelectors = [
'input[placeholder*="用户名"]',
'input[placeholder*="账号"]',
'input#username',
'input[name="username"]',
'input:not([type="password"]):not([type="hidden"])',
]
for (const sel of usernameSelectors) {
const input = page.locator(sel).first()
if (await input.count() > 0 && await input.isVisible()) {
await input.fill(AUTH_CONFIG.username)
break
}
}
// 填写密码
await page.locator('input[type="password"]').first().fill(AUTH_CONFIG.password)
// 点击登录按钮
const loginBtn = page.locator('button[type="submit"], button:has-text("登录"), button:has-text("登 录")').first()
await loginBtn.click()
// 等待跳转离开登录页
await page.waitForURL((url) => !url.pathname.includes('/login'), { timeout: 15_000 })
return page
}
/**
* 扩展 Playwright test fixture
*/
type AuthFixtures = {
/** 已登录的页面(浏览器模式) */
loggedInPage: Page
/** JWT token 字符串 */
authToken: string
/** 带 token 的 API 请求上下文 */
authedApi: APIRequestContext
}
export const test = base.extend<AuthFixtures>({
loggedInPage: async ({ page }, use) => {
await doLogin(page)
await use(page)
},
authToken: async ({ request }, use) => {
const token = await fetchJwtToken(request)
await use(token)
},
authedApi: async ({ request }, use) => {
const token = await fetchJwtToken(request)
const context = await requestFactory.newContext({
baseURL: AUTH_CONFIG.apiBase,
extraHTTPHeaders: { Authorization: `Bearer ${token}` },
})
await use(context)
},
})
export { expect }