feat: make third-camp preparation choices explicit
This commit is contained in:
@@ -28,7 +28,7 @@ const priorityFixtures = [
|
||||
},
|
||||
{
|
||||
id: 'equipment',
|
||||
label: '장비 정비',
|
||||
label: '선봉 방호',
|
||||
effectKind: 'prepared-equipment-guard',
|
||||
configuredValue: 8
|
||||
},
|
||||
@@ -142,6 +142,10 @@ async function verifyRenderer(browser, baseUrl, rendererFixture) {
|
||||
priority
|
||||
);
|
||||
}
|
||||
await verifyCompanionFormationMismatch(
|
||||
page,
|
||||
rendererFixture.renderer
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
pageErrors,
|
||||
@@ -360,14 +364,15 @@ async function verifyCampPreparationUi(page, renderer) {
|
||||
'equipment'
|
||||
);
|
||||
assert.equal(exploration.visit.completedActivityCount, 1);
|
||||
assert.equal(exploration.visit.selectedPriorityId, 'equipment');
|
||||
assert.equal(exploration.visit.selectedPriorityId, null);
|
||||
assert.equal(exploration.visit.selectionConfirmed, false);
|
||||
assert.equal(
|
||||
exploration.visit.hudActivityTitle,
|
||||
'자유 준비 1 / 3'
|
||||
);
|
||||
assert.equal(
|
||||
exploration.visit.hudPriorityLocation,
|
||||
'장비 정비'
|
||||
'아직 정하지 않음'
|
||||
);
|
||||
assertThirdCampWorldFeedback(exploration, ['equipment']);
|
||||
assert(
|
||||
@@ -383,6 +388,12 @@ async function verifyCampPreparationUi(page, renderer) {
|
||||
return scene?.debugInteractWith?.('camp-exit') ?? false;
|
||||
});
|
||||
assert.equal(partialExit, true);
|
||||
await page.waitForFunction(
|
||||
() =>
|
||||
window.__HEROS_DEBUG__?.campVisitExploration?.()
|
||||
?.dialogue?.active === true
|
||||
);
|
||||
await advanceExplorationDialogue(page);
|
||||
await waitForCamp(page);
|
||||
await page.reload({
|
||||
waitUntil: 'domcontentloaded',
|
||||
@@ -404,7 +415,7 @@ async function verifyCampPreparationUi(page, renderer) {
|
||||
['equipment'],
|
||||
'A partial activity must survive a Camp round trip and page reload.'
|
||||
);
|
||||
assert.equal(exploration.visit.selectedPriorityId, 'equipment');
|
||||
assert.equal(exploration.visit.selectedPriorityId, null);
|
||||
assertThirdCampWorldFeedback(exploration, ['equipment']);
|
||||
|
||||
exploration = await completeThirdCampNpcActivity(
|
||||
@@ -416,7 +427,7 @@ async function verifyCampPreparationUi(page, renderer) {
|
||||
[...exploration.visit.completedActivityIds].sort(),
|
||||
['equipment', 'information']
|
||||
);
|
||||
assert.equal(exploration.visit.selectedPriorityId, 'information');
|
||||
assert.equal(exploration.visit.selectedPriorityId, null);
|
||||
assertThirdCampWorldFeedback(
|
||||
exploration,
|
||||
['equipment', 'information']
|
||||
@@ -468,8 +479,7 @@ async function verifyCampPreparationUi(page, renderer) {
|
||||
const state =
|
||||
window.__HEROS_DEBUG__?.campVisitExploration?.();
|
||||
return (
|
||||
state?.visit?.completedActivityCount === 3 &&
|
||||
state?.visit?.selectedPriorityId === 'companion'
|
||||
state?.visit?.completedActivityCount === 3
|
||||
);
|
||||
}
|
||||
);
|
||||
@@ -485,7 +495,7 @@ async function verifyCampPreparationUi(page, renderer) {
|
||||
);
|
||||
assert.equal(
|
||||
exploration.visit.hudPriorityLocation,
|
||||
'동료 공명'
|
||||
'아직 정하지 않음'
|
||||
);
|
||||
assertThirdCampWorldFeedback(
|
||||
exploration,
|
||||
@@ -544,6 +554,12 @@ async function verifyCampPreparationUi(page, renderer) {
|
||||
return scene?.debugInteractWith?.('camp-exit') ?? false;
|
||||
});
|
||||
assert.equal(completedExit, true);
|
||||
await page.waitForFunction(
|
||||
() =>
|
||||
window.__HEROS_DEBUG__?.campVisitExploration?.()
|
||||
?.dialogue?.active === true
|
||||
);
|
||||
await advanceExplorationDialogue(page);
|
||||
await waitForCamp(page);
|
||||
await page.evaluate(() => {
|
||||
const scene =
|
||||
@@ -570,7 +586,45 @@ async function verifyCampPreparationUi(page, renderer) {
|
||||
priority.id
|
||||
);
|
||||
}
|
||||
assert.equal(camp.thirdCampPreparation.selectedPriorityId, null);
|
||||
assert.equal(camp.thirdCampPreparation.confirmed, false);
|
||||
assert.equal(
|
||||
camp.thirdCampPreparation.keyboardFocusPriorityId,
|
||||
'information'
|
||||
);
|
||||
await page.keyboard.press('ArrowDown');
|
||||
await page.waitForFunction(
|
||||
() =>
|
||||
window.__HEROS_DEBUG__?.camp?.()
|
||||
?.thirdCampPreparation?.keyboardFocusPriorityId ===
|
||||
'equipment'
|
||||
);
|
||||
await page.keyboard.press('Shift+Tab');
|
||||
await page.waitForFunction(
|
||||
() =>
|
||||
window.__HEROS_DEBUG__?.camp?.()
|
||||
?.thirdCampPreparation?.keyboardFocusPriorityId ===
|
||||
'information'
|
||||
);
|
||||
await page.keyboard.press('3');
|
||||
await page.waitForFunction(
|
||||
() => {
|
||||
const preparation =
|
||||
window.__HEROS_DEBUG__?.camp?.()?.thirdCampPreparation;
|
||||
return (
|
||||
preparation?.selectedPriorityId === 'companion' &&
|
||||
preparation.confirmed === true &&
|
||||
preparation.ready === true
|
||||
);
|
||||
}
|
||||
);
|
||||
camp = await readCamp(page);
|
||||
assertPreparationSelection(camp, 'companion');
|
||||
await verifyPreparationModalKeyboardIsolation(page);
|
||||
camp = await verifyPreparationStepKeyboardIsolation(
|
||||
page,
|
||||
'information'
|
||||
);
|
||||
assertFiniteBounds(
|
||||
camp.thirdCampPreparation.clearButtonBounds,
|
||||
'preparation clear button'
|
||||
@@ -616,6 +670,368 @@ async function verifyCampPreparationUi(page, renderer) {
|
||||
null,
|
||||
'cleared optional preparation'
|
||||
);
|
||||
await verifyRetryLockedPreparationCard(page);
|
||||
}
|
||||
|
||||
async function verifyPreparationModalKeyboardIsolation(page) {
|
||||
await page.evaluate(async () => {
|
||||
const campaignModule = await import(
|
||||
'/heros_web/src/game/state/campaignState.ts'
|
||||
);
|
||||
const snapshot = campaignModule.getCampaignState();
|
||||
campaignModule.saveCampaignState(snapshot, 2);
|
||||
campaignModule.saveCampaignState(snapshot, 1);
|
||||
const scene =
|
||||
window.__HEROS_GAME__?.scene.getScene('CampScene');
|
||||
scene.campaign = campaignModule.getCampaignState();
|
||||
scene.showSortiePrep();
|
||||
});
|
||||
await page.waitForFunction(
|
||||
() => {
|
||||
const state = window.__HEROS_DEBUG__?.camp?.();
|
||||
return (
|
||||
state?.campaign?.activeSaveSlot === 1 &&
|
||||
state?.thirdCampPreparation?.browserOpen === true &&
|
||||
state.thirdCampPreparation.selectedPriorityId ===
|
||||
'companion'
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
await openCampSaveSlotPanel(page);
|
||||
await page.keyboard.press('1');
|
||||
await waitForCampSaveModalClosed(page);
|
||||
let camp = await readCamp(page);
|
||||
assertPreparationSelection(camp, 'companion');
|
||||
assert.equal(
|
||||
camp.campaign.activeSaveSlot,
|
||||
1,
|
||||
'The 1 key must save to slot 1 instead of selecting the first preparation card.'
|
||||
);
|
||||
|
||||
await selectPreparationCard(page, 'equipment');
|
||||
await waitForCampSelection(page, 'equipment');
|
||||
await openCampSaveSlotPanel(page);
|
||||
await page.keyboard.press('3');
|
||||
await waitForCampSaveModalClosed(page);
|
||||
camp = await readCamp(page);
|
||||
assertPreparationSelection(camp, 'equipment');
|
||||
assert.equal(
|
||||
camp.campaign.activeSaveSlot,
|
||||
3,
|
||||
'The 3 key must save to slot 3 instead of selecting the third preparation card.'
|
||||
);
|
||||
|
||||
await selectPreparationCard(page, 'information');
|
||||
await waitForCampSelection(page, 'information');
|
||||
await openCampSaveSlotPanel(page);
|
||||
await page.keyboard.press('2');
|
||||
await page.waitForFunction(
|
||||
() => {
|
||||
const scene =
|
||||
window.__HEROS_GAME__?.scene.getScene('CampScene');
|
||||
return (
|
||||
scene?.saveSlotObjects?.length > 0 &&
|
||||
scene?.saveSlotConfirmObjects?.length > 0 &&
|
||||
scene?.pendingSaveSlot === 2
|
||||
);
|
||||
}
|
||||
);
|
||||
camp = await readCamp(page);
|
||||
assertPreparationSelection(camp, 'information');
|
||||
|
||||
await page.keyboard.press('1');
|
||||
await page.keyboard.press('3');
|
||||
const guardedConfirm = await readCampSaveModalState(page);
|
||||
assert.equal(guardedConfirm.slotPanelOpen, true);
|
||||
assert.equal(guardedConfirm.overwriteConfirmOpen, true);
|
||||
assert.equal(guardedConfirm.pendingSaveSlot, 2);
|
||||
assert.equal(
|
||||
guardedConfirm.selectedPriorityId,
|
||||
'information',
|
||||
'Number keys owned by the overwrite confirmation must not leak to hidden preparation cards.'
|
||||
);
|
||||
|
||||
await page.keyboard.press('Enter');
|
||||
await waitForCampSaveModalClosed(page);
|
||||
camp = await readCamp(page);
|
||||
assertPreparationSelection(camp, 'information');
|
||||
assert.equal(
|
||||
camp.campaign.activeSaveSlot,
|
||||
2,
|
||||
'Enter must confirm the pending overwrite instead of confirming the focused preparation card.'
|
||||
);
|
||||
}
|
||||
|
||||
async function verifyPreparationStepKeyboardIsolation(
|
||||
page,
|
||||
selectedPriorityId
|
||||
) {
|
||||
let camp = await readCamp(page);
|
||||
assertPreparationSelection(camp, selectedPriorityId);
|
||||
assert.equal(camp.thirdCampPreparation.browserOpen, true);
|
||||
assert.equal(camp.sortiePrimaryAction?.kind, 'next');
|
||||
assert.equal(
|
||||
camp.sortiePrimaryAction?.nextStep,
|
||||
'formation'
|
||||
);
|
||||
assertFiniteBounds(
|
||||
camp.sortiePrimaryAction?.bounds,
|
||||
'sortie formation action'
|
||||
);
|
||||
await clickSceneBounds(
|
||||
page,
|
||||
'CampScene',
|
||||
camp.sortiePrimaryAction.bounds
|
||||
);
|
||||
await page.waitForFunction(
|
||||
(selectedPriorityId) => {
|
||||
const state = window.__HEROS_DEBUG__?.camp?.();
|
||||
return (
|
||||
state?.sortiePrepStep === 'formation' &&
|
||||
state?.thirdCampPreparation?.browserOpen === false &&
|
||||
state.thirdCampPreparation.selectedPriorityId ===
|
||||
selectedPriorityId
|
||||
);
|
||||
},
|
||||
selectedPriorityId
|
||||
);
|
||||
camp = await readCamp(page);
|
||||
assert(
|
||||
camp.thirdCampPreparation.priorities.every(
|
||||
({ cardBounds, actionButtonBounds }) =>
|
||||
cardBounds === null && actionButtonBounds === null
|
||||
),
|
||||
'Leaving the briefing must destroy every preparation-card input target.'
|
||||
);
|
||||
|
||||
await page.keyboard.press('1');
|
||||
camp = await readCamp(page);
|
||||
assert.equal(camp.sortiePrepStep, 'formation');
|
||||
assert.equal(
|
||||
camp.thirdCampPreparation.selectedPriorityId,
|
||||
selectedPriorityId,
|
||||
'A hidden preparation card must not consume number keys during formation.'
|
||||
);
|
||||
|
||||
await page.keyboard.press('Enter');
|
||||
await page.waitForFunction(
|
||||
(selectedPriorityId) => {
|
||||
const state = window.__HEROS_DEBUG__?.camp?.();
|
||||
return (
|
||||
state?.sortiePrepStep === 'loadout' &&
|
||||
state?.thirdCampPreparation?.browserOpen === false &&
|
||||
state.thirdCampPreparation.selectedPriorityId ===
|
||||
selectedPriorityId
|
||||
);
|
||||
},
|
||||
selectedPriorityId
|
||||
);
|
||||
|
||||
await page.evaluate(() => {
|
||||
const scene =
|
||||
window.__HEROS_GAME__?.scene.getScene('CampScene');
|
||||
scene.setSortiePrepStep('briefing');
|
||||
});
|
||||
await waitForPreparationToggle(page);
|
||||
camp = await openPreparationBrowser(page);
|
||||
assertPreparationSelection(camp, selectedPriorityId);
|
||||
return camp;
|
||||
}
|
||||
|
||||
async function verifyRetryLockedPreparationCard(page) {
|
||||
await seedThirdVictory(page, {
|
||||
acknowledgeActivities: false,
|
||||
selectPriorityId: null
|
||||
});
|
||||
await page.evaluate(async (targetBattleId) => {
|
||||
const campaignModule = await import(
|
||||
'/heros_web/src/game/state/campaignState.ts'
|
||||
);
|
||||
const actionModule = await import(
|
||||
'/heros_web/src/game/state/thirdCampPreparationActions.ts'
|
||||
);
|
||||
const explorationActionModule = await import(
|
||||
'/heros_web/src/game/state/thirdCampExplorationActions.ts'
|
||||
);
|
||||
const { battleScenarios } = await import(
|
||||
'/heros_web/src/game/data/battles.ts'
|
||||
);
|
||||
const scenario = battleScenarios[targetBattleId];
|
||||
if (!scenario) {
|
||||
throw new Error(
|
||||
`Missing retry scenario ${targetBattleId}.`
|
||||
);
|
||||
}
|
||||
const entry =
|
||||
explorationActionModule.enterThirdCampExploration();
|
||||
const activity =
|
||||
explorationActionModule.recordThirdCampExplorationActivity(
|
||||
'information'
|
||||
);
|
||||
const selection =
|
||||
actionModule.setThirdCampPreparationPriority(
|
||||
'information'
|
||||
);
|
||||
if (!entry.ok || !activity.ok || !selection.ok) {
|
||||
throw new Error(
|
||||
`Failed to seed a legacy preparation selection: ${JSON.stringify(
|
||||
{
|
||||
entry,
|
||||
activity,
|
||||
selection
|
||||
}
|
||||
)}`
|
||||
);
|
||||
}
|
||||
campaignModule.setFirstBattleReport({
|
||||
battleId: scenario.id,
|
||||
battleTitle: scenario.title,
|
||||
outcome: 'defeat',
|
||||
turnNumber: 7,
|
||||
rewardGold: 0,
|
||||
defeatedEnemies: 0,
|
||||
totalEnemies: scenario.units.filter(
|
||||
(unit) => unit.faction === 'enemy'
|
||||
).length,
|
||||
objectives: scenario.objectives.map((objective) => ({
|
||||
id: objective.id,
|
||||
label: objective.label,
|
||||
achieved: false,
|
||||
status: 'failed',
|
||||
detail: '미달성',
|
||||
rewardGold: 0
|
||||
})),
|
||||
units: scenario.units,
|
||||
bonds: scenario.bonds.map((bond) => ({
|
||||
...bond,
|
||||
battleExp: 0
|
||||
})),
|
||||
itemRewards: [],
|
||||
campaignRewards: {
|
||||
supplies: [],
|
||||
equipment: [],
|
||||
reputation: [],
|
||||
recruits: [],
|
||||
unlocks: [],
|
||||
note: ''
|
||||
},
|
||||
completedCampDialogues: [],
|
||||
completedCampVisits: [],
|
||||
createdAt: '2026-07-27T01:00:00.000Z'
|
||||
});
|
||||
campaignModule.completeCampaignAftermath(targetBattleId);
|
||||
const legacyRetry = campaignModule.getCampaignState();
|
||||
legacyRetry.thirdCampPreparationSelection = {
|
||||
...legacyRetry.thirdCampPreparationSelection,
|
||||
confirmed: false
|
||||
};
|
||||
campaignModule.setCampaignState(legacyRetry);
|
||||
const game = window.__HEROS_GAME__;
|
||||
for (const scene of game?.scene.getScenes(true) ?? []) {
|
||||
game.scene.stop(scene.scene.key);
|
||||
}
|
||||
game.scene.start('CampScene', {
|
||||
retryBattleId: targetBattleId,
|
||||
openSortiePrep: true,
|
||||
skipIntroStory: true
|
||||
});
|
||||
}, targetBattleId);
|
||||
await page.waitForFunction(
|
||||
(targetBattleId) => {
|
||||
const state = window.__HEROS_DEBUG__?.camp?.();
|
||||
return (
|
||||
window.__HEROS_DEBUG__
|
||||
?.activeScenes?.()
|
||||
.includes('CampScene') &&
|
||||
state?.campaign?.step === 'fourth-battle' &&
|
||||
state?.sortieVisible === true &&
|
||||
state?.sortiePrepStep === 'briefing' &&
|
||||
state?.thirdCampPreparation?.available === true &&
|
||||
state.thirdCampPreparation.targetBattleId ===
|
||||
targetBattleId &&
|
||||
state.thirdCampPreparation.selectedPriorityId ===
|
||||
'information' &&
|
||||
state.thirdCampPreparation.confirmed === false &&
|
||||
state.thirdCampPreparation.ready === false &&
|
||||
state.thirdCampPreparation.browserOpen === false
|
||||
);
|
||||
},
|
||||
targetBattleId,
|
||||
{ timeout: 90000 }
|
||||
);
|
||||
let camp = await openPreparationBrowser(page);
|
||||
assert.equal(
|
||||
camp.thirdCampPreparation.selectedPriorityId,
|
||||
'information'
|
||||
);
|
||||
assert.equal(camp.thirdCampPreparation.confirmed, false);
|
||||
assert.equal(camp.thirdCampPreparation.ready, false);
|
||||
assert.equal(
|
||||
preparationPriority(camp, 'information').selectable,
|
||||
true
|
||||
);
|
||||
const locked = preparationPriority(camp, 'equipment');
|
||||
assert.equal(locked.selectable, false);
|
||||
assert.equal(
|
||||
locked.unavailableReason,
|
||||
'equipment-change-required'
|
||||
);
|
||||
const lockedLabels = await page.evaluate(() => {
|
||||
const scene =
|
||||
window.__HEROS_GAME__?.scene.getScene('CampScene');
|
||||
return (
|
||||
scene?.children.list
|
||||
.filter(
|
||||
(child) =>
|
||||
child?.active &&
|
||||
child?.visible &&
|
||||
child?.text === '초회 한정'
|
||||
)
|
||||
.map((child) => child.text) ?? []
|
||||
);
|
||||
});
|
||||
assert.equal(
|
||||
lockedLabels.length,
|
||||
2,
|
||||
'Every still-locked CTA in a legacy retry must read "초회 한정".'
|
||||
);
|
||||
await clickSceneBounds(
|
||||
page,
|
||||
'CampScene',
|
||||
locked.actionButtonBounds
|
||||
);
|
||||
await page.waitForTimeout(300);
|
||||
const retryState = await page.evaluate(() => {
|
||||
const scene =
|
||||
window.__HEROS_GAME__?.scene.getScene('CampScene');
|
||||
return {
|
||||
activeScenes:
|
||||
window.__HEROS_DEBUG__?.activeScenes?.() ?? [],
|
||||
camp: window.__HEROS_DEBUG__?.camp?.(),
|
||||
navigationPending: scene?.navigationPending ?? null
|
||||
};
|
||||
});
|
||||
assert.equal(retryState.navigationPending, false);
|
||||
assert.equal(
|
||||
retryState.activeScenes.includes(
|
||||
'CampVisitExplorationScene'
|
||||
),
|
||||
false,
|
||||
'A retry-only locked preparation CTA must not reopen exploration.'
|
||||
);
|
||||
assert.equal(
|
||||
retryState.camp?.thirdCampPreparation?.browserOpen,
|
||||
true
|
||||
);
|
||||
assert.equal(
|
||||
retryState.camp?.thirdCampPreparation?.selectedPriorityId,
|
||||
'information'
|
||||
);
|
||||
assert.equal(
|
||||
retryState.camp?.thirdCampPreparation?.confirmed,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
async function waitForThirdCampExploration(page) {
|
||||
@@ -806,7 +1222,8 @@ async function verifyBattlePriority(
|
||||
assert.deepEqual(seeded.selection, {
|
||||
sourceBattleId,
|
||||
targetBattleId,
|
||||
priorityId: priority.id
|
||||
priorityId: priority.id,
|
||||
confirmed: true
|
||||
});
|
||||
assert.equal(seeded.memory?.selectionRecordId, selectionRecordId);
|
||||
assert.equal(seeded.memory?.priorityId, priority.id);
|
||||
@@ -1048,9 +1465,119 @@ async function verifyBattlePriority(
|
||||
);
|
||||
}
|
||||
|
||||
async function verifyCompanionFormationMismatch(page, renderer) {
|
||||
await seedThirdVictory(page, {
|
||||
acknowledgeActivities: true,
|
||||
selectPriorityId: 'companion',
|
||||
selectedSortieUnitIds: ['liu-bei', 'guan-yu']
|
||||
});
|
||||
await page.evaluate(async () => {
|
||||
await window.__HEROS_DEBUG__.goToCamp();
|
||||
});
|
||||
await waitForCamp(page);
|
||||
await page.evaluate(() => {
|
||||
const scene =
|
||||
window.__HEROS_GAME__?.scene.getScene('CampScene');
|
||||
scene?.showSortiePrep?.();
|
||||
});
|
||||
let camp = await waitForPreparationToggle(page);
|
||||
assert.deepEqual(
|
||||
camp.thirdCampPreparation.compatibility,
|
||||
{
|
||||
compatible: false,
|
||||
requiredUnitIds: ['guan-yu', 'zhang-fei'],
|
||||
missingUnitIds: ['zhang-fei'],
|
||||
disabledReason: 'required-units-missing'
|
||||
}
|
||||
);
|
||||
const checklist = camp.sortieChecklist.find(
|
||||
({ label }) => label === '출진 우선 준비'
|
||||
);
|
||||
assert.equal(checklist?.complete, false);
|
||||
assert.match(checklist?.detail ?? '', /장비.*미편성.*미발동/);
|
||||
await capture(
|
||||
page,
|
||||
`dist/verification-third-camp-preparation-${renderer}-companion-mismatch-camp.png`
|
||||
);
|
||||
|
||||
await page.evaluate(async (battleId) => {
|
||||
await window.__HEROS_DEBUG__.goToBattle(battleId);
|
||||
}, targetBattleId);
|
||||
await page.waitForFunction(
|
||||
({ targetBattleId, selectionRecordId }) => {
|
||||
const state = window.__HEROS_DEBUG__?.battle?.();
|
||||
const preparation = state?.thirdCampPreparation;
|
||||
return (
|
||||
state?.scene === 'BattleScene' &&
|
||||
state.battleId === targetBattleId &&
|
||||
state.phase === 'deployment' &&
|
||||
preparation?.selectionRecordId === selectionRecordId &&
|
||||
preparation.compatibility?.compatible === false
|
||||
);
|
||||
},
|
||||
{ targetBattleId, selectionRecordId },
|
||||
{ timeout: 90000 }
|
||||
);
|
||||
let battle = await readBattle(page);
|
||||
assert.equal(battle.thirdCampPreparation.effectActive, false);
|
||||
assert.equal(
|
||||
battle.thirdCampPreparation.presentation.deployment.status,
|
||||
'incompatible'
|
||||
);
|
||||
assert.equal(
|
||||
battle.thirdCampPreparation.presentation.turn.status,
|
||||
'incompatible'
|
||||
);
|
||||
assert.match(
|
||||
battle.thirdCampPreparation.presentation.deployment.text,
|
||||
/편성되지 않아.*미적용/
|
||||
);
|
||||
await page.evaluate(() => {
|
||||
const scene =
|
||||
window.__HEROS_GAME__?.scene.getScene('BattleScene');
|
||||
scene.phase = 'idle';
|
||||
scene.renderTacticalInitiativeChip();
|
||||
});
|
||||
battle = await readBattle(page);
|
||||
assert.equal(battle.thirdCampPreparation.chip.visible, true);
|
||||
assert.match(
|
||||
battle.thirdCampPreparation.chip.text,
|
||||
/미편성.*미발동/
|
||||
);
|
||||
await capture(
|
||||
page,
|
||||
`dist/verification-third-camp-preparation-${renderer}-companion-mismatch-battle.png`
|
||||
);
|
||||
|
||||
await page.evaluate(() => {
|
||||
window.__HEROS_DEBUG__.forceBattleOutcome('victory');
|
||||
});
|
||||
await page.waitForFunction(
|
||||
() =>
|
||||
window.__HEROS_DEBUG__?.battle?.()
|
||||
?.thirdCampPreparation?.presentation?.result?.status ===
|
||||
'incompatible',
|
||||
undefined,
|
||||
{ timeout: 90000 }
|
||||
);
|
||||
battle = await readBattle(page);
|
||||
assert.match(
|
||||
battle.thirdCampPreparation.resultFeedbackText,
|
||||
/편성되지 않아.*미적용/
|
||||
);
|
||||
}
|
||||
|
||||
async function seedThirdVictory(
|
||||
page,
|
||||
{ acknowledgeActivities, selectPriorityId }
|
||||
{
|
||||
acknowledgeActivities,
|
||||
selectPriorityId,
|
||||
selectedSortieUnitIds = [
|
||||
'liu-bei',
|
||||
'guan-yu',
|
||||
'zhang-fei'
|
||||
]
|
||||
}
|
||||
) {
|
||||
return page.evaluate(
|
||||
async ({
|
||||
@@ -1059,7 +1586,8 @@ async function seedThirdVictory(
|
||||
priorityDialogueId,
|
||||
priorityDialogueBondId,
|
||||
acknowledgeActivities,
|
||||
selectPriorityId
|
||||
selectPriorityId,
|
||||
selectedSortieUnitIds
|
||||
}) => {
|
||||
const game = window.__HEROS_GAME__;
|
||||
for (const scene of game?.scene.getScenes(true) ?? []) {
|
||||
@@ -1160,9 +1688,7 @@ async function seedThirdVictory(
|
||||
])
|
||||
];
|
||||
campaign.selectedSortieUnitIds = [
|
||||
'liu-bei',
|
||||
'guan-yu',
|
||||
'zhang-fei'
|
||||
...selectedSortieUnitIds
|
||||
];
|
||||
campaignModule.setCampaignState(campaign);
|
||||
campaignModule.completeCampaignAftermath(sourceBattleId);
|
||||
@@ -1262,7 +1788,8 @@ async function seedThirdVictory(
|
||||
priorityDialogueId,
|
||||
priorityDialogueBondId,
|
||||
acknowledgeActivities,
|
||||
selectPriorityId
|
||||
selectPriorityId,
|
||||
selectedSortieUnitIds
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -1606,6 +2133,72 @@ async function waitForPreparationToggle(page) {
|
||||
return readCamp(page);
|
||||
}
|
||||
|
||||
async function openCampSaveSlotPanel(page) {
|
||||
const before = await readCampSaveModalState(page);
|
||||
assert.equal(
|
||||
before.slotPanelOpen,
|
||||
false,
|
||||
'The save-slot panel must begin closed.'
|
||||
);
|
||||
await page.evaluate(() => {
|
||||
const scene =
|
||||
window.__HEROS_GAME__?.scene.getScene('CampScene');
|
||||
if (typeof scene?.showCampSaveSlotPanel !== 'function') {
|
||||
throw new Error(
|
||||
'CampScene.showCampSaveSlotPanel is unavailable.'
|
||||
);
|
||||
}
|
||||
scene.showCampSaveSlotPanel();
|
||||
});
|
||||
await page.waitForFunction(
|
||||
() => {
|
||||
const scene =
|
||||
window.__HEROS_GAME__?.scene.getScene('CampScene');
|
||||
return (
|
||||
scene?.saveSlotObjects?.length > 0 &&
|
||||
scene?.saveSlotConfirmObjects?.length === 0
|
||||
);
|
||||
}
|
||||
);
|
||||
return readCampSaveModalState(page);
|
||||
}
|
||||
|
||||
async function waitForCampSaveModalClosed(page) {
|
||||
await page.waitForFunction(
|
||||
() => {
|
||||
const scene =
|
||||
window.__HEROS_GAME__?.scene.getScene('CampScene');
|
||||
return (
|
||||
scene?.saveSlotObjects?.length === 0 &&
|
||||
scene?.saveSlotConfirmObjects?.length === 0 &&
|
||||
scene?.pendingSaveSlot === undefined
|
||||
);
|
||||
}
|
||||
);
|
||||
return readCampSaveModalState(page);
|
||||
}
|
||||
|
||||
async function readCampSaveModalState(page) {
|
||||
return page.evaluate(() => {
|
||||
const scene =
|
||||
window.__HEROS_GAME__?.scene.getScene('CampScene');
|
||||
const camp = window.__HEROS_DEBUG__?.camp?.();
|
||||
return {
|
||||
slotPanelOpen:
|
||||
(scene?.saveSlotObjects?.length ?? 0) > 0,
|
||||
overwriteConfirmOpen:
|
||||
(scene?.saveSlotConfirmObjects?.length ?? 0) > 0,
|
||||
pendingSaveSlot: scene?.pendingSaveSlot ?? null,
|
||||
selectedPriorityId:
|
||||
camp?.thirdCampPreparation?.selectedPriorityId ??
|
||||
null,
|
||||
browserOpen:
|
||||
camp?.thirdCampPreparation?.browserOpen ?? false,
|
||||
activeSaveSlot: camp?.campaign?.activeSaveSlot ?? null
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
async function openPreparationBrowser(page) {
|
||||
let lastState = await readCamp(page);
|
||||
for (let attempt = 0; attempt < 2; attempt += 1) {
|
||||
@@ -1806,6 +2399,7 @@ function assertPreparationSelection(camp, priorityId) {
|
||||
const preparation = camp.thirdCampPreparation;
|
||||
assert.equal(preparation.selectionRecordId, selectionRecordId);
|
||||
assert.equal(preparation.ready, true);
|
||||
assert.equal(preparation.confirmed, true);
|
||||
assert.equal(
|
||||
preparation.selectedPriorityId,
|
||||
priorityId
|
||||
@@ -1840,7 +2434,8 @@ async function assertCampaignSelectionPersisted(
|
||||
{
|
||||
sourceBattleId,
|
||||
targetBattleId,
|
||||
priorityId
|
||||
priorityId,
|
||||
confirmed: true
|
||||
}
|
||||
);
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user