fix: prevent camp save click-through

This commit is contained in:
2026-07-29 11:42:11 +09:00
parent 289599149b
commit 4321ba591e
3 changed files with 152 additions and 41 deletions

View File

@@ -42,9 +42,12 @@ const expectedBattleId = 'second-battle-yellow-turban-pursuit';
let serverProcess;
let browser;
const pageErrors = [];
let delayedBattleModuleRequests = 0;
try {
serverProcess = await ensureLocalServer(targetUrl);
await prewarmBattleSceneModule(targetUrl);
browser = await chromium.launch({
headless:
process.env.VERIFY_INTERACTION_UX_HEADLESS !== '0',
@@ -60,10 +63,8 @@ try {
const page = await context.newPage();
page.setDefaultTimeout(30000);
const pageErrors = [];
page.on('pageerror', (error) => pageErrors.push(error.stack ?? error.message));
let delayedBattleModuleRequests = 0;
await page.route('**/src/game/scenes/BattleScene.ts*', async (route) => {
delayedBattleModuleRequests += 1;
await delay(1200);
@@ -275,11 +276,49 @@ async function verifyCampModalAndNavigation(page) {
})}`
);
await page.waitForFunction(() => {
const scenes = window.__HEROS_DEBUG__?.activeScenes() ?? [];
const battle = window.__HEROS_DEBUG__?.battle();
return scenes.includes('BattleScene') && battle?.scene === 'BattleScene';
}, undefined, { timeout: 90000 });
try {
await page.waitForFunction(() => {
const scenes = window.__HEROS_DEBUG__?.activeScenes() ?? [];
const battle = window.__HEROS_DEBUG__?.battle();
return scenes.includes('BattleScene') && battle?.scene === 'BattleScene';
}, undefined, { timeout: 90000 });
} catch (error) {
const diagnostic = await page.evaluate(() => {
const scene = window.__HEROS_GAME__?.scene.getScene('CampScene');
const camp = window.__HEROS_DEBUG__?.camp();
return {
activeScenes: window.__HEROS_DEBUG__?.activeScenes() ?? [],
camp: camp
? {
campaignStep: camp.campaign?.step ?? null,
sortiePrepStep: camp.sortiePrepStep ?? null,
sortiePrimaryAction: camp.sortiePrimaryAction?.kind ?? null,
selectedSortieUnitIds: camp.selectedSortieUnitIds ?? [],
nextSortieBattleId: camp.nextSortieBattleId ?? null,
skipIntroStoryForSortie:
camp.skipIntroStoryForSortie ?? null
}
: null,
navigationPending: scene?.navigationPending ?? null,
navigationBlockerCount: scene?.navigationBlockerObjects?.length ?? null,
saveSlotObjectCount: scene?.saveSlotObjects?.length ?? null,
saveConfirmObjectCount: scene?.saveSlotConfirmObjects?.length ?? null,
sceneKeys: Object.keys(window.__HEROS_GAME__?.scene.keys ?? {}),
resources: performance
.getEntriesByType('resource')
.map((entry) => entry.name)
.filter((name) => name.includes('BattleScene'))
};
});
throw new Error(
`BattleScene did not become active after camp navigation: ${JSON.stringify({
diagnostic,
delayedBattleModuleRequests,
pageErrors
})}`,
{ cause: error }
);
}
const navigationCompleteState = await page.evaluate(() => ({
battleSceneStarts: window.__HEROS_INTERACTION_UX__?.battleSceneStarts ?? 0,
activeScenes: window.__HEROS_DEBUG__?.activeScenes() ?? []
@@ -2260,6 +2299,23 @@ async function canReach(url) {
}
}
async function prewarmBattleSceneModule(url) {
const parsed = new URL(url);
const moduleUrl = new URL(
'/heros_web/src/game/scenes/BattleScene.ts',
parsed.origin
);
const response = await fetch(moduleUrl, {
signal: AbortSignal.timeout(30000)
});
if (!response.ok) {
throw new Error(
`Could not prewarm the BattleScene module (${response.status}) at ${moduleUrl}.`
);
}
await response.arrayBuffer();
}
function delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}