diff --git a/app/game-frontend/e2e/commandArguments.spec.ts b/app/game-frontend/e2e/commandArguments.spec.ts index 8f6f8aab..33ae42eb 100644 --- a/app/game-frontend/e2e/commandArguments.spec.ts +++ b/app/game-frontend/e2e/commandArguments.spec.ts @@ -2227,6 +2227,39 @@ test('limits non-aggression end years to the Ref twenty-year window on desktop a } }); +test('reserves argument-free expansion and reduction immediately on desktop and mobile', async ({ + browser, +}, testInfo) => { + for (const viewport of [ + { name: 'desktop', width: 1200, height: 900 }, + { name: 'mobile', width: 500, height: 900 }, + ]) { + const context = await browser.newContext({ viewport }); + const page = await context.newPage(); + try { + const requests = await install(page); + await page.goto('/che/chief-center'); + const editor = page.locator('[data-command-scope="nation"]'); + + for (const [turn, action] of ['증축', '감축'].entries()) { + await editor.getByRole('button', { name: `${turn + 1}턴 명령 입력`, exact: true }).click(); + const picker = editor.getByTestId('command-picker'); + await picker.getByRole('button', { name: /^(?:국가:)?특수$/ }).click(); + await picker.getByRole('button', { name: new RegExp(action) }).click(); + await expect(picker).toBeHidden(); + await expect(editor.locator('.action-column > div').nth(turn)).toContainText(action); + } + + const serialized = JSON.stringify(requests); + expect(serialized).toContain('"action":"che_증축","args":{}'); + expect(serialized).toContain('"action":"che_감축","args":{}'); + await page.screenshot({ path: testInfo.outputPath(`chief-capital-commands-${viewport.name}.png`) }); + } finally { + await context.close(); + } + } +}); + test('shows a map and target details for every city or nation argument chief command except assignment', async ({ page, }) => { @@ -2277,20 +2310,6 @@ test('shows a map and target details for every city or nation argument chief com await page.screenshot({ path: test.info().outputPath('chief-command-target-map-details.png'), fullPage: true }); - const targetMapPickerWidth = await page - .getByTestId('command-picker') - .evaluate((element) => element.getBoundingClientRect().width); - await page.goto('/che/chief-center'); - await page.getByRole('button', { name: '1턴 명령 입력', exact: true }).click(); - const capitalPicker = page.getByTestId('command-picker'); - await capitalPicker.getByRole('button', { name: /^(?:국가:)?특수$/ }).click(); - await capitalPicker.getByRole('button', { name: /증축/ }).click(); - await expect(capitalPicker.getByTestId('command-argument-map')).toBeVisible(); - const capitalMapPickerWidth = await capitalPicker.evaluate((element) => element.getBoundingClientRect().width); - expect(capitalMapPickerWidth).toBe(targetMapPickerWidth); - expect(capitalMapPickerWidth).toBeGreaterThanOrEqual(700); - await capitalPicker.screenshot({ path: test.info().outputPath('chief-capital-map-expanded-desktop.png') }); - await page.setViewportSize({ width: 500, height: 900 }); await page.goto('/che/chief-center'); await page.getByRole('button', { name: '1턴 명령 입력', exact: true }).click(); diff --git a/app/game-frontend/src/components/command/ReservedCommandEditor.vue b/app/game-frontend/src/components/command/ReservedCommandEditor.vue index d496aca4..7f4529c5 100644 --- a/app/game-frontend/src/components/command/ReservedCommandEditor.vue +++ b/app/game-frontend/src/components/command/ReservedCommandEditor.vue @@ -250,9 +250,7 @@ const togglePicker = (turnIndex?: number) => { }; const selectCommand = (commandKey: string) => { const table = props.commandTable; - const command = table?.[props.scope] - .flatMap((group) => group.values) - .find((entry) => entry.key === commandKey); + const command = table?.[props.scope].flatMap((group) => group.values).find((entry) => entry.key === commandKey); if (!table || !command) return; selectedCommand.value = command; // Ref opens argument commands on a separate processing page. Keep the same @@ -266,8 +264,7 @@ const selectCommand = (commandKey: string) => { }; commandArgs.value = {}; commandArgsValid.value = !command.reqArg; - const needsInformationalConfirmation = commandArgumentPresentation(command.key).mapTarget === 'capital'; - if (!command.reqArg && !needsInformationalConfirmation) submitCommand(); + if (!command.reqArg) submitCommand(); }; const submitCommand = () => { const command = selectedCommand.value; @@ -799,11 +796,7 @@ const clickOutsideMenu = (event: Event) => { @submit="submitCommand" /> ({ lines, mapTarget: 'city' }); const nationTarget = (lines: string[]): CommandArgumentPresentation => ({ lines, mapTarget: 'nation' }); -const capitalTarget = (lines: string[]): CommandArgumentPresentation => ({ lines, mapTarget: 'capital' }); // Ref hwe/ts/processing의 명령별 안내를 예약 명령 옵션창에 맞게 옮긴다. // 징병/모병은 별도 이관 범위이므로 이 표에 넣지 않는다. @@ -38,15 +37,6 @@ const PRESENTATIONS: Record = { ]), cr_인구이동: cityTarget(['현재 도시의 인구를 선택한 인접 도시로 이동합니다.']), che_발령: cityTarget(['선택한 도시로 아국 장수를 발령합니다.', '아국 도시만 대상이 됩니다.']), - che_증축: capitalTarget([ - '현재 수도를 증축해 인구·내정·성벽의 최대치를 높입니다.', - '지도에는 이번 명령의 대상인 현재 수도가 강조됩니다.', - ]), - che_감축: capitalTarget([ - '현재 수도를 감축해 인구·내정·성벽의 최대치를 낮추고 국고를 회수합니다.', - '지도에는 이번 명령의 대상인 현재 수도가 강조됩니다.', - ]), - che_선전포고: nationTarget([ '선택한 국가에 선전포고합니다.', '고립되지 않은 아국 도시와 인접한 국가에만 가능하며 초반 제한의 영향을 받습니다.', @@ -104,7 +94,6 @@ export const commandArgumentPresentation = (commandKey: string): CommandArgument /** * 대상 지도는 명령명 목록이 아니라 API가 내린 실제 인자 계약을 우선한다. - * 인자가 없는 증축·감축의 수도 확인 지도만 presentation의 명시적 target을 사용한다. */ export const resolveCommandArgumentMapTarget = ( commandKey: string, diff --git a/app/game-frontend/src/components/main/CommandArgumentForm.vue b/app/game-frontend/src/components/main/CommandArgumentForm.vue index 8d041b5f..87992a66 100644 --- a/app/game-frontend/src/components/main/CommandArgumentForm.vue +++ b/app/game-frontend/src/components/main/CommandArgumentForm.vue @@ -191,10 +191,6 @@ const mapSelectedCityId = computed(() => { null ); } - if (mapTarget.value === 'capital') { - const myNation = props.mapData.myNation; - return props.mapData.nationList.find((entry) => entry[0] === myNation)?.[3] ?? null; - } return null; }); @@ -215,11 +211,6 @@ const selectedMapTargetName = computed(() => { if (typeof nationId !== 'number') return '-'; return props.mapData?.nationList.find((nation) => nation[0] === nationId)?.[1] ?? '-'; } - if (mapTarget.value === 'capital') { - const cityId = mapSelectedCityId.value; - if (!cityId) return '-'; - return props.mapLayout?.cityList.find((city) => city.id === cityId)?.name ?? '-'; - } return '-'; }); @@ -272,14 +263,6 @@ const mapTargetSummary = computed(() => { const cityCount = props.mapData.cityList.filter((entry) => entry[3] === value).length; return `${nation[1]} · 수도 ${capital?.name ?? '-'} · 도시 ${cityCount.toLocaleString()}개`; } - if (mapTarget.value === 'capital' && mapSelectedCityId.value) { - const city = props.mapLayout.cityList.find((entry) => entry.id === mapSelectedCityId.value); - const dynamic = props.mapData.cityList.find((entry) => entry[0] === mapSelectedCityId.value); - if (!city) return ''; - return `${city.name} · ${props.mapLayout.regionMap[dynamic?.[4] ?? city.region]} · ${ - props.mapLayout.levelMap[dynamic?.[1] ?? city.level] - } · 현재 수도`; - } return ''; }); @@ -424,21 +407,17 @@ watch( :detail-mode="true" :fit-container="true" :show-current-city-marker="true" - :readonly="mapTarget === 'capital'" @select-city="selectMapCity" /> - 현재 명령이 적용될 수도를 지도에서 확인하세요. - 지도에서 도시를 클릭하거나 아래 목록에서 대상을 선택하세요. + 지도에서 도시를 클릭하거나 아래 목록에서 대상을 선택하세요.
- + 현재 도시 {{ currentCityName }} - + - {{ - mapTarget === 'nation' ? '선택 국가' : mapTarget === 'capital' ? '현재 수도' : '선택 도시' - }} + {{ mapTarget === 'nation' ? '선택 국가' : '선택 도시' }} {{ selectedMapTargetName }}
diff --git a/app/game-frontend/test/commandArgumentPresentation.test.ts b/app/game-frontend/test/commandArgumentPresentation.test.ts index f8dca1d7..00b38967 100644 --- a/app/game-frontend/test/commandArgumentPresentation.test.ts +++ b/app/game-frontend/test/commandArgumentPresentation.test.ts @@ -36,8 +36,6 @@ const nationCommands = [ 'che_물자원조', ]; -const capitalCommands = ['che_증축', 'che_감축']; - const otherArgumentCommands = [ 'che_증여', 'che_헌납', @@ -61,7 +59,7 @@ const otherArgumentCommands = [ ]; void test('provides Ref-level guidance for every in-scope argument command', () => { - const expected = [...cityCommands, ...nationCommands, ...capitalCommands, ...otherArgumentCommands].sort(); + const expected = [...cityCommands, ...nationCommands, ...otherArgumentCommands].sort(); assert.deepEqual(presentedCommandKeys().sort(), expected); for (const commandKey of expected) { assert.ok(commandArgumentPresentation(commandKey).lines.join(' ').length >= 12, commandKey); @@ -79,9 +77,6 @@ void test('marks the same city and nation target families that Ref renders with for (const commandKey of nationCommands) { assert.equal(commandArgumentPresentation(commandKey).mapTarget, 'nation', commandKey); } - for (const commandKey of capitalCommands) { - assert.equal(commandArgumentPresentation(commandKey).mapTarget, 'capital', commandKey); - } }); void test('derives selection maps from the actual city and nation argument contract', () => { @@ -97,7 +92,7 @@ void test('derives selection maps from the actual city and nation argument contr ]), 'nation' ); - assert.equal(resolveCommandArgumentMapTarget('che_증축', []), 'capital'); + assert.equal(resolveCommandArgumentMapTarget('che_증축', []), undefined); assert.equal( resolveCommandArgumentMapTarget('future_general_command', [ { key: 'target', label: '장수', kind: 'select', required: true, optionSource: 'generals' },