chore: 初始化文档站仓库

This commit is contained in:
2026-09-21 13:38:20 +08:00
commit a4ae7ac979
58 changed files with 4622 additions and 0 deletions
+111
View File
@@ -0,0 +1,111 @@
// 浏览器自动化公共模块(Playwright + Chromium)与 HTTP 公共方法(axios)。
import axios from 'axios'
import { chromium, type Browser, type BrowserContext, type Page } from 'playwright-core'
import { API_URL, BASE_URL, CHROMIUM_BIN, PASSWORD, USERNAME } from './config.js'
/** 默认视口尺寸 */
export const DEFAULT_VIEWPORT = { width: 1440, height: 1000 }
/**
* 启动 headless Chromium(复用 Playwright 缓存的浏览器)。
*
* @returns Browser 实例
*/
export async function launchBrowser(): Promise<Browser> {
return chromium.launch({
executablePath: CHROMIUM_BIN,
headless: true,
args: ['--no-sandbox', '--disable-dev-shm-usage'],
})
}
/**
* 创建浏览器上下文。
*
* @param browser Browser 实例
* @param viewport 视口尺寸
* @returns BrowserContext 实例
*/
export async function newContext(
browser: Browser,
viewport: { width: number; height: number } = DEFAULT_VIEWPORT,
): Promise<BrowserContext> {
return browser.newContext({ viewport })
}
/**
* 在上下文中注入登录态与中文语言偏好(对之后打开的页面生效)。
*
* @param context BrowserContext 实例
* @param token 访问令牌
*/
export async function injectSession(context: BrowserContext, token: string): Promise<void> {
await context.addInitScript(
([t]) => {
localStorage.setItem('token', t as string)
localStorage.setItem('locale', 'zh-CN')
},
[token],
)
}
/**
* 等待页面渲染完成:先等文档就绪,再等关键元素可见,最后做短暂稳定等待。
*
* @param page Page 实例
* @param options.selector 关键元素选择器(可选)
* @param options.settle 元素出现后的稳定等待毫秒数,默认 600ms
*/
export async function waitPage(
page: Page,
options: { selector?: string; settle?: number } = {},
): Promise<void> {
await page.waitForLoadState('domcontentloaded').catch(() => undefined)
if (options.selector) {
await page
.locator(options.selector)
.first()
.waitFor({ state: 'visible', timeout: 20000 })
.catch(() => undefined)
}
// 等待异步数据请求基本完成后再截图,避免表格/图表空白;
// SSE 页面(运行日志)不会进入空闲,超时忽略即可。
await page.waitForLoadState('networkidle', { timeout: 8000 }).catch(() => undefined)
await page.waitForTimeout(options.settle ?? 900)
}
/**
* 按可见文本点击按钮。
*
* @param page Page 实例
* @param text 按钮文本
* @returns 是否点击成功
*/
export async function clickText(page: Page, text: string): Promise<boolean> {
const button = page.locator('button', { hasText: text }).first()
try {
// 按钮可能随异步配置(如 AI 启用状态)延迟渲染,等待其出现
await button.waitFor({ state: 'visible', timeout: 15000 })
} catch {
return false
}
// 部分 Naive UI 按钮对 Playwright 合成鼠标事件不响应,改用 DOM 原生 click
await button.evaluate((el) => (el as HTMLElement).click())
return true
}
/**
* 登录测试账号并返回 JWT。
*
* @returns 访问令牌
*/
export async function login(): Promise<string> {
const res = await axios.post(`${API_URL}/user/login`, { username: USERNAME, password: PASSWORD })
return res.data.data.token
}
/** 打开首页并等待(用于非截图场景的简单访问) */
export async function openHome(page: Page): Promise<void> {
await page.goto(BASE_URL)
await page.waitForTimeout(1500)
}