feat: add battle sidebar quick navigation
This commit is contained in:
@@ -5477,6 +5477,7 @@ async function assertLargeRosterHud(browser, url) {
|
||||
assertLateBattleHeaderLayout(deploymentProbe, 'deployment');
|
||||
assertLateBattleSidebarLayout(deploymentProbe, 'deployment', false);
|
||||
assertLateBattleDeploymentLayout(deploymentProbe);
|
||||
assertSideQuickTabsLayout(deploymentProbe, null, null, false);
|
||||
await page.screenshot({ path: `${screenshotDir}/rc-final-battle-deployment.png`, fullPage: true });
|
||||
await assertCanvasPainted(page, 'final battle deployment');
|
||||
|
||||
@@ -5496,6 +5497,9 @@ async function assertLargeRosterHud(browser, url) {
|
||||
const situationProbe = await readLargeRosterHudProbe(page);
|
||||
assertLateBattleHeaderLayout(situationProbe, 'situation');
|
||||
assertLateBattleSidebarLayout(situationProbe, 'situation', true);
|
||||
assertSideQuickTabsLayout(situationProbe, 'situation', 'situation');
|
||||
|
||||
await assertFinalBattleQuickTabs(page);
|
||||
|
||||
await assertFinalBattleFhdDetailPanels(page);
|
||||
|
||||
@@ -5595,6 +5599,139 @@ async function assertLargeRosterHud(browser, url) {
|
||||
}
|
||||
}
|
||||
|
||||
async function assertFinalBattleQuickTabs(page) {
|
||||
const initialProbe = await readLargeRosterHudProbe(page);
|
||||
assertSideQuickTabsLayout(initialProbe, 'situation', 'situation');
|
||||
|
||||
const rosterTab = initialProbe.quickTabs.tabs.find((tab) => tab.id === 'roster');
|
||||
await page.mouse.move(
|
||||
rosterTab.bounds.x + rosterTab.bounds.width / 2,
|
||||
rosterTab.bounds.y + rosterTab.bounds.height / 2
|
||||
);
|
||||
await page.waitForFunction(() => {
|
||||
const quickTabs = window.__HEROS_DEBUG__?.battle()?.battleHud?.quickTabs;
|
||||
return quickTabs?.hoveredTab === 'roster' && quickTabs.tabs?.find((tab) => tab.id === 'roster')?.visualState === 'hover';
|
||||
}, undefined, { timeout: 30000 });
|
||||
const hoverProbe = await readLargeRosterHudProbe(page);
|
||||
assert(
|
||||
hoverProbe.quickTabs.activeTab === 'situation' && hoverProbe.quickTabs.hoveredTab === 'roster',
|
||||
`Expected hover feedback without changing the active quick tab: ${JSON.stringify(hoverProbe)}`
|
||||
);
|
||||
await page.mouse.move(initialProbe.sceneBounds.x + 20, initialProbe.sceneBounds.y + 20);
|
||||
await page.waitForFunction(
|
||||
() => window.__HEROS_DEBUG__?.battle()?.battleHud?.quickTabs?.hoveredTab === null,
|
||||
undefined,
|
||||
{ timeout: 30000 }
|
||||
);
|
||||
|
||||
await page.mouse.click(
|
||||
rosterTab.bounds.x + rosterTab.bounds.width / 2,
|
||||
rosterTab.bounds.y + rosterTab.bounds.height / 2
|
||||
);
|
||||
await waitForSideQuickTabView(page, 'roster', 'roster');
|
||||
const rosterProbe = await readLargeRosterHudProbe(page);
|
||||
assertSideQuickTabsLayout(rosterProbe, 'roster', 'roster');
|
||||
assertCompletedSidePanelTransition(rosterProbe, 'tab', 'roster');
|
||||
|
||||
await page.keyboard.press('1');
|
||||
await waitForSideQuickTabView(page, 'situation', 'situation');
|
||||
await page.keyboard.press('2');
|
||||
await waitForSideQuickTabView(page, 'roster', 'roster');
|
||||
const keyboardRosterProbe = await readLargeRosterHudProbe(page);
|
||||
assertSideQuickTabsLayout(keyboardRosterProbe, 'roster', 'roster');
|
||||
assertCompletedSidePanelTransition(keyboardRosterProbe, 'tab', 'roster');
|
||||
|
||||
await page.keyboard.press('3');
|
||||
await waitForSideQuickTabView(page, 'bond', 'bond');
|
||||
const bondProbe = await readLargeRosterHudProbe(page);
|
||||
assertSideQuickTabsLayout(bondProbe, 'bond', 'bond');
|
||||
assertCompletedSidePanelTransition(bondProbe, 'tab', 'bond');
|
||||
|
||||
await page.keyboard.press('4');
|
||||
await waitForSideQuickTabView(page, 'threat', 'threat-overview');
|
||||
await page.waitForFunction(() => window.__HEROS_DEBUG__?.battle()?.markerCount > 0, undefined, { timeout: 30000 });
|
||||
const threatProbe = await readLargeRosterHudProbe(page);
|
||||
assertSideQuickTabsLayout(threatProbe, 'threat', 'threat-overview');
|
||||
assert(threatProbe.markerCount > 0, `Expected the threat quick tab to paint map markers: ${JSON.stringify(threatProbe)}`);
|
||||
assertCompletedSidePanelTransition(threatProbe, 'tab', 'threat-overview');
|
||||
|
||||
await page.keyboard.press('1');
|
||||
await waitForSideQuickTabView(page, 'situation', 'situation');
|
||||
await page.waitForFunction(() => window.__HEROS_DEBUG__?.battle()?.markerCount === 0, undefined, { timeout: 30000 });
|
||||
const situationProbe = await readLargeRosterHudProbe(page);
|
||||
assertSideQuickTabsLayout(situationProbe, 'situation', 'situation');
|
||||
assert(situationProbe.markerCount === 0, `Expected another quick tab to clear threat markers: ${JSON.stringify(situationProbe)}`);
|
||||
assertCompletedSidePanelTransition(situationProbe, 'tab', 'situation');
|
||||
|
||||
await page.evaluate(() => {
|
||||
const scene = window.__HEROS_GAME__?.scene.getScene('BattleScene');
|
||||
if (!scene) {
|
||||
return;
|
||||
}
|
||||
scene.phase = 'animating';
|
||||
scene.renderSituationPanel?.('빠른 탭 입력 차단 점검');
|
||||
});
|
||||
await page.waitForFunction(() => {
|
||||
const quickTabs = window.__HEROS_DEBUG__?.battle()?.battleHud?.quickTabs;
|
||||
return quickTabs?.visible === true && quickTabs.enabled === false && quickTabs.activeTab === 'situation';
|
||||
}, undefined, { timeout: 30000 });
|
||||
const disabledProbe = await readLargeRosterHudProbe(page);
|
||||
const disabledRosterTab = disabledProbe.quickTabs.tabs.find((tab) => tab.id === 'roster');
|
||||
assert(
|
||||
disabledProbe.quickTabs.tabs.find((tab) => tab.id === 'situation')?.visualState === 'disabled-active' &&
|
||||
disabledRosterTab?.visualState === 'disabled',
|
||||
`Expected a dimmed active tab and disabled alternatives while battle input is locked: ${JSON.stringify(disabledProbe)}`
|
||||
);
|
||||
await page.keyboard.press('2');
|
||||
await page.mouse.click(
|
||||
disabledRosterTab.bounds.x + disabledRosterTab.bounds.width / 2,
|
||||
disabledRosterTab.bounds.y + disabledRosterTab.bounds.height / 2
|
||||
);
|
||||
await page.evaluate(() => new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve))));
|
||||
const blockedProbe = await readLargeRosterHudProbe(page);
|
||||
assert(
|
||||
blockedProbe.quickTabs.activeTab === 'situation' && blockedProbe.quickTabs.currentView === 'situation',
|
||||
`Expected disabled mouse and keyboard quick-tab input to be ignored: ${JSON.stringify(blockedProbe)}`
|
||||
);
|
||||
|
||||
await page.evaluate(() => {
|
||||
const scene = window.__HEROS_GAME__?.scene.getScene('BattleScene');
|
||||
if (scene) {
|
||||
scene.phase = 'idle';
|
||||
}
|
||||
});
|
||||
await page.waitForFunction(
|
||||
() => window.__HEROS_DEBUG__?.battle()?.battleHud?.quickTabs?.enabled === true,
|
||||
undefined,
|
||||
{ timeout: 30000 }
|
||||
);
|
||||
const restoredProbe = await readLargeRosterHudProbe(page);
|
||||
const restoredRosterTab = restoredProbe.quickTabs.tabs.find((tab) => tab.id === 'roster');
|
||||
await page.mouse.click(
|
||||
restoredRosterTab.bounds.x + restoredRosterTab.bounds.width / 2,
|
||||
restoredRosterTab.bounds.y + restoredRosterTab.bounds.height / 2
|
||||
);
|
||||
await waitForSideQuickTabView(page, 'roster', 'roster');
|
||||
await page.keyboard.press('1');
|
||||
await waitForSideQuickTabView(page, 'situation', 'situation');
|
||||
}
|
||||
|
||||
async function waitForSideQuickTabView(page, activeTab, currentView) {
|
||||
await page.waitForFunction(
|
||||
({ expectedTab, expectedView }) => {
|
||||
const hud = window.__HEROS_DEBUG__?.battle()?.battleHud;
|
||||
return (
|
||||
hud?.quickTabs?.activeTab === expectedTab &&
|
||||
hud.quickTabs.currentView === expectedView &&
|
||||
!hud.panelTransition?.active &&
|
||||
hud.panelTransition?.completedRevision === hud.panelTransition?.revision
|
||||
);
|
||||
},
|
||||
{ expectedTab: activeTab, expectedView: currentView },
|
||||
{ timeout: 30000 }
|
||||
);
|
||||
}
|
||||
|
||||
async function assertFinalBattleFhdDetailPanels(page) {
|
||||
const allyFixture = await page.evaluate(() => {
|
||||
const scene = window.__HEROS_GAME__?.scene.getScene('BattleScene');
|
||||
@@ -5617,6 +5754,7 @@ async function assertFinalBattleFhdDetailPanels(page) {
|
||||
);
|
||||
const allyDetailProbe = await readLargeRosterHudProbe(page);
|
||||
assertUnitDetailPanelLayout(allyDetailProbe, 'zhuge-liang', 'ally');
|
||||
assertCompletedSidePanelTransition(allyDetailProbe, 'detail', 'officer-detail');
|
||||
|
||||
const enemyFixture = await page.evaluate(() => {
|
||||
const scene = window.__HEROS_GAME__?.scene.getScene('BattleScene');
|
||||
@@ -5642,6 +5780,23 @@ async function assertFinalBattleFhdDetailPanels(page) {
|
||||
await page.screenshot({ path: `${screenshotDir}/rc-final-battle-unit-detail.png`, fullPage: true });
|
||||
await assertCanvasPainted(page, 'final battle unit detail');
|
||||
|
||||
await page.mouse.click(
|
||||
enemyDetailProbe.unitDetailPanel.returnButtonBounds.x + enemyDetailProbe.unitDetailPanel.returnButtonBounds.width / 2,
|
||||
enemyDetailProbe.unitDetailPanel.returnButtonBounds.y + enemyDetailProbe.unitDetailPanel.returnButtonBounds.height / 2
|
||||
);
|
||||
await waitForSideQuickTabView(page, 'roster', 'roster');
|
||||
await page.waitForFunction(
|
||||
() => window.__HEROS_DEBUG__?.battle()?.battleHud?.rosterPanel?.tab === 'enemy',
|
||||
undefined,
|
||||
{ timeout: 30000 }
|
||||
);
|
||||
const returnedRosterProbe = await readLargeRosterHudProbe(page);
|
||||
assert(
|
||||
returnedRosterProbe.roster?.tab === 'enemy' && returnedRosterProbe.unitDetailPanel === null && returnedRosterProbe.phase === 'idle',
|
||||
`Expected the shared unit-detail return to restore the matching roster: ${JSON.stringify(returnedRosterProbe)}`
|
||||
);
|
||||
assertCompletedSidePanelTransition(returnedRosterProbe, 'back', 'roster');
|
||||
|
||||
const bondFixture = await page.evaluate(() => {
|
||||
const scene = window.__HEROS_GAME__?.scene.getScene('BattleScene');
|
||||
if (!scene) {
|
||||
@@ -5725,10 +5880,24 @@ async function assertFinalBattleFhdDetailPanels(page) {
|
||||
);
|
||||
const threatProbe = await readLargeRosterHudProbe(page);
|
||||
assertThreatPanelLayout(threatProbe, expectedThreatIds);
|
||||
assertCompletedSidePanelTransition(threatProbe, 'detail', 'threat-detail');
|
||||
await waitForBattleHudPaint(page);
|
||||
await page.screenshot({ path: `${screenshotDir}/rc-final-battle-threat-panel.png`, fullPage: true });
|
||||
await assertCanvasPainted(page, 'final battle threat panel');
|
||||
|
||||
await page.mouse.click(
|
||||
threatProbe.threatPanel.returnButtonBounds.x + threatProbe.threatPanel.returnButtonBounds.width / 2,
|
||||
threatProbe.threatPanel.returnButtonBounds.y + threatProbe.threatPanel.returnButtonBounds.height / 2
|
||||
);
|
||||
await waitForSideQuickTabView(page, 'threat', 'threat-overview');
|
||||
await page.waitForFunction(() => window.__HEROS_DEBUG__?.battle()?.markerCount > 0, undefined, { timeout: 30000 });
|
||||
const returnedThreatProbe = await readLargeRosterHudProbe(page);
|
||||
assert(
|
||||
returnedThreatProbe.threatPanel === null && returnedThreatProbe.markerCount > 0,
|
||||
`Expected the shared threat return to restore the map range overview: ${JSON.stringify(returnedThreatProbe)}`
|
||||
);
|
||||
assertCompletedSidePanelTransition(returnedThreatProbe, 'back', 'threat-overview');
|
||||
|
||||
await page.evaluate(() => {
|
||||
window.__HEROS_GAME__?.scene.getScene('BattleScene')?.renderTerrainDetail?.(38, 96);
|
||||
});
|
||||
@@ -5738,21 +5907,40 @@ async function assertFinalBattleFhdDetailPanels(page) {
|
||||
}, undefined, { timeout: 30000 });
|
||||
const terrainProbe = await readLargeRosterHudProbe(page);
|
||||
assertTerrainPanelLayout(terrainProbe, 'camp');
|
||||
assertCompletedSidePanelTransition(terrainProbe, 'detail', 'terrain-detail');
|
||||
await waitForBattleHudPaint(page);
|
||||
await page.screenshot({ path: `${screenshotDir}/rc-final-battle-terrain-panel.png`, fullPage: true });
|
||||
await assertCanvasPainted(page, 'final battle terrain panel');
|
||||
|
||||
await page.keyboard.press('Escape');
|
||||
await waitForSideQuickTabView(page, 'situation', 'situation');
|
||||
const returnedSituationProbe = await readLargeRosterHudProbe(page);
|
||||
assert(
|
||||
returnedSituationProbe.terrainPanel === null && returnedSituationProbe.miniMap?.visible === true,
|
||||
`Expected the shared terrain return to restore the situation overview: ${JSON.stringify(returnedSituationProbe)}`
|
||||
);
|
||||
assertCompletedSidePanelTransition(returnedSituationProbe, 'back', 'situation');
|
||||
}
|
||||
|
||||
async function waitForBattleHudPaint(page) {
|
||||
await page.waitForFunction(() => {
|
||||
const transition = window.__HEROS_DEBUG__?.battle()?.battleHud?.panelTransition;
|
||||
return !transition || (!transition.active && transition.completedRevision === transition.revision);
|
||||
}, undefined, { timeout: 30000 });
|
||||
await page.evaluate(() => new Promise((resolve) => {
|
||||
requestAnimationFrame(() => requestAnimationFrame(resolve));
|
||||
}));
|
||||
}
|
||||
|
||||
async function readLargeRosterHudProbe(page) {
|
||||
await page.waitForFunction(() => {
|
||||
const transition = window.__HEROS_DEBUG__?.battle()?.battleHud?.panelTransition;
|
||||
return !transition || (!transition.active && transition.completedRevision === transition.revision);
|
||||
}, undefined, { timeout: 30000 });
|
||||
return page.evaluate(() => {
|
||||
const scene = window.__HEROS_GAME__?.scene.getScene('BattleScene');
|
||||
const hud = window.__HEROS_DEBUG__?.battle()?.battleHud;
|
||||
const battle = window.__HEROS_DEBUG__?.battle();
|
||||
const hud = battle?.battleHud;
|
||||
return {
|
||||
roster: hud?.rosterPanel ?? null,
|
||||
recentActions: hud?.recentActions ?? null,
|
||||
@@ -5764,6 +5952,11 @@ async function readLargeRosterHudProbe(page) {
|
||||
bondPanel: hud?.bondPanel ?? null,
|
||||
threatPanel: hud?.threatPanel ?? null,
|
||||
terrainPanel: hud?.terrainPanel ?? null,
|
||||
quickTabs: hud?.quickTabs ?? null,
|
||||
panelTransition: hud?.panelTransition ?? null,
|
||||
markerCount: battle?.markerCount ?? 0,
|
||||
phase: battle?.phase ?? null,
|
||||
selectedUnitId: hud?.miniMap?.selectedUnitId ?? null,
|
||||
sceneBounds: scene
|
||||
? { x: 0, y: 0, width: scene.scale.width, height: scene.scale.height }
|
||||
: null,
|
||||
@@ -5774,6 +5967,70 @@ async function readLargeRosterHudProbe(page) {
|
||||
});
|
||||
}
|
||||
|
||||
function assertSideQuickTabsLayout(probe, expectedActiveTab, expectedView, expectedVisible = true) {
|
||||
const quickTabs = probe.quickTabs;
|
||||
if (!expectedVisible) {
|
||||
assert(
|
||||
quickTabs?.visible === false && quickTabs.tabs?.length === 0 && quickTabs.bounds === null,
|
||||
`Expected deployment to omit side quick tabs: ${JSON.stringify(probe)}`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const tabs = quickTabs?.tabs ?? [];
|
||||
const activeTabs = tabs.filter((tab) => tab.active);
|
||||
assert(
|
||||
quickTabs?.visible === true &&
|
||||
quickTabs.enabled === true &&
|
||||
quickTabs.activeTab === expectedActiveTab &&
|
||||
quickTabs.currentView === expectedView &&
|
||||
tabs.length === 4 &&
|
||||
sameJsonValue(tabs.map((tab) => tab.id), ['situation', 'roster', 'bond', 'threat']) &&
|
||||
sameJsonValue(tabs.map((tab) => tab.label), ['전황', '부대', '공명', '위협']) &&
|
||||
sameJsonValue(tabs.map((tab) => tab.shortcut), ['1', '2', '3', '4']) &&
|
||||
activeTabs.length === 1 &&
|
||||
activeTabs[0].id === expectedActiveTab &&
|
||||
activeTabs[0].visualState === 'active',
|
||||
`Expected one active 1-4 quick tab for ${expectedView}: ${JSON.stringify(probe)}`
|
||||
);
|
||||
assert(
|
||||
isFiniteBounds(quickTabs.bounds) &&
|
||||
boundsInside(quickTabs.bounds, probe.panelBounds) &&
|
||||
boundsInside(quickTabs.bounds, probe.sceneBounds) &&
|
||||
probe.header.sortieOrderBounds.y + probe.header.sortieOrderBounds.height <= quickTabs.bounds.y + 1 &&
|
||||
quickTabs.bounds.y + quickTabs.bounds.height <= quickTabs.bodyTop + 1 &&
|
||||
quickTabs.bodyTop === probe.sidebar.bodyTop &&
|
||||
probe.sidebar.bounds.y >= quickTabs.bodyTop,
|
||||
`Expected quick tabs between the FHD header and side-panel body: ${JSON.stringify(probe)}`
|
||||
);
|
||||
tabs.forEach((tab) => {
|
||||
assert(
|
||||
boundsInside(tab.bounds, quickTabs.bounds) &&
|
||||
boundsInside(tab.labelBounds, tab.bounds) &&
|
||||
boundsInside(tab.shortcutBounds, tab.bounds),
|
||||
`Expected quick-tab content inside its FHD button: ${JSON.stringify(tab)}`
|
||||
);
|
||||
});
|
||||
assertHorizontalBounds(tabs.map((tab) => tab.bounds), `${expectedView} quick tabs`);
|
||||
}
|
||||
|
||||
function assertCompletedSidePanelTransition(probe, expectedTrigger, expectedView) {
|
||||
const transition = probe.panelTransition;
|
||||
assert(
|
||||
transition?.active === false &&
|
||||
transition.completedRevision === transition.revision &&
|
||||
transition.revision > 0 &&
|
||||
transition.last?.kind === 'fade-slide' &&
|
||||
transition.last.trigger === expectedTrigger &&
|
||||
transition.last.to === expectedView &&
|
||||
transition.last.durationMs >= 100 &&
|
||||
transition.last.durationMs <= 240 &&
|
||||
transition.last.offsetX > 0 &&
|
||||
transition.last.objectCount > 0,
|
||||
`Expected a completed side-panel transition into ${expectedView}: ${JSON.stringify(probe)}`
|
||||
);
|
||||
}
|
||||
|
||||
function assertLateBattleHeaderLayout(probe, label) {
|
||||
const { header, panelBounds, sceneBounds } = probe;
|
||||
const headerBounds = [
|
||||
@@ -5866,6 +6123,7 @@ function assertLateBattleDeploymentLayout(probe) {
|
||||
function assertUnitDetailPanelLayout(probe, expectedUnitId, label) {
|
||||
const panel = probe.unitDetailPanel;
|
||||
assertLateBattleSidebarLayout(probe, `${label} unit detail`, false);
|
||||
assertSideQuickTabsLayout(probe, 'roster', 'officer-detail');
|
||||
assert(
|
||||
panel?.unitId === expectedUnitId &&
|
||||
panel.summaryBounds?.length === 3 &&
|
||||
@@ -5881,6 +6139,7 @@ function assertUnitDetailPanelLayout(probe, expectedUnitId, label) {
|
||||
panel.headerBounds,
|
||||
panel.nameBounds,
|
||||
panel.levelBadgeBounds,
|
||||
panel.returnButtonBounds,
|
||||
panel.hpBounds,
|
||||
...panel.summaryBounds,
|
||||
...panel.statBounds,
|
||||
@@ -5893,6 +6152,8 @@ function assertUnitDetailPanelLayout(probe, expectedUnitId, label) {
|
||||
assert(
|
||||
boundsInside(panel.nameBounds, panel.headerBounds) &&
|
||||
boundsInside(panel.levelBadgeBounds, panel.headerBounds) &&
|
||||
boundsInside(panel.returnButtonBounds, panel.headerBounds) &&
|
||||
panel.returnTarget === 'roster' &&
|
||||
panel.nameBounds.x + panel.nameBounds.width <= panel.levelBadgeBounds.x + 1,
|
||||
`Expected the FHD ${label} unit name to remain inside the header and left of the level badge: ${JSON.stringify(panel)}`
|
||||
);
|
||||
@@ -5917,6 +6178,7 @@ function assertUnitDetailPanelLayout(probe, expectedUnitId, label) {
|
||||
function assertBondPanelLayout(probe, expectedPage, expectedPageCount, expectedTotalCount) {
|
||||
const panel = probe.bondPanel;
|
||||
assertLateBattleSidebarLayout(probe, `bond page ${expectedPage + 1}`, false);
|
||||
assertSideQuickTabsLayout(probe, 'bond', 'bond');
|
||||
const expectedVisibleCount = Math.min(
|
||||
panel?.rowsPerPage ?? 0,
|
||||
Math.max(0, expectedTotalCount - expectedPage * (panel?.rowsPerPage ?? 0))
|
||||
@@ -5965,6 +6227,7 @@ function assertBondPanelLayout(probe, expectedPage, expectedPageCount, expectedT
|
||||
function assertThreatPanelLayout(probe, expectedEnemyIds) {
|
||||
const panel = probe.threatPanel;
|
||||
assertLateBattleSidebarLayout(probe, 'threat detail', true);
|
||||
assertSideQuickTabsLayout(probe, 'threat', 'threat-detail');
|
||||
assert(
|
||||
panel?.tile?.x === 38 &&
|
||||
panel.tile.y === 96 &&
|
||||
@@ -5978,6 +6241,7 @@ function assertThreatPanelLayout(probe, expectedEnemyIds) {
|
||||
const enemyBounds = panel.enemyRows.map((row) => row.rowBounds);
|
||||
const allBounds = [
|
||||
panel.headerBounds,
|
||||
panel.returnButtonBounds,
|
||||
...summaryBounds,
|
||||
...panel.summaryRows.flatMap((row) => [row.labelBounds, row.valueBounds]),
|
||||
...enemyBounds,
|
||||
@@ -5985,7 +6249,11 @@ function assertThreatPanelLayout(probe, expectedEnemyIds) {
|
||||
panel.messageBounds
|
||||
];
|
||||
assertDetailBoundsInside(probe, allBounds, 'threat detail');
|
||||
assertSituationRows(panel.summaryRows, 'threat summary');
|
||||
assert(
|
||||
panel.returnTarget === 'threat-overview' && boundsInside(panel.returnButtonBounds, panel.headerBounds),
|
||||
`Expected threat detail to expose an in-header range return: ${JSON.stringify(panel)}`
|
||||
);
|
||||
assertSituationGrid(panel.summaryRows, 'threat summary', 2);
|
||||
panel.enemyRows.forEach((row) => {
|
||||
assert(
|
||||
boundsInside(row.nameBounds, row.rowBounds) &&
|
||||
@@ -5996,7 +6264,7 @@ function assertThreatPanelLayout(probe, expectedEnemyIds) {
|
||||
});
|
||||
assertSequentialBounds(enemyBounds, 'threat enemy rows');
|
||||
assertSequentialBounds(
|
||||
[panel.headerBounds, ...summaryBounds, ...enemyBounds, panel.messageBounds],
|
||||
[panel.headerBounds, boundsUnion(summaryBounds), ...enemyBounds, panel.messageBounds],
|
||||
'threat detail sections'
|
||||
);
|
||||
assert(
|
||||
@@ -6008,6 +6276,7 @@ function assertThreatPanelLayout(probe, expectedEnemyIds) {
|
||||
function assertTerrainPanelLayout(probe, expectedTerrain) {
|
||||
const panel = probe.terrainPanel;
|
||||
assertLateBattleSidebarLayout(probe, 'terrain detail', true);
|
||||
assertSideQuickTabsLayout(probe, 'situation', 'terrain-detail');
|
||||
assert(
|
||||
panel?.tile?.x === 38 &&
|
||||
panel.tile.y === 96 &&
|
||||
@@ -6019,11 +6288,16 @@ function assertTerrainPanelLayout(probe, expectedTerrain) {
|
||||
const summaryBounds = panel.summaryRows.map((row) => row.bounds);
|
||||
const allBounds = [
|
||||
panel.headerBounds,
|
||||
panel.returnButtonBounds,
|
||||
...summaryBounds,
|
||||
...panel.summaryRows.flatMap((row) => [row.labelBounds, row.valueBounds]),
|
||||
panel.messageBounds
|
||||
];
|
||||
assertDetailBoundsInside(probe, allBounds, 'terrain detail');
|
||||
assert(
|
||||
panel.returnTarget === 'situation' && boundsInside(panel.returnButtonBounds, panel.headerBounds),
|
||||
`Expected terrain detail to expose an in-header situation return: ${JSON.stringify(panel)}`
|
||||
);
|
||||
assertSituationRows(panel.summaryRows, 'terrain summary');
|
||||
assertSequentialBounds([panel.headerBounds, ...summaryBounds, panel.messageBounds], 'terrain detail sections');
|
||||
assert(
|
||||
@@ -6044,6 +6318,24 @@ function assertSituationRows(rows, label) {
|
||||
assertSequentialBounds(rows.map((row) => row.bounds), `${label} rows`);
|
||||
}
|
||||
|
||||
function assertSituationGrid(rows, label, columnCount) {
|
||||
rows.forEach((row, index) => {
|
||||
assert(
|
||||
boundsInside(row.labelBounds, row.bounds) &&
|
||||
boundsInside(row.valueBounds, row.bounds) &&
|
||||
row.labelBounds.x + row.labelBounds.width <= row.valueBounds.x + 1,
|
||||
`Expected ${label} cell ${index + 1} labels and values in separate columns: ${JSON.stringify(row)}`
|
||||
);
|
||||
});
|
||||
const rowGroups = [];
|
||||
for (let index = 0; index < rows.length; index += columnCount) {
|
||||
const rowBounds = rows.slice(index, index + columnCount).map((row) => row.bounds);
|
||||
assertHorizontalBounds(rowBounds, `${label} row ${rowGroups.length + 1}`);
|
||||
rowGroups.push(boundsUnion(rowBounds));
|
||||
}
|
||||
assertSequentialBounds(rowGroups, `${label} rows`);
|
||||
}
|
||||
|
||||
function assertDetailBoundsInside(probe, boundsList, label) {
|
||||
assert(
|
||||
isFiniteBounds(probe.sidebar?.bounds) &&
|
||||
|
||||
Reference in New Issue
Block a user