test: follow live title bounds in performance flow

This commit is contained in:
2026-07-29 13:35:57 +09:00
parent da7897ed77
commit 0392a28a32

View File

@@ -12,7 +12,6 @@ const chromiumLaunchOptions = Object.freeze({
headless: true,
args: ['--use-angle=swiftshader', '--enable-unsafe-swiftshader']
});
const legacyUiScale = 1.5;
const budgets = {
titleReadyMs: readBudget('PERF_BUDGET_TITLE_READY_MS', 30000),
storyReadyFromNewGameMs: readBudget('PERF_BUDGET_STORY_READY_MS', 30000),
@@ -28,8 +27,61 @@ const budgets = {
largestJsKB: readBudget('PERF_BUDGET_LARGEST_JS_KB', 2500)
};
async function clickLegacyUi(page, x, y, options) {
await page.mouse.click(x * legacyUiScale, y * legacyUiScale, options);
async function clickTitleNewGame(page) {
const clickPoint = await page.evaluate(() => {
const scene = window.__HEROS_GAME__?.scene.getScene('TitleScene');
const state = scene?.getDebugState?.();
const item = state?.focus?.scope === 'main'
? state.focus.items.find((candidate) => candidate.id === 'new-game' && candidate.enabled === true)
: undefined;
const canvas = document.querySelector('canvas');
const canvasBounds = canvas?.getBoundingClientRect();
if (!scene || !item?.bounds || !canvas || !canvasBounds) {
return null;
}
return {
x: canvasBounds.left + (item.bounds.x + item.bounds.width / 2) * canvasBounds.width / scene.scale.width,
y: canvasBounds.top + (item.bounds.y + item.bounds.height / 2) * canvasBounds.height / scene.scale.height,
itemBounds: item.bounds,
canvasBounds: {
x: canvasBounds.x,
y: canvasBounds.y,
width: canvasBounds.width,
height: canvasBounds.height
}
};
});
assert(
clickPoint &&
Number.isFinite(clickPoint.x) &&
Number.isFinite(clickPoint.y) &&
clickPoint.itemBounds.width > 0 &&
clickPoint.itemBounds.height > 0 &&
clickPoint.x >= clickPoint.canvasBounds.x &&
clickPoint.x <= clickPoint.canvasBounds.x + clickPoint.canvasBounds.width &&
clickPoint.y >= clickPoint.canvasBounds.y &&
clickPoint.y <= clickPoint.canvasBounds.y + clickPoint.canvasBounds.height,
`Expected an enabled title new-game control: ${JSON.stringify(clickPoint)}`
);
const clickStartedAt = Date.now();
await page.mouse.click(clickPoint.x, clickPoint.y);
try {
await page.waitForFunction(() => {
const activeScenes = window.__HEROS_DEBUG__?.activeScenes() ?? [];
const title = window.__HEROS_DEBUG__?.title?.();
return title?.navigating === true || activeScenes.includes('StoryScene');
}, undefined, { timeout: 10000 });
} catch (error) {
const diagnostic = await page.evaluate(() => ({
activeScenes: window.__HEROS_DEBUG__?.activeScenes() ?? [],
title: window.__HEROS_DEBUG__?.title?.() ?? null
}));
throw new Error(
`The live title new-game control did not begin navigation: ${JSON.stringify({ clickPoint, diagnostic })}`,
{ cause: error }
);
}
return clickStartedAt;
}
async function clickBattleDeploymentStart(page) {
@@ -92,8 +144,7 @@ try {
const titleMs = Date.now() - titleStartedAt;
const titleEntries = await resourceEntries(page);
const storyStartedAt = Date.now();
await clickLegacyUi(page, 962, 240);
const storyStartedAt = await clickTitleNewGame(page);
await waitForStoryReady(page);
await page.waitForLoadState('networkidle', { timeout: 120000 });
const storyMs = Date.now() - storyStartedAt;
@@ -313,11 +364,23 @@ async function waitForTitle(page) {
}
async function waitForStoryReady(page) {
await page.waitForFunction(() => {
const activeScenes = window.__HEROS_DEBUG__?.activeScenes() ?? [];
const story = window.__HEROS_DEBUG__?.scene('StoryScene')?.getDebugState?.();
return activeScenes.includes('StoryScene') && story?.ready === true;
}, undefined, { timeout: 120000 });
try {
await page.waitForFunction(() => {
const activeScenes = window.__HEROS_DEBUG__?.activeScenes() ?? [];
const story = window.__HEROS_DEBUG__?.scene('StoryScene')?.getDebugState?.();
return activeScenes.includes('StoryScene') && story?.ready === true;
}, undefined, { timeout: 120000 });
} catch (error) {
const diagnostic = await page.evaluate(() => ({
activeScenes: window.__HEROS_DEBUG__?.activeScenes() ?? [],
title: window.__HEROS_DEBUG__?.title?.() ?? null,
story: window.__HEROS_DEBUG__?.scene('StoryScene')?.getDebugState?.() ?? null
}));
throw new Error(
`StoryScene did not become ready during performance measurement: ${JSON.stringify(diagnostic)}`,
{ cause: error }
);
}
}
async function advanceStoryUntilBattle(page, battleId) {