전체화면 명령 입력의 제출 버튼을 primary로 구분

하단 `입력`이 막대 위 글자로만 보여 제출인지 알기 어려웠고 녹색 상단 버튼이 제출처럼
보였다. Ref처럼 녹색은 이동(명령 취소)에만 두고, 제출은 명령명을 붙인 primary 버튼
(`화계 입력`)으로, `명령 다시 선택`은 secondary로 낮춘다. e2e locator와 버튼 색·
geometry assertion을 함께 갱신한다.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-23 14:04:49 +00:00
co-authored by Claude Opus 5.5
parent a373ed327d
commit b90dd4499d
6 changed files with 72 additions and 28 deletions
@@ -139,7 +139,7 @@ test('persists one chief command and exposes it to a normal nation user and anot
selectedTargetId = selectableGeneralIds.find((generalId) => generalId !== editor.id); selectedTargetId = selectableGeneralIds.find((generalId) => generalId !== editor.id);
if (!selectedTargetId) throw new Error('No reward target is available in the live command table.'); if (!selectedTargetId) throw new Error('No reward target is available in the live command table.');
await argumentForm.locator('select').selectOption(String(selectedTargetId)); await argumentForm.locator('select').selectOption(String(selectedTargetId));
await picker.getByRole('button', { name: '입력', exact: true }).click(); await picker.getByRole('button', { name: / 입력$/ }).click();
await expect( await expect(
editorPage.getByTestId('chief-command-editor').locator('.editor-turn-row strong').first() editorPage.getByTestId('chief-command-editor').locator('.editor-turn-row strong').first()
).toHaveText('포상'); ).toHaveText('포상');
+49 -20
View File
@@ -1237,7 +1237,7 @@ test('shows and reserves the Ref spy command for a user on desktop and mobile',
); );
await form.locator('select').selectOption('2'); await form.locator('select').selectOption('2');
await picker.screenshot({ path: test.info().outputPath('spy-command-desktop-1200.png') }); await picker.screenshot({ path: test.info().outputPath('spy-command-desktop-1200.png') });
await picker.getByRole('button', { name: '입력', exact: true }).click(); await picker.getByRole('button', { name: / 입력$/ }).click();
await expect(editor.locator('.action-column > div').first()).toHaveText('【허창】에 첩보 실행'); await expect(editor.locator('.action-column > div').first()).toHaveText('【허창】에 첩보 실행');
expect(JSON.stringify(requests)).toContain('"action":"che_첩보","args":{"destCityId":2}'); expect(JSON.stringify(requests)).toContain('"action":"che_첩보","args":{"destCityId":2}');
@@ -1355,7 +1355,7 @@ test('defaults founding to a Ref-selectable nation trait and opens colored optio
await mobilePicker.getByRole('button', { name: '건국', exact: true }).click(); await mobilePicker.getByRole('button', { name: '건국', exact: true }).click();
await mobilePicker.getByLabel('국가명').fill('가나다라마바사아자차'); await mobilePicker.getByLabel('국가명').fill('가나다라마바사아자차');
await expect(mobilePicker.getByText('국가명은 전각 9자 또는 반각 18자 이하여야 합니다.')).toBeVisible(); await expect(mobilePicker.getByText('국가명은 전각 9자 또는 반각 18자 이하여야 합니다.')).toBeVisible();
await expect(mobilePicker.getByRole('button', { name: '입력', exact: true })).toBeDisabled(); await expect(mobilePicker.getByRole('button', { name: / 입력$/ })).toBeDisabled();
await mobilePicker.getByLabel('국가명').fill('신국'); await mobilePicker.getByLabel('국가명').fill('신국');
const mobileColorType = mobilePicker.getByLabel('국기 색상'); const mobileColorType = mobilePicker.getByLabel('국기 색상');
await mobileColorType.click(); await mobileColorType.click();
@@ -1384,7 +1384,7 @@ test('defaults founding to a Ref-selectable nation trait and opens colored optio
expect(colorSelectRect.right).toBeLessThanOrEqual(500); expect(colorSelectRect.right).toBeLessThanOrEqual(500);
await expect.poll(() => page.evaluate(() => document.documentElement.scrollWidth)).toBeLessThanOrEqual(500); await expect.poll(() => page.evaluate(() => document.documentElement.scrollWidth)).toBeLessThanOrEqual(500);
await mobilePicker.screenshot({ path: test.info().outputPath('founding-colored-option-mobile-500.png') }); await mobilePicker.screenshot({ path: test.info().outputPath('founding-colored-option-mobile-500.png') });
await mobilePicker.getByRole('button', { name: '입력', exact: true }).click(); await mobilePicker.getByRole('button', { name: / 입력$/ }).click();
await expect(page.locator('[data-command-scope="general"] .action-column > div').first()).toContainText('신국'); await expect(page.locator('[data-command-scope="general"] .action-column > div').first()).toContainText('신국');
await expect.poll(() => JSON.stringify(requests)).toContain('"colorType":15'); await expect.poll(() => JSON.stringify(requests)).toContain('"colorType":15');
}); });
@@ -1499,7 +1499,7 @@ test('shows speciality reset cooldowns and reserves special user commands from t
const forceMoveForm = picker.getByTestId('command-argument-form'); const forceMoveForm = picker.getByTestId('command-argument-form');
await expect(forceMoveForm.getByTestId('command-argument-guidance')).toContainText('선택한 도시로 강행합니다.'); await expect(forceMoveForm.getByTestId('command-argument-guidance')).toContainText('선택한 도시로 강행합니다.');
await forceMoveForm.locator('select').selectOption('2'); await forceMoveForm.locator('select').selectOption('2');
await picker.getByRole('button', { name: '입력', exact: true }).click(); await picker.getByRole('button', { name: / 입력$/ }).click();
await expect(editor.locator('.action-column > div').nth(2)).toHaveText('【허창】으로 강행'); await expect(editor.locator('.action-column > div').nth(2)).toHaveText('【허창】으로 강행');
const serialized = JSON.stringify(requests); const serialized = JSON.stringify(requests);
@@ -1669,7 +1669,7 @@ test('keeps general and chief command categories after input and across page rel
await picker.getByRole('button', { name: '군사', exact: true }).click(); await picker.getByRole('button', { name: '군사', exact: true }).click();
await picker.getByRole('button', { name: '출병', exact: true }).click(); await picker.getByRole('button', { name: '출병', exact: true }).click();
await picker.getByTestId('command-argument-form').locator('select').selectOption('3'); await picker.getByTestId('command-argument-form').locator('select').selectOption('3');
await picker.getByRole('button', { name: '입력', exact: true }).click(); await picker.getByRole('button', { name: / 입력$/ }).click();
await expect(generalEditor.locator('.action-column > div').first()).toHaveText('【단양】으로 출병'); await expect(generalEditor.locator('.action-column > div').first()).toHaveText('【단양】으로 출병');
await generalEditor.getByRole('button', { name: '2턴 명령 입력', exact: true }).click(); await generalEditor.getByRole('button', { name: '2턴 명령 입력', exact: true }).click();
@@ -2032,7 +2032,7 @@ test('enters general and nation command arguments and sends exact values', async
const rect = area.getBoundingClientRect(); const rect = area.getBoundingClientRect();
return { width: rect.width, height: rect.height }; return { width: rect.width, height: rect.height };
}); });
await page.getByTestId('command-picker').getByRole('button', { name: '입력', exact: true }).click(); await page.getByTestId('command-picker').getByRole('button', { name: / 입력$/ }).click();
await expect(page.locator('[data-command-scope="general"] .action-column > div').first()).toHaveText( await expect(page.locator('[data-command-scope="general"] .action-column > div').first()).toHaveText(
'【허창】에 화계실행' '【허창】에 화계실행'
); );
@@ -2059,7 +2059,7 @@ test('enters general and nation command arguments and sends exact values', async
fontSize: style.fontSize, fontSize: style.fontSize,
}; };
}); });
await chiefPicker.getByRole('button', { name: '입력', exact: true }).click(); await chiefPicker.getByRole('button', { name: / 입력$/ }).click();
await expect(page.locator('[data-command-scope="nation"] .action-column > div').first()).toHaveText( await expect(page.locator('[data-command-scope="nation"] .action-column > div').first()).toHaveText(
'【여포NPC】 쌀 300 포상' '【여포NPC】 쌀 300 포상'
); );
@@ -2184,7 +2184,7 @@ test('uses a Ref-style full recruitment page without horizontal overflow on desk
await infantry.getByRole('button', { name: '절반', exact: true }).click(); await infantry.getByRole('button', { name: '절반', exact: true }).click();
await expect(estimatedGold).toHaveText('363금'); await expect(estimatedGold).toHaveText('363금');
await page.screenshot({ path: testInfo.outputPath('recruitment-desktop.png') }); await page.screenshot({ path: testInfo.outputPath('recruitment-desktop.png') });
await picker.getByRole('button', { name: '입력', exact: true }).click(); await picker.getByRole('button', { name: / 입력$/ }).click();
await expect(page.locator('[data-command-scope="general"] .action-column > div').first()).toHaveText( await expect(page.locator('[data-command-scope="general"] .action-column > div').first()).toHaveText(
'【보병】 3500명 징병' '【보병】 3500명 징병'
); );
@@ -2307,7 +2307,7 @@ test('uses a Ref-style full recruitment page without horizontal overflow on desk
await unavailableMercenary.click(); await unavailableMercenary.click();
await mercenaryForm.locator('.mobile-selected-panel input[type=number]').fill('25'); await mercenaryForm.locator('.mobile-selected-panel input[type=number]').fill('25');
await expect(mercenaryForm.locator('.mobile-selected-panel .submit-recruit')).toBeEnabled(); await expect(mercenaryForm.locator('.mobile-selected-panel .submit-recruit')).toBeEnabled();
await picker.getByRole('button', { name: '입력', exact: true }).click(); await picker.getByRole('button', { name: / 입력$/ }).click();
await expect(page.locator('[data-command-scope="general"] .action-column > div').nth(1)).toHaveText( await expect(page.locator('[data-command-scope="general"] .action-column > div').nth(1)).toHaveText(
'【정예병】 2500명 모병' '【정예병】 2500명 모병'
); );
@@ -2593,7 +2593,7 @@ test('limits non-aggression end years to the Ref twenty-year window on desktop a
await expect(yearInput).toHaveAttribute('max', '210'); await expect(yearInput).toHaveAttribute('max', '210');
await expect(yearInput).toHaveValue('191'); await expect(yearInput).toHaveValue('191');
const submit = picker.getByRole('button', { name: '입력', exact: true }); const submit = picker.getByRole('button', { name: / 입력$/ });
await yearInput.fill('211'); await yearInput.fill('211');
await expect(submit).toBeDisabled(); await expect(submit).toBeDisabled();
await yearInput.fill('210'); await yearInput.fill('210');
@@ -2881,7 +2881,7 @@ test('offers Ref amount presets and rich, command-specific general lists', async
await expect(generalList.locator('.target-option strong')).toHaveText(['장수 (업)', '관우 (업)', '여포NPC (업)']); await expect(generalList.locator('.target-option strong')).toHaveText(['장수 (업)', '관우 (업)', '여포NPC (업)']);
await generalList.locator('.target-option').filter({ hasText: '여포NPC' }).click(); await generalList.locator('.target-option').filter({ hasText: '여포NPC' }).click();
const awardResponse = page.waitForResponse((response) => response.url().includes('turns.reserved.setNationBulk')); const awardResponse = page.waitForResponse((response) => response.url().includes('turns.reserved.setNationBulk'));
await picker.getByRole('button', { name: '입력', exact: true }).click(); await picker.getByRole('button', { name: / 입력$/ }).click();
await awardResponse; await awardResponse;
await page.goto('/che/chief-center'); await page.goto('/che/chief-center');
@@ -2946,7 +2946,7 @@ test('offers Ref amount presets and rich, command-specific general lists', async
await expect(tupleInputs.nth(0)).toHaveValue('20000'); await expect(tupleInputs.nth(0)).toHaveValue('20000');
await expect(tupleInputs.nth(1)).toHaveValue('1370'); await expect(tupleInputs.nth(1)).toHaveValue('1370');
const aidResponse = page.waitForResponse((response) => response.url().includes('turns.reserved.setNationBulk')); const aidResponse = page.waitForResponse((response) => response.url().includes('turns.reserved.setNationBulk'));
await picker.getByRole('button', { name: '입력', exact: true }).click(); await picker.getByRole('button', { name: / 입력$/ }).click();
await aidResponse; await aidResponse;
const serialized = JSON.stringify(requests); const serialized = JSON.stringify(requests);
@@ -3088,7 +3088,7 @@ for (const width of [1200, 500]) {
); );
await picker.screenshot({ path: testInfo.outputPath('assignment-details.png') }); await picker.screenshot({ path: testInfo.outputPath('assignment-details.png') });
const saved = page.waitForResponse((response) => response.url().includes('turns.reserved.setNationBulk')); const saved = page.waitForResponse((response) => response.url().includes('turns.reserved.setNationBulk'));
await picker.getByRole('button', { name: '입력', exact: true }).click(); await picker.getByRole('button', { name: / 입력$/ }).click();
await saved; await saved;
expect(JSON.stringify(requests)).toContain('"destGeneralId":3'); expect(JSON.stringify(requests)).toContain('"destGeneralId":3');
expect(JSON.stringify(requests)).toContain('"destCityId":2'); expect(JSON.stringify(requests)).toContain('"destCityId":2');
@@ -3163,7 +3163,7 @@ test('keeps the entered command visible and reports a server validation error',
await page.getByRole('button', { name: '1턴 명령 입력', exact: true }).click(); await page.getByRole('button', { name: '1턴 명령 입력', exact: true }).click();
await page.getByTestId('command-picker').getByRole('button', { name: /화계/ }).click(); await page.getByTestId('command-picker').getByRole('button', { name: /화계/ }).click();
await page.getByTestId('command-argument-form').locator('select').selectOption('2'); await page.getByTestId('command-argument-form').locator('select').selectOption('2');
const submit = page.getByTestId('command-picker').getByRole('button', { name: '입력', exact: true }); const submit = page.getByTestId('command-picker').getByRole('button', { name: / 입력$/ });
await submit.click(); await submit.click();
await expect(page.getByRole('alert')).toContainText('대상 도시를 선택할 수 없습니다.'); await expect(page.getByRole('alert')).toContainText('대상 도시를 선택할 수 없습니다.');
@@ -3212,7 +3212,7 @@ test('keeps Ref command briefs and autonomous-action state after a turn mutation
await picker.getByRole('button', { name: '군사', exact: true }).click(); await picker.getByRole('button', { name: '군사', exact: true }).click();
await picker.getByRole('button', { name: '출병', exact: true }).click(); await picker.getByRole('button', { name: '출병', exact: true }).click();
await picker.getByTestId('command-argument-form').locator('select').selectOption('3'); await picker.getByTestId('command-argument-form').locator('select').selectOption('3');
await picker.getByRole('button', { name: '입력', exact: true }).click(); await picker.getByRole('button', { name: / 입력$/ }).click();
await expect(firstRow).toHaveText('【단양】으로 출병'); await expect(firstRow).toHaveText('【단양】으로 출병');
await expect(firstRow).toHaveAttribute('data-autorun-tooltip', / : 200 3 · .*/u); await expect(firstRow).toHaveAttribute('data-autorun-tooltip', / : 200 3 · .*/u);
@@ -3305,7 +3305,7 @@ test('uses drag selection, clipboard paste, and a stored template in advanced mo
await expect(blockedFire).toBeEnabled(); await expect(blockedFire).toBeEnabled();
await blockedFire.click(); await blockedFire.click();
await picker.getByTestId('command-argument-form').locator('select').selectOption('2'); await picker.getByTestId('command-argument-form').locator('select').selectOption('2');
await picker.getByRole('button', { name: '입력', exact: true }).click(); await picker.getByRole('button', { name: / 입력$/ }).click();
await expect(editor.locator('.action-column > div').nth(2)).toHaveText('【허창】에 화계실행'); await expect(editor.locator('.action-column > div').nth(2)).toHaveText('【허창】에 화계실행');
const recentMenu = editor.locator('details').filter({ has: page.getByText('최근 실행', { exact: true }) }); const recentMenu = editor.locator('details').filter({ has: page.getByText('최근 실행', { exact: true }) });
@@ -3495,7 +3495,7 @@ test('keeps the shared main and chief shell geometry and interaction states', as
await chiefArgumentForm.getByRole('button', { name: '쌀' }).click(); await chiefArgumentForm.getByRole('button', { name: '쌀' }).click();
await chiefArgumentForm.locator('input[type=number]').fill('300'); await chiefArgumentForm.locator('input[type=number]').fill('300');
await chiefArgumentForm.locator('select').selectOption('2'); await chiefArgumentForm.locator('select').selectOption('2');
await page.getByTestId('command-picker').getByRole('button', { name: '입력', exact: true }).click(); await page.getByTestId('command-picker').getByRole('button', { name: / 입력$/ }).click();
await expect(page.locator('[data-command-scope="nation"] .action-column > div').first()).toHaveText( await expect(page.locator('[data-command-scope="nation"] .action-column > div').first()).toHaveText(
'【관우】 쌀 300 포상' '【관우】 쌀 300 포상'
); );
@@ -3748,7 +3748,7 @@ for (const width of [1200, 390]) {
await expect(input).toHaveCount(0); await expect(input).toHaveCount(0);
await expect(results.locator('button')).toHaveCount(3); await expect(results.locator('button')).toHaveCount(3);
await expect(form.locator('#command-arg-destCityId')).toHaveValue('2'); await expect(form.locator('#command-arg-destCityId')).toHaveValue('2');
await picker.getByRole('button', { name: '입력', exact: true }).click(); await picker.getByRole('button', { name: / 입력$/ }).click();
await expect.poll(() => JSON.stringify(requests)).toContain('"destCityId":2'); await expect.poll(() => JSON.stringify(requests)).toContain('"destCityId":2');
await page.goto(gamePath('/chief-center')); await page.goto(gamePath('/chief-center'));
@@ -3794,7 +3794,7 @@ for (const width of [1200, 390]) {
expect(geometry.viewport.scale).toBe(initialScale); expect(geometry.viewport.scale).toBe(initialScale);
await writeFile(testInfo.outputPath(`search-${width}.json`), JSON.stringify(geometry, null, 2)); await writeFile(testInfo.outputPath(`search-${width}.json`), JSON.stringify(geometry, null, 2));
await form.screenshot({ path: testInfo.outputPath(`search-${width}.png`) }); await form.screenshot({ path: testInfo.outputPath(`search-${width}.png`) });
await picker.getByRole('button', { name: '입력', exact: true }).click(); await picker.getByRole('button', { name: / 입력$/ }).click();
await expect.poll(() => JSON.stringify(requests)).toContain('"destGeneralId":2,"destCityId":2'); await expect.poll(() => JSON.stringify(requests)).toContain('"destGeneralId":2,"destCityId":2');
await page.reload(); await page.reload();
await page.getByRole('button', { name: '2턴 명령 입력', exact: true }).click(); await page.getByRole('button', { name: '2턴 명령 입력', exact: true }).click();
@@ -3976,8 +3976,20 @@ for (const viewport of [
bar: rect(bar), bar: rect(bar),
buttons: [...bar.querySelectorAll('button')].map((button) => ({ buttons: [...bar.querySelectorAll('button')].map((button) => ({
text: button.textContent?.trim(), text: button.textContent?.trim(),
background: getComputedStyle(button).backgroundColor,
...button.getBoundingClientRect().toJSON(), ...button.getBoundingClientRect().toJSON(),
})), })),
submit: (() => {
const submit = element.querySelector<HTMLButtonElement>('.picker-actions > .command-submit');
if (!submit) return null;
return {
text: submit.textContent?.trim(),
disabled: submit.disabled,
background: getComputedStyle(submit).backgroundColor,
fontWeight: getComputedStyle(submit).fontWeight,
...submit.getBoundingClientRect().toJSON(),
};
})(),
title: bar.querySelector('h2')?.textContent?.replace(/\s+/g, ' ').trim(), title: bar.querySelector('h2')?.textContent?.replace(/\s+/g, ' ').trim(),
map: rect(map), map: rect(map),
fields: rect(fields), fields: rect(fields),
@@ -4003,6 +4015,23 @@ for (const viewport of [
expect(geometry.bar?.y).toBe(0); expect(geometry.bar?.y).toBe(0);
expect(geometry.buttons.map((button) => button.text)).toEqual(['명령 취소', '명령 다시 선택', '검색 꺼짐']); expect(geometry.buttons.map((button) => button.text)).toEqual(['명령 취소', '명령 다시 선택', '검색 꺼짐']);
expect(geometry.title).toBe('화계1턴'); expect(geometry.title).toBe('화계1턴');
// 녹색은 Ref 돌아가기와 같은 이동 버튼(명령 취소)에만 쓰고, 제출은 명령명을 붙인 primary(파랑) 버튼이다.
expect(geometry.buttons.map((button) => button.background)).toEqual([
'rgb(0, 88, 44)',
'rgb(68, 68, 68)',
'rgb(68, 68, 68)',
]);
expect(geometry.submit).toMatchObject({
text: '화계 입력',
disabled: false,
background: 'rgb(55, 90, 127)',
fontWeight: '700',
});
expect(geometry.submit!.width).toBe(Math.min(360, viewport.width - 12));
expect(geometry.submit!.x + geometry.submit!.width / 2).toBeCloseTo(viewport.width / 2, 0);
expect(geometry.submit!.height).toBeGreaterThanOrEqual(40);
expect(geometry.submit!.bottom).toBeLessThanOrEqual(viewport.height);
expect(geometry.submit!.bottom).toBeGreaterThan(viewport.height - 12);
expect(geometry.bodyOverflow).toBe('hidden'); expect(geometry.bodyOverflow).toBe('hidden');
expect(geometry.scrollWidth).toBe(geometry.clientWidth); expect(geometry.scrollWidth).toBe(geometry.clientWidth);
for (const button of geometry.buttons) { for (const button of geometry.buttons) {
@@ -4063,7 +4092,7 @@ for (const viewport of [
await listPicker.getByRole('button', { name: /화계/ }).click(); await listPicker.getByRole('button', { name: /화계/ }).click();
overlay = page.getByRole('dialog', { name: '화계 4턴 명령 입력' }); overlay = page.getByRole('dialog', { name: '화계 4턴 명령 입력' });
await overlay.getByTestId('command-argument-form').locator('select').selectOption('2'); await overlay.getByTestId('command-argument-form').locator('select').selectOption('2');
await overlay.getByRole('button', { name: '입력', exact: true }).click(); await overlay.getByRole('button', { name: / 입력$/ }).click();
await expect(page.getByTestId('command-picker')).toHaveCount(0); await expect(page.getByTestId('command-picker')).toHaveCount(0);
await expect(editor.locator('.action-column > div').nth(3)).toHaveText('【허창】에 화계실행'); await expect(editor.locator('.action-column > div').nth(3)).toHaveText('【허창】에 화계실행');
await expect await expect
@@ -79,7 +79,7 @@ test('reserves every requested general and chief command through Chromium and re
if (fill) { if (fill) {
const form = picker.getByTestId('command-argument-form'); const form = picker.getByTestId('command-argument-form');
await fill(form); await fill(form);
await picker.getByRole('button', { name: '입력', exact: true }).click(); await picker.getByRole('button', { name: / 입력$/ }).click();
} }
await expect(editor.locator('.action-column > div').nth(turn)).toHaveText(command); await expect(editor.locator('.action-column > div').nth(turn)).toHaveText(command);
}; };
@@ -90,7 +90,7 @@ const reserveFirstTurn = async (page: Page, commandName: string, targetName: str
const targetValue = await targetOption.getAttribute('value'); const targetValue = await targetOption.getAttribute('value');
if (!targetValue) throw new Error(`Missing target value for ${targetName}.`); if (!targetValue) throw new Error(`Missing target value for ${targetName}.`);
await picker.locator('select').selectOption(targetValue); await picker.locator('select').selectOption(targetValue);
await picker.getByRole('button', { name: '입력', exact: true }).click(); await picker.getByRole('button', { name: / 입력$/ }).click();
await expect(picker).toBeHidden(); await expect(picker).toBeHidden();
}; };
+2 -2
View File
@@ -5283,7 +5283,7 @@ for (const viewport of [
); );
await picker.screenshot({ path: test.info().outputPath(`equipment-order-${viewport.name}.png`) }); await picker.screenshot({ path: test.info().outputPath(`equipment-order-${viewport.name}.png`) });
const request = page.waitForRequest((entry) => entry.url().includes('turns.reserved.setGeneral')); const request = page.waitForRequest((entry) => entry.url().includes('turns.reserved.setGeneral'));
await picker.getByRole('button', { name: '입력', exact: true }).click(); await picker.getByRole('button', { name: / 입력$/ }).click();
expect(JSON.stringify((await request).postDataJSON())).toContain('"itemCode":"che_훈련_청주"'); expect(JSON.stringify((await request).postDataJSON())).toContain('"itemCode":"che_훈련_청주"');
}); });
@@ -5351,7 +5351,7 @@ for (const viewport of [
await writeFile(test.info().outputPath(`owned-sale-${viewport.name}.json`), JSON.stringify(geometry, null, 2)); await writeFile(test.info().outputPath(`owned-sale-${viewport.name}.json`), JSON.stringify(geometry, null, 2));
await picker.screenshot({ path: test.info().outputPath(`owned-sale-${viewport.name}.png`) }); await picker.screenshot({ path: test.info().outputPath(`owned-sale-${viewport.name}.png`) });
const request = page.waitForRequest((request) => request.url().includes('turns.reserved.setGeneralBulk')); const request = page.waitForRequest((request) => request.url().includes('turns.reserved.setGeneralBulk'));
await picker.getByRole('button', { name: '입력', exact: true }).click(); await picker.getByRole('button', { name: / 입력$/ }).click();
const payload = (await request).postDataJSON(); const payload = (await request).postDataJSON();
expect(JSON.stringify(payload)).toContain('"itemCode":"None"'); expect(JSON.stringify(payload)).toContain('"itemCode":"None"');
expect(JSON.stringify(payload)).toContain('"itemType":"item"'); expect(JSON.stringify(payload)).toContain('"itemType":"item"');
@@ -827,7 +827,7 @@ const clickOutsideMenu = (event: Event) => {
</button> </button>
<button <button
type="button" type="button"
class="legacy-button legacy-button--navigation overlay-reselect" class="legacy-button legacy-button--secondary overlay-reselect"
:disabled="Boolean(pendingReservation)" :disabled="Boolean(pendingReservation)"
@click="returnToCommandList" @click="returnToCommandList"
> >
@@ -896,8 +896,13 @@ const clickOutsideMenu = (event: Event) => {
@click="returnToCommandList" @click="returnToCommandList"
> >
명령 다시 선택</button 명령 다시 선택</button
><button :disabled="!commandArgsValid || Boolean(pendingReservation)" @click="submitCommand"> ><button
{{ pendingReservation ? '저장 ' : '입력' }} type="button"
class="legacy-button legacy-button--primary command-submit"
:disabled="!commandArgsValid || Boolean(pendingReservation)"
@click="submitCommand"
>
{{ pendingReservation ? '저장 중' : `${selectedCommand.name} 입력` }}
</button> </button>
</div> </div>
</template> </template>
@@ -1266,6 +1271,11 @@ small {
.picker-actions button { .picker-actions button {
min-height: 34px; min-height: 34px;
} }
/* 녹색은 Ref TopBackBar `돌아가기`와 같은 이동 버튼 색이다. 제출은 Ref 처리 화면의 primary(파랑) 버튼처럼
명령명을 붙여 이동 버튼과 구분한다. */
.picker-actions > .command-submit {
font-weight: bold;
}
.compact .editor-layout { .compact .editor-layout {
display: flex; display: flex;
@@ -1403,6 +1413,11 @@ small {
border-top: 1px solid #777; border-top: 1px solid #777;
background: #302016 var(--sammo-texture-walnut); background: #302016 var(--sammo-texture-walnut);
} }
.command-picker.argument-overlay .picker-actions > .command-submit {
justify-self: center;
width: min(100%, 360px);
min-height: 40px;
}
/* Ref 1000px 문서 폭을 기본으로 두고, 지도 입력만 원본 지도와 목록 열을 함께 담도록 넓힌다. */ /* Ref 1000px 문서 폭을 기본으로 두고, 지도 입력만 원본 지도와 목록 열을 함께 담도록 넓힌다. */
@media (min-width: 1120px) { @media (min-width: 1120px) {