diff --git a/scripts/measure-performance.mjs b/scripts/measure-performance.mjs index 5e66eac..1123f04 100644 --- a/scripts/measure-performance.mjs +++ b/scripts/measure-performance.mjs @@ -510,25 +510,71 @@ async function waitForBattleReady(page, battleId) { }, battleId, { timeout: 120000 }).then((handle) => handle.jsonValue()); } -async function waitForBattlePlayable(page, battleId) { - return page.waitForFunction((expectedBattleId) => { - try { +async function waitForBattlePlayable(page, battleId, timeout = 30000) { + try { + const handle = await page.waitForFunction((expectedBattleId) => { + try { + const state = window.__HEROS_DEBUG__?.battle(); + return ( + state?.scene === 'BattleScene' && + state?.battleId === expectedBattleId && + state?.phase === 'idle' && + state?.mapBackgroundReady === true && + (state?.combatAssets?.status === 'ready' || state?.combatAssets?.status === 'degraded') && + state?.triggeredBattleEvents?.includes('opening') && + state?.resultVisible === false + ) + ? { phase: state.phase, combatAssets: state.combatAssets } + : false; + } catch { + return false; + } + }, battleId, { timeout }); + return handle.jsonValue(); + } catch (error) { + const diagnostic = await page.evaluate(() => ({ + activeScenes: window.__HEROS_DEBUG__?.activeScenes() ?? [], + battle: window.__HEROS_DEBUG__?.battle() ?? null + })); + throw new Error( + `Battle did not become playable during performance measurement: ${JSON.stringify({ battleId, timeout, diagnostic })}`, + { cause: error } + ); + } +} + +async function waitForCombatAssetFallbackSettlement(page, battleId, kind) { + try { + const handle = await page.waitForFunction((expected) => { const state = window.__HEROS_DEBUG__?.battle(); return ( state?.scene === 'BattleScene' && - state?.battleId === expectedBattleId && + state?.battleId === expected.battleId && state?.phase === 'idle' && state?.mapBackgroundReady === true && - (state?.combatAssets?.status === 'ready' || state?.combatAssets?.status === 'degraded') && - state?.triggeredBattleEvents?.includes('opening') && + state?.combatAssets?.status === 'degraded' && + state.combatAssets.pendingDeploymentConfirmation === false && + state.combatAssets.settlementCount === 1 && state?.resultVisible === false ) - ? { phase: state.phase, combatAssets: state.combatAssets } + ? { + phase: state.phase, + combatAssets: state.combatAssets, + triggeredBattleEvents: state.triggeredBattleEvents + } : false; - } catch { - return false; - } - }, battleId, { timeout: 30000 }).then((handle) => handle.jsonValue()); + }, { battleId }, { timeout: 15000 }); + return handle.jsonValue(); + } catch (error) { + const diagnostic = await page.evaluate(() => ({ + activeScenes: window.__HEROS_DEBUG__?.activeScenes() ?? [], + battle: window.__HEROS_DEBUG__?.battle() ?? null + })); + throw new Error( + `The ${kind} stall watchdog did not settle combat assets and resume deployment: ${JSON.stringify({ battleId, diagnostic })}`, + { cause: error } + ); + } } async function verifyCombatAssetStallFallback(browserInstance, baseUrl, kind) { @@ -572,14 +618,22 @@ async function verifyCombatAssetStallFallback(browserInstance, baseUrl, kind) { ); await clickBattleDeploymentStart(page); - const playable = await waitForBattlePlayable(page, 'first-battle-zhuo-commandery'); + const fallbackSettlement = await waitForCombatAssetFallbackSettlement( + page, + 'first-battle-zhuo-commandery', + kind + ); + const playable = await waitForBattlePlayable(page, 'first-battle-zhuo-commandery', 90000); const settled = await page.evaluate(() => window.__HEROS_DEBUG__?.battle()?.combatAssets ?? null); assert(stalledUrl, `Expected ${kind} stall QA to intercept one deferred asset request.`); assert( + fallbackSettlement.combatAssets?.status === 'degraded' && + fallbackSettlement.combatAssets?.pendingDeploymentConfirmation === false && + fallbackSettlement.combatAssets?.settlementCount === 1 && playable.combatAssets?.status === 'degraded' && settled?.pendingDeploymentConfirmation === false && settled?.settlementCount === 1, - `Expected ${kind} stall watchdog to settle once and resume pending combat: ${JSON.stringify({ playable, settled, stalledUrl })}` + `Expected ${kind} stall watchdog to settle once and resume pending combat: ${JSON.stringify({ fallbackSettlement, playable, settled, stalledUrl })}` ); const fallback = await page.evaluate((assetKind) => {