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 { 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 { 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({ 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 }