游戏分发 web E2E 的冷启动假失败改为浏览器级预热
Project CI / AI game creator shell Rust crates (push) Successful in 1m31s
Project CI / AI game creator shell Rust smoke (push) Successful in 1m56s
Project CI / Backend tests (push) Successful in 3m56s
Project CI / Frontend tests (push) Successful in 2m7s
Project CI / Native shell tests (push) Successful in 6m0s
Project CI / AI game creator shell Rust lane 2/2 (push) Successful in 8m34s
Project CI / AI game creator shell web tests (push) Successful in 1m42s
Project CI / AI game creator shell Rust lane 1/2 (push) Successful in 9m40s
Project CI / Repository checks (push) Successful in 2m15s
Project CI / AI game creator shell Rust crates (push) Successful in 1m31s
Project CI / AI game creator shell Rust smoke (push) Successful in 1m56s
Project CI / Backend tests (push) Successful in 3m56s
Project CI / Frontend tests (push) Successful in 2m7s
Project CI / Native shell tests (push) Successful in 6m0s
Project CI / AI game creator shell Rust lane 2/2 (push) Successful in 8m34s
Project CI / AI game creator shell web tests (push) Successful in 1m42s
Project CI / AI game creator shell Rust lane 1/2 (push) Successful in 9m40s
Project CI / Repository checks (push) Successful in 2m15s
- 现象:冷 Vite 下 check:game-distribution-web-e2e 先卡在目录卡片 20 秒等待,重跑又卡在游玩页「开始游戏」15 秒等待;同一时刻公开目录 API 已把该游戏排在第一 - 根因:原先的 fetch 预热只拿到 SPA 外壳(HTML 里没有路由模块),Vite 仍在断言阶段现编译这些模块图 - 修复:改成起浏览器后用一次性 context 真走 /games、/games/detail、/games/play(domcontentloaded + 2 秒 settle,120 秒上限,失败只告警),首个 goto 的 120 秒超时保留 - 复验:重启 dev 栈(冷缓存)→ 24/24 PASS;四条 web E2E(web / publish / publish-recovery / a11y)全部复跑通过 - 文档:里程碑补 2026-09-29 复跑记录,pitfalls 同日条目按实测证据补正
This commit is contained in:
@@ -38,20 +38,33 @@ function check(name, ok, detail = '') {
|
||||
);
|
||||
}
|
||||
|
||||
// Vite dev 是按需编译的:某条路由第一次被访问时现编译模块图,实测 /games 冷启动可以到
|
||||
// 20 秒以上,超过导航默认超时就会被当成"页面打不开"。先预热一遍路由,把冷启动从断言
|
||||
// 语义里摘出去(预热失败不致命:真正导航时还会再试一次,失败信息照旧可见)。
|
||||
async function warmUpWebRoute(url, timeoutMs = 120_000) {
|
||||
const controller = new AbortController();
|
||||
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
||||
// Vite dev 是按需编译的:某条路由的模块图要等**浏览器**真的加载它时才现编译,实测冷启动
|
||||
// 可以超过 20 秒,超过单步断言超时就会被当成"页面打不开"。纯 HTTP 预热只取到 SPA 外壳
|
||||
// (HTML 里没有路由模块),挡不住这类慢——所以预热必须在浏览器里把每条路由真的走一遍,
|
||||
// 让 Vite 先把模块编译好。预热失败不致命:断言阶段的导航还会再试一次,失败信息照旧可见。
|
||||
async function warmUpWebRoutesInBrowser(browser, urls) {
|
||||
const context = await browser.newContext({
|
||||
viewport: { width: 1280, height: 800 },
|
||||
});
|
||||
try {
|
||||
const response = await fetch(url, { signal: controller.signal });
|
||||
await response.arrayBuffer();
|
||||
console.log(`[warmup] ${url} -> ${response.status}`);
|
||||
} catch (error) {
|
||||
console.warn(`[warmup] ${url} 预热失败,导航阶段会再试一次:${error}`);
|
||||
const page = await context.newPage();
|
||||
for (const url of urls) {
|
||||
try {
|
||||
const response = await page.goto(url, {
|
||||
waitUntil: 'domcontentloaded',
|
||||
timeout: 120_000,
|
||||
});
|
||||
// 模块请求是 DOMContentLoaded 之后才陆续完成的,留一点时间让 Vite 编译落缓存。
|
||||
await page.waitForTimeout(2_000);
|
||||
console.log(
|
||||
`[warmup] ${url} -> ${response?.status() ?? 'no-response'}`,
|
||||
);
|
||||
} catch (error) {
|
||||
console.warn(`[warmup] ${url} 预热失败,断言阶段会再试一次:${error}`);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
clearTimeout(timer);
|
||||
await context.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,15 +295,6 @@ async function main() {
|
||||
const title = `网页链路 ${String(stamp).slice(-6)}`;
|
||||
const { gameId } = await publishFixtureGame(stamp, title);
|
||||
|
||||
// 先把这次要用到的三条网页路由预热一遍,避免 dev 冷启动把它们误伤成"打不开"。
|
||||
for (const route of [
|
||||
'/games',
|
||||
`/games/detail?id=${gameId}`,
|
||||
`/games/play?id=${gameId}`,
|
||||
]) {
|
||||
await warmUpWebRoute(`${WEB}${route}`);
|
||||
}
|
||||
|
||||
const { chromium } = await loadPlaywright();
|
||||
const executablePath = (process.env.E2E_CHROMIUM_EXECUTABLE ?? '').trim();
|
||||
const browser = await chromium.launch({
|
||||
@@ -298,6 +302,14 @@ async function main() {
|
||||
...(executablePath ? { executablePath } : {}),
|
||||
});
|
||||
try {
|
||||
// 先把这次要用到的三条网页路由在浏览器里预热一遍,避免 dev 冷启动把它们误伤成"打不开"。
|
||||
await warmUpWebRoutesInBrowser(
|
||||
browser,
|
||||
['/games', `/games/detail?id=${gameId}`, `/games/play?id=${gameId}`].map(
|
||||
(route) => `${WEB}${route}`,
|
||||
),
|
||||
);
|
||||
|
||||
const context = await browser.newContext({
|
||||
viewport: { width: 1280, height: 800 },
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user