merge: 최신 로컬 main을 건국 색상 라벨 브랜치에 통합

This commit is contained in:
2026-08-19 11:33:24 +00:00
22 changed files with 1342 additions and 1897 deletions
@@ -1161,6 +1161,80 @@ test('shows every Ref chief command in the exact category and command order', as
await mobilePicker.screenshot({ path: test.info().outputPath('ref-chief-command-list-mobile-500.png') });
});
test('keeps general and chief command categories after input and across page reloads', async ({ page, context }) => {
await install(page);
await page.setViewportSize({ width: 1200, height: 900 });
await page.goto('/');
const generalEditor = page.locator('[data-command-scope="general"]');
await generalEditor.getByRole('button', { name: '1턴 명령 입력', exact: true }).click();
let picker = page.getByTestId('command-picker');
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.getByRole('button', { name: '입력', exact: true }).click();
await expect(generalEditor.locator('.action-column > div').first()).toHaveText('【단양】으로 출병');
await generalEditor.getByRole('button', { name: '2턴 명령 입력', exact: true }).click();
picker = page.getByTestId('command-picker');
await expect(picker.getByRole('button', { name: '군사', exact: true })).toHaveClass(/active/);
await expect(picker.getByRole('button', { name: '출병', exact: true })).toBeVisible();
await picker.screenshot({ path: test.info().outputPath('general-category-after-input-desktop-1200.png') });
await picker.getByRole('button', { name: '명령 입력 닫기', exact: true }).click();
await expect
.poll(() => page.evaluate(() => localStorage.getItem('core2026:general:1:category')))
.toBe(JSON.stringify('general:군사'));
const reloadedGeneralPage = await context.newPage();
await install(reloadedGeneralPage);
await reloadedGeneralPage.goto('/');
const reloadedGeneralEditor = reloadedGeneralPage.locator('[data-command-scope="general"]');
await reloadedGeneralEditor.getByRole('button', { name: '1턴 명령 입력', exact: true }).click();
const reloadedGeneralPicker = reloadedGeneralPage.getByTestId('command-picker');
await expect(reloadedGeneralPicker.getByRole('button', { name: '군사', exact: true })).toHaveClass(/active/);
await expect(reloadedGeneralPicker.getByRole('button', { name: '출병', exact: true })).toBeVisible();
await reloadedGeneralPicker.screenshot({
path: test.info().outputPath('general-category-after-reload-desktop-1200.png'),
});
await reloadedGeneralPage.close();
const chiefPage = await context.newPage();
await install(chiefPage, false, refChiefCommandTable);
await chiefPage.setViewportSize({ width: 500, height: 900 });
await chiefPage.goto('/che/chief-center');
const chiefEditor = chiefPage.locator('[data-command-scope="nation"]');
await chiefEditor.getByRole('button', { name: '1턴 명령 입력', exact: true }).click();
picker = chiefPage.getByTestId('command-picker');
await picker.getByRole('button', { name: '전략', exact: true }).click();
await picker.getByRole('button', { name: '필사즉생', exact: true }).click();
await expect(chiefEditor.locator('.action-column > div').first()).toHaveText('필사즉생');
await chiefEditor.getByRole('button', { name: '2턴 명령 입력', exact: true }).click();
picker = chiefPage.getByTestId('command-picker');
await expect(picker.getByRole('button', { name: '전략', exact: true })).toHaveClass(/active/);
await expect(picker.getByRole('button', { name: '필사즉생', exact: true })).toBeVisible();
await picker.screenshot({ path: test.info().outputPath('chief-category-after-input-mobile-500.png') });
await picker.getByRole('button', { name: '명령 입력 닫기', exact: true }).click();
await expect
.poll(() => chiefPage.evaluate(() => localStorage.getItem('core2026:nation:1:5:category')))
.toBe(JSON.stringify('nation:전략'));
await chiefPage.close();
const reloadedChiefPage = await context.newPage();
await install(reloadedChiefPage, false, refChiefCommandTable);
await reloadedChiefPage.setViewportSize({ width: 500, height: 900 });
await reloadedChiefPage.goto('/che/chief-center');
const reloadedChiefEditor = reloadedChiefPage.locator('[data-command-scope="nation"]');
await reloadedChiefEditor.getByRole('button', { name: '1턴 명령 입력', exact: true }).click();
const reloadedChiefPicker = reloadedChiefPage.getByTestId('command-picker');
await expect(reloadedChiefPicker.getByRole('button', { name: '전략', exact: true })).toHaveClass(/active/);
await expect(reloadedChiefPicker.getByRole('button', { name: '필사즉생', exact: true })).toBeVisible();
await expect.poll(() => reloadedChiefPicker.evaluate((element) => element.getBoundingClientRect().height)).toBeGreaterThan(200);
await reloadedChiefPicker.screenshot({
path: test.info().outputPath('chief-category-after-reload-mobile-500.png'),
});
});
test('shows all 12 advanced chief turns before the actions and uses the full mobile chief matrix', async ({ page }) => {
await install(page);
await page.setViewportSize({ width: 500, height: 900 });
@@ -73,7 +73,7 @@ const categories = computed(() => {
return [...general, ...nation];
});
const selectedCategory = ref('');
const selectedCategory = ref(props.activeCategory ?? '');
const selectedGroup = computed(() => {
if (!props.commandTable) {
return null;
@@ -89,9 +89,7 @@ const selectedGroup = computed(() => {
watch(
() => props.activeCategory,
(value) => {
if (value) {
selectedCategory.value = value;
}
selectedCategory.value = value ?? '';
}
);
@@ -109,11 +107,15 @@ watch(
{ immediate: true }
);
watch(selectedCategory, (value) => {
if (value) {
emit('update:activeCategory', value);
}
});
watch(
selectedCategory,
(value) => {
if (value) {
emit('update:activeCategory', value);
}
},
{ immediate: true }
);
const commandTitle = (command: CommandAvailability) =>
command.reason || (command.reqArg ? '대상을 선택하는 명령입니다.' : command.possible ? '실행 가능' : '실행 불가');
@@ -35,6 +35,8 @@ const nationCommands = [
'che_물자원조',
];
const capitalCommands = ['che_증축', 'che_감축'];
const otherArgumentCommands = [
'che_증여',
'che_헌납',
@@ -58,7 +60,7 @@ const otherArgumentCommands = [
];
void test('provides Ref-level guidance for every in-scope argument command', () => {
const expected = [...cityCommands, ...nationCommands, ...otherArgumentCommands].sort();
const expected = [...cityCommands, ...nationCommands, ...capitalCommands, ...otherArgumentCommands].sort();
assert.deepEqual(presentedCommandKeys().sort(), expected);
for (const commandKey of expected) {
assert.ok(commandArgumentPresentation(commandKey).lines.join(' ').length >= 12, commandKey);
@@ -76,4 +78,7 @@ 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);
}
});
@@ -104,7 +104,7 @@ const table: CommandTable = {
},
};
test('Ref getBrief를 상속하는 출병·계략·모병까지 실제 인자 요약으로 표시한다', () => {
void test('Ref getBrief를 상속하는 출병·계략·모병까지 실제 인자 요약으로 표시한다', () => {
assert.equal(formatReservedCommandBrief('general', 'che_출병', { destCityId: 2 }, table), '【단양】으로 출병');
assert.equal(formatReservedCommandBrief('general', 'che_화계', { destCityId: 3 }, table), '【업】에 화계실행');
assert.equal(formatReservedCommandBrief('general', 'che_선동', { destCityId: 2 }, table), '【단양】에 선동실행');
@@ -114,7 +114,7 @@ test('Ref getBrief를 상속하는 출병·계략·모병까지 실제 인자
);
});
test('개인·인사·국가 명령의 Ref brief 변형을 보존한다', () => {
void test('개인·인사·국가 명령의 Ref brief 변형을 보존한다', () => {
const cases: Array<[string, Record<string, unknown>, string]> = [
['che_이동', { destCityId: 3 }, '【업】으로 이동'],
['che_강행', { destCityId: 2 }, '【단양】으로 강행'],
@@ -139,7 +139,7 @@ test('개인·인사·국가 명령의 Ref brief 변형을 보존한다', () =>
}
});
test('국가 명령의 도시·국가·장수·자원 인자를 Ref brief로 표시한다', () => {
void test('국가 명령의 도시·국가·장수·자원 인자를 Ref brief로 표시한다', () => {
const cases: Array<[string, Record<string, unknown>, string]> = [
['che_발령', { destGeneralId: 8, destCityId: 3 }, '【손권】【업】으로 발령'],
['che_부대탈퇴지시', { destGeneralId: 8 }, '【손권】부대 탈퇴 지시'],
@@ -177,7 +177,7 @@ test('국가 명령의 도시·국가·장수·자원 인자를 Ref brief로 표
);
});
test('Ref가 getBrief를 재정의하지 않은 명령은 실제 표시명을 유지한다', () => {
void test('Ref가 getBrief를 재정의하지 않은 명령은 실제 표시명을 유지한다', () => {
assert.equal(formatReservedCommandBrief('general', '휴식', {}, table), '휴식');
assert.equal(formatReservedCommandBrief('general', 'che_훈련', {}, table), '훈련');
assert.equal(formatReservedCommandBrief('nation', 'che_필사즉생', {}, table), '필사즉생');