인게임 예약 명령과 장수 상태 표시를 바로잡음

This commit is contained in:
2026-09-04 19:16:56 +00:00
parent 5d220de739
commit 0157ff6a6b
31 changed files with 511 additions and 81 deletions
+30 -7
View File
@@ -1112,7 +1112,15 @@ test('defaults founding to a Ref-selectable nation trait and opens colored optio
possible: true,
status: 'needsInput',
inputFields: [
{ key: 'nationName', label: '국가명', kind: 'text', required: true, min: 1, max: 18 },
{
key: 'nationName',
label: '국가명',
kind: 'text',
required: true,
min: 1,
max: 18,
legacyWidthMax: 18,
},
{
key: 'nationType',
label: '국가 성향',
@@ -1182,6 +1190,9 @@ test('defaults founding to a Ref-selectable nation trait and opens colored optio
const mobilePicker = page.getByTestId('command-picker');
await mobilePicker.getByRole('button', { name: '국가', exact: true }).click();
await mobilePicker.getByRole('button', { name: '건국', exact: true }).click();
await mobilePicker.getByLabel('국가명').fill('가나다라마바사아자차');
await expect(mobilePicker.getByText('국가명은 전각 9자 또는 반각 18자 이하여야 합니다.')).toBeVisible();
await expect(mobilePicker.getByRole('button', { name: '입력', exact: true })).toBeDisabled();
await mobilePicker.getByLabel('국가명').fill('신국');
const mobileColorType = mobilePicker.getByLabel('국기 색상');
await mobileColorType.click();
@@ -1814,9 +1825,9 @@ test('enters general and nation command arguments and sends exact values', async
await chiefForm.locator('input[type=number]').fill('300');
const chiefTarget = chiefForm.locator('#command-arg-destGeneralId');
await expect(chiefTarget.locator('option')).toHaveText([
'장수 (아국 · 업)',
'여포NPC (아국 · 업)',
'관우 (아국 · 업)',
'장수 (업)',
'관우 (업)',
'여포NPC (업)',
]);
await chiefTarget.selectOption('3');
const geometry = await chiefForm.evaluate((element) => {
@@ -1905,9 +1916,9 @@ test('uses a Ref-style full recruitment page without horizontal overflow on desk
await expect(form).toContainText('가격');
await expect(form).toContainText('군량');
await expect(form).toContainText('표준적인 보병입니다.');
await expect(form.getByRole('button', { name: '정예병 선택 불가', exact: true })).toHaveCount(0);
await expect(form.getByRole('button', { name: '정예병 현재 실행 불가, 예약 가능', exact: true })).toHaveCount(0);
await form.getByRole('button', { name: '선택 할 수 없는 병종도 보기', exact: true }).click();
const unavailable = form.getByRole('button', { name: '정예병 선택 불가', exact: true });
const unavailable = form.getByRole('button', { name: '정예병 현재 실행 불가, 예약 가능', exact: true });
await expect(unavailable).toBeVisible();
await expect(unavailable.locator('.crew-name')).toHaveCSS('background-color', 'rgb(201, 0, 0)');
await expect(unavailable.locator('.crew-info')).toHaveText(
@@ -2069,7 +2080,19 @@ test('uses a Ref-style full recruitment page without horizontal overflow on desk
const mercenaryForm = picker.getByTestId('recruitment-command-form');
await expect(mercenaryForm).toContainText('모병은 가격 2배의 자금이 소요됩니다.');
await expect(mercenaryForm.locator('.mobile-selected-panel output')).toHaveText('1,346금');
await page.keyboard.press('Escape');
await mercenaryForm.getByRole('button', { name: '선택 할 수 없는 병종도 보기', exact: true }).click();
const unavailableMercenary = mercenaryForm.getByRole('button', {
name: '정예병 현재 실행 불가, 예약 가능',
exact: true,
});
await unavailableMercenary.click();
await mercenaryForm.locator('.mobile-selected-panel input[type=number]').fill('25');
await expect(mercenaryForm.locator('.mobile-selected-panel .submit-recruit')).toBeEnabled();
await picker.getByRole('button', { name: '입력', exact: true }).click();
await expect(page.locator('[data-command-scope="general"] .action-column > div').nth(1)).toHaveText(
'【정예병】 2500명 모병'
);
expect(JSON.stringify(requests)).toContain('"crewType":1101');
await expect(picker).toHaveCount(0);
await expect.poll(() => page.evaluate(() => getComputedStyle(document.body).overflow)).not.toBe('hidden');
});
+9 -6
View File
@@ -1396,7 +1396,7 @@ test('the private-message notice moves a mobile reader to the private section',
.poll(() =>
page.locator('.PrivateTalk > .stickyAnchor').evaluate((element) => element.getBoundingClientRect().top)
)
.toBeLessThan(80);
.toBeLessThan(400);
expect(await page.evaluate(() => window.scrollY)).toBeGreaterThan(500);
});
@@ -2917,8 +2917,10 @@ test('main cards and command input stay inside their Ref-sized grid slots', asyn
await expect(cityBars).toHaveCount(8);
await expect(statBars).toHaveCount(3);
await expect(experienceBar).toHaveCount(1);
await expect(page.locator('[data-main-target="general"] [data-dex-progress]')).toHaveCount(0);
await expect(page.locator('[data-main-target="general"] [role="progressbar"]')).toHaveCount(4);
await expect(page.locator('[data-main-target="general"] [data-general-information-panel]')).toHaveCount(1);
await expect(page.locator('[data-main-target="general"] [data-general-battle-summary]')).toHaveCount(1);
await expect(page.locator('[data-main-target="general"] [data-dex-progress]')).toHaveCount(5);
await expect(page.locator('[data-main-target="general"] [role="progressbar"]')).toHaveCount(14);
const nationCard = page.locator('[data-main-target="nation"] [data-nation-basic-card]');
await expect(nationCard.locator('.head')).toHaveCount(17);
@@ -3288,7 +3290,7 @@ test('main cards and command input stay inside their Ref-sized grid slots', asyn
await page.locator('[data-main-target="commands"] .bottom-actions').getByRole('button', { name: '펼치기' }).click();
expect(await page.evaluate(() => document.documentElement.scrollWidth)).toBeLessThanOrEqual(500);
await expect(page.locator('[data-main-target="city"] [role="progressbar"]')).toHaveCount(8);
await expect(page.locator('[data-main-target="general"] [role="progressbar"]')).toHaveCount(4);
await expect(page.locator('[data-main-target="general"] [role="progressbar"]')).toHaveCount(14);
const mobileNationCard = page.locator('[data-main-target="nation"] [data-nation-basic-card]');
expect(await mobileNationCard.evaluate((element) => element.getBoundingClientRect().height)).toBe(193);
expect(await mobileNationCard.evaluate((element) => element.scrollWidth - element.clientWidth)).toBeLessThanOrEqual(
@@ -4796,10 +4798,11 @@ for (const viewport of [
await picker.getByRole('button', { name: '건국', exact: true }).click();
await picker.getByLabel('국가명').fill('초안보존국');
await picker.getByLabel('국기 색상').selectOption('15');
await picker.getByLabel('국기 색상').click();
await picker.getByRole('option', { name: '색상 16', exact: true }).click();
await refreshActivityAndCommands();
await expect(picker.getByLabel('국가명')).toHaveValue('초안보존국');
await expect(picker.getByLabel('국기 색상')).toHaveValue('15');
await expect(picker.getByLabel('국기 색상')).toContainText('색상 16');
await expect(picker.getByLabel('국기 색상')).toHaveCSS('background-color', 'rgb(100, 149, 237)');
await picker.screenshot({ path: test.info().outputPath(`command-draft-${viewport.name}.png`) });
@@ -134,6 +134,7 @@ const npcColorSecretGenerals = npcColorStates.map((npcState) => ({
name: `색상장수${npcState}`,
npcState,
injury: 0,
baseStats: { leadership: 70, strength: 60, intelligence: 50 },
stats: { leadership: 70, strength: 60, intelligence: 50 },
leadershipBonus: 0,
experienceLevel: 9,
@@ -205,8 +206,9 @@ const install = async (page: Page, secretAllowed = true, npcColorFixture = false
id: 1,
name: '테스트장수',
npcState: 0,
injury: 0,
stats: { leadership: 70, strength: 60, intelligence: 50 },
injury: 30,
baseStats: { leadership: 100, strength: 80, intelligence: 60 },
stats: { leadership: 70, strength: 56, intelligence: 42 },
leadershipBonus: 0,
experienceLevel: 9,
troopId: 0,
@@ -236,6 +238,7 @@ const install = async (page: Page, secretAllowed = true, npcColorFixture = false
name: '부유장수',
npcState: 0,
injury: 0,
baseStats: { leadership: 60, strength: 50, intelligence: 40 },
stats: { leadership: 60, strength: 50, intelligence: 40 },
leadershipBonus: 0,
experienceLevel: 8,
@@ -644,6 +647,12 @@ test('secret office renders five Ref-style command briefs and the forbidden erro
'5 : 휴식',
]);
await expect(commandRows.nth(2)).toHaveAttribute('title', '【다른장수】에게 쌀 200을 증여');
const woundedName = page.locator('[data-directory-tooltip="secret-injury-name-1"]');
const woundedLeadership = page.locator('[data-directory-tooltip="secret-injury-leadership-1"]');
await expect(woundedName.locator('[data-general-name]')).toHaveCSS('color', 'rgb(255, 165, 0)');
await expect(woundedLeadership.locator('span').first()).toHaveCSS('color', 'rgb(255, 165, 0)');
await woundedLeadership.hover();
await expect(woundedLeadership.getByRole('tooltip')).toContainText('부상 30% · 중상 · 원래 통솔 100 → 적용 70');
const geometry = await page
.locator('#secret-general-list .turns')
.first()
+10 -2
View File
@@ -17,7 +17,7 @@ type FixtureState = {
const candidates = Array.from({ length: 5 }, (_, index) => ({
id: index + 1,
name: `빙의후보${index + 1}`,
name: index === 0 ? '아주긴빙의후보장수이름' : `빙의후보${index + 1}`,
nation: { id: 0, name: '재야', color: '#aaaaaa' },
stats: {
leadership: 40 + index,
@@ -336,15 +336,23 @@ test('renders Ref-shaped token cards, preserves keep cooldown and retries posses
return {
sectionWidth: sectionRect.width,
cardWidths: cards.map((card) => card.getBoundingClientRect().width),
nameHeights: cards.map(
(card) => card.querySelector<HTMLElement>('.npc-card-name')!.getBoundingClientRect().height
),
nameWhiteSpace: getComputedStyle(cards[0]!.querySelector<HTMLElement>('.npc-card-name')!).whiteSpace,
longNameFontSize: getComputedStyle(cards[0]!.querySelector<HTMLElement>('.npc-card-name')!).fontSize,
imageWidth: image?.getBoundingClientRect().width,
imageHeight: image?.getBoundingClientRect().height,
imageNaturalWidth: image?.naturalWidth,
imageNaturalHeight: image?.naturalHeight,
};
});
expect(geometry).toEqual({
expect(geometry).toMatchObject({
sectionWidth: 1000,
cardWidths: [125, 125, 125, 125, 125],
nameHeights: [25, 25, 25, 25, 25],
nameWhiteSpace: 'nowrap',
longNameFontSize: '12px',
imageWidth: 64,
imageHeight: 64,
imageNaturalWidth: 64,