library-picturebook-activity/lesingle-creation-frontend/e2e/fixtures/auth.fixture.ts

124 lines
3.3 KiB
TypeScript
Raw Normal View History

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 }