diff --git a/scripts/verify-release-candidate.mjs b/scripts/verify-release-candidate.mjs index 1efc8a8..b090b63 100644 --- a/scripts/verify-release-candidate.mjs +++ b/scripts/verify-release-candidate.mjs @@ -163,6 +163,35 @@ try { `Expected campaign report debug state to retain first battle unlock reward: ${JSON.stringify(firstCampProbe.state?.report?.campaignRewards)}` ); + const firstCampSelectedSortieUnitIds = firstCampProbe.state?.selectedSortieUnitIds ?? []; + const firstCampDeploymentPreview = firstCampProbe.state?.sortieDeploymentPreview ?? []; + await page.mouse.click(1160, 38); + await waitForSortiePrep(page); + await page.screenshot({ path: `${screenshotDir}/rc-first-camp-sortie-prep.png`, fullPage: true }); + await assertCanvasPainted(page, 'first camp sortie prep'); + await page.mouse.click(1116, 656); + await advanceUntilBattle(page, 'second-battle-yellow-turban-pursuit'); + await page.screenshot({ path: `${screenshotDir}/rc-second-battle-from-first-camp.png`, fullPage: true }); + await assertCanvasPainted(page, 'second battle from first camp'); + + const secondBattleProbe = await page.evaluate(() => window.__HEROS_DEBUG__?.battle()); + assert(secondBattleProbe?.battleId === 'second-battle-yellow-turban-pursuit', `Expected first camp sortie to enter second battle: ${JSON.stringify(secondBattleProbe)}`); + assertSameMembers( + secondBattleProbe?.selectedSortieUnitIds, + firstCampSelectedSortieUnitIds, + `Expected second battle to receive first camp selected sortie ids: ${JSON.stringify(secondBattleProbe?.selectedSortieUnitIds)} / ${JSON.stringify(firstCampSelectedSortieUnitIds)}` + ); + assertSameMembers( + secondBattleProbe?.deployedAllyIds, + firstCampSelectedSortieUnitIds, + `Expected second battle deployed allies to match first camp sortie ids: ${JSON.stringify(secondBattleProbe?.deployedAllyIds)} / ${JSON.stringify(firstCampSelectedSortieUnitIds)}` + ); + assertDeploymentMatchesPreview( + secondBattleProbe?.deployedAllyPositions, + firstCampDeploymentPreview, + `Expected second battle ally positions to match first camp deployment preview: ${JSON.stringify(secondBattleProbe?.deployedAllyPositions)} / ${JSON.stringify(firstCampDeploymentPreview)}` + ); + await seedCampaignSave(page, { battleId: 'fifty-eighth-battle-qishan-retreat', battleTitle: '기산 후퇴로', @@ -386,6 +415,14 @@ async function waitForCamp(page) { }, undefined, { timeout: 90000 }); } +async function waitForSortiePrep(page) { + await page.waitForFunction(() => { + const activeScenes = window.__HEROS_DEBUG__?.activeScenes() ?? []; + const camp = window.__HEROS_DEBUG__?.camp?.(); + return activeScenes.includes('CampScene') && camp?.sortieVisible === true; + }, undefined, { timeout: 30000 }); +} + async function waitForCampAfterBattleResult(page) { await page.waitForFunction(() => { const activeScenes = window.__HEROS_DEBUG__?.activeScenes() ?? []; @@ -621,6 +658,26 @@ function assertUnique(values, message) { assert(new Set(values).size === values.length, message); } +function assertSameMembers(values, expected, message) { + assert(Array.isArray(values) && Array.isArray(expected), message); + assert(values.length === expected.length, message); + const valueSet = new Set(values); + assert(expected.every((value) => valueSet.has(value)), message); +} + +function assertDeploymentMatchesPreview(positions, preview, message) { + assert(Array.isArray(positions) && Array.isArray(preview), message); + assert(positions.length === preview.length, message); + const positionsById = new Map(positions.map((position) => [position.id, position])); + assert( + preview.every((slot) => { + const position = positionsById.get(slot.unitId); + return position?.x === slot.x && position?.y === slot.y; + }), + message + ); +} + function runCommand(command, args, env = {}) { const result = spawnSync(command, args, { cwd: process.cwd(), env: { ...process.env, ...env }, stdio: 'inherit' }); if (result.status !== 0) {