merge: 최신 main 정렬 제어 변경을 갱신 제어 작업에 통합

This commit is contained in:
2026-08-21 04:55:12 +00:00
15 changed files with 874 additions and 142 deletions
+126 -1
View File
@@ -130,6 +130,45 @@ const generals = [
},
];
const npcGenerals = [
{
id: 10,
name: '낮은장수',
ownerName: '',
npcState: 0,
level: 4,
nationId: 2,
nationName: '촉',
personality: null,
specialDomestic: null,
specialWar: null,
statTotal: 120,
leadership: 30,
strength: 50,
intelligence: 40,
experience: 100,
dedication: 50,
},
{
id: 20,
name: '높은장수',
ownerName: '빙의자',
npcState: 1,
level: 8,
nationId: 1,
nationName: '위',
personality: null,
specialDomestic: null,
specialWar: null,
statTotal: 240,
leadership: 90,
strength: 70,
intelligence: 80,
experience: 500,
dedication: 300,
},
];
const parseSort = (route: Route): number => {
try {
const request = route.request();
@@ -224,6 +263,20 @@ const install = async (
sort === 8 ? [...generals].sort((left, right) => left.killturn - right.killturn) : generals;
return response({ sort, generals: rows });
}
if (operation === 'public.getNpcList') {
const sort = parseSort(route);
const rows = [...npcGenerals].sort((left, right) => {
if (sort === 2) return left.nationId - right.nationId || left.id - right.id;
if (sort === 3) return right.statTotal - left.statTotal || left.id - right.id;
if (sort === 4) return right.leadership - left.leadership || left.id - right.id;
if (sort === 5) return right.strength - left.strength || left.id - right.id;
if (sort === 6) return right.intelligence - left.intelligence || left.id - right.id;
if (sort === 7) return right.experience - left.experience || left.id - right.id;
if (sort === 8) return right.dedication - left.dedication || left.id - right.id;
return left.name.localeCompare(right.name) || left.id - right.id;
});
return response({ sort, generals: rows, tokenKeepCounts: {} });
}
return { error: { message: `unhandled ${operation}`, data: { code: 'BAD_REQUEST' } } };
});
await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify(results) });
@@ -340,7 +393,7 @@ test('nation and general directories preserve the fixed legacy Chromium geometry
await expect.poll(() => accessPages).toContain('nation-list');
expect(accessPages).not.toContain('general-list');
const header = page.locator('.general-table thead td').first();
const header = page.locator('.general-table thead th').first();
expect(await header.evaluate((element) => getComputedStyle(element).backgroundImage)).toContain('back_green.jpg');
const icon = page.locator('.general-icon').first();
await expect(icon).toBeVisible();
@@ -392,6 +445,78 @@ test('general directory submits the legacy sort selector and keeps wounded/bonus
expect(await page.locator('#viewType').evaluate((element) => document.activeElement === element)).toBe(true);
});
test('directory sort controls stay legible in dark mode and sortable headers apply the matching option', async ({
page,
}, testInfo) => {
await install(page);
await page.goto('general-list');
const select = page.locator('#viewType');
const submit = page.getByRole('button', { name: '정렬하기' });
const colors = await select.evaluate((element) => {
const selectStyle = getComputedStyle(element);
const optionStyle = getComputedStyle(element.querySelector('option')!);
return {
selectBackground: selectStyle.backgroundColor,
selectColor: selectStyle.color,
optionBackground: optionStyle.backgroundColor,
optionColor: optionStyle.color,
};
});
expect(colors).toEqual({
selectBackground: 'rgb(24, 35, 29)',
selectColor: 'rgb(247, 250, 248)',
optionBackground: 'rgb(24, 35, 29)',
optionColor: 'rgb(247, 250, 248)',
});
const defaultButton = await submit.evaluate((element) => {
const style = getComputedStyle(element);
return {
background: style.backgroundColor,
color: style.color,
borderBottomWidth: style.borderBottomWidth,
cursor: style.cursor,
};
});
expect(defaultButton).toEqual({
background: 'rgb(55, 90, 127)',
color: 'rgb(255, 255, 255)',
borderBottomWidth: '3px',
cursor: 'pointer',
});
await submit.hover();
await page.mouse.down();
expect(await submit.evaluate((element) => getComputedStyle(element).borderBottomWidth)).toBe('1px');
await page.mouse.up();
await page.getByRole('button', { name: '삭턴 기준 정렬' }).click();
await expect(select).toHaveValue('8');
await expect(page.locator('th[aria-sort="ascending"]')).toContainText('삭턴');
await expect(page.locator('tbody tr[data-general-id]').first()).toHaveAttribute('data-general-id', '20');
await page.screenshot({ path: testInfo.outputPath('directory-sort-controls-desktop.png'), fullPage: true });
await page.setViewportSize({ width: 500, height: 844 });
await expect(select).toHaveCSS('background-color', 'rgb(24, 35, 29)');
await expect(submit).toHaveCSS('border-bottom-width', '3px');
await page.screenshot({ path: testInfo.outputPath('directory-sort-controls-mobile.png'), fullPage: true });
});
test('npc directory reuses the dark sort controls and sorts from a table header', async ({ page }, testInfo) => {
await install(page);
await page.goto('npc-list');
await expect(page.locator('.npc-table tbody tr[data-general-id]')).toHaveCount(2);
await page.getByRole('button', { name: '통솔 기준 정렬' }).click();
await expect(page.locator('#npc-list-sort')).toHaveValue('4');
await expect(page.locator('.npc-table th[aria-sort="descending"]')).toContainText('통솔');
await expect(page.locator('.npc-table tbody tr[data-general-id]').first()).toHaveAttribute('data-general-id', '20');
await expect(page.locator('#npc-list-sort')).toHaveCSS('background-color', 'rgb(24, 35, 29)');
await expect(page.getByRole('button', { name: '정렬하기' })).toHaveCSS('background-color', 'rgb(55, 90, 127)');
await page.screenshot({ path: testInfo.outputPath('npc-sort-controls-desktop.png'), fullPage: true });
await page.setViewportSize({ width: 500, height: 844 });
await expect(page.locator('#npc-list-sort')).toHaveCSS('color', 'rgb(247, 250, 248)');
await page.screenshot({ path: testInfo.outputPath('npc-sort-controls-mobile.png'), fullPage: true });
});
test('nation directory reuses only the public general-directory row on hover and keyboard focus', async ({ page }) => {
const requestedOperations: string[] = [];
await install(page, 'general', [], requestedOperations);
@@ -353,6 +353,15 @@ test('암행부 행을 도시별로 나누고 수뇌의 인사부 즉시 임명
await expect(page.locator('.city-user-table')).toHaveCount(0);
await expect(page.getByRole('button', { name: '인사부 연동' })).toHaveCount(0);
const citySort = page.locator('#nation-city-sort');
await expect(citySort).toHaveCSS('background-color', 'rgb(24, 35, 29)');
await citySort.selectOption('5');
await page.getByRole('button', { name: '정렬하기' }).click();
await expect(page.locator('.city th[aria-sort="descending"]').first()).toContainText('농업');
await page.getByRole('button', { name: '시세 기준 정렬' }).first().click();
await expect(citySort).toHaveValue('10');
await expect(page.locator('.city th[aria-sort="descending"]').first()).toContainText('시세');
await page.getByRole('button', { name: '암행부 연동' }).click();
await expect(page.locator('.city-user-table')).toHaveCount(2);
await expect(page.locator('.city[data-city-id="1"] .city-user-table tr[data-general-id="21"]')).toContainText(
@@ -149,6 +149,30 @@ const install = async (page: Page, secretAllowed = true) => {
{ action: '휴식', args: {} },
],
},
{
id: 2,
name: '부유장수',
npcState: 0,
injury: 0,
stats: { leadership: 60, strength: 50, intelligence: 40 },
leadershipBonus: 0,
experienceLevel: 8,
troopId: 1,
troopName: '제1부대',
gold: 3000,
rice: 1000,
cityId: 2,
cityName: '낙양',
defenceTrain: 80,
defenceTrainText: '◎',
crewTypeId: 2,
crew: 100,
train: 80,
atmos: 80,
killTurn: 3,
turnTime: '2026-01-01T02:02:00.000Z',
reservedCommands: [],
},
],
});
}
@@ -381,7 +405,7 @@ test('secret office renders five Ref-style command briefs and the forbidden erro
'5 : 휴식',
]);
await expect(commandRows.nth(2)).toHaveAttribute('title', '【다른장수】에게 쌀 200을 증여');
const geometry = await page.locator('#secret-general-list .turns').evaluate((element) => {
const geometry = await page.locator('#secret-general-list .turns').first().evaluate((element) => {
const rect = element.getBoundingClientRect();
const style = getComputedStyle(element);
return {
@@ -416,3 +440,22 @@ test('secret office renders five Ref-style command briefs and the forbidden erro
await expect(page.getByRole('alert')).toContainText('권한이 부족합니다.');
await expect(page.locator('#secret-general-list')).toHaveCount(0);
});
test('secret office applies the selected sort on submit and immediately from sortable headers', async ({ page }) => {
await install(page);
await page.goto('nation/secret');
const rows = page.locator('#secret-general-list tbody tr[data-general-id]');
await expect(rows.first()).toHaveAttribute('data-general-id', '1');
await page.locator('#secret-list-sort').selectOption('1');
await expect(rows.first()).toHaveAttribute('data-general-id', '1');
await page.getByRole('button', { name: '정렬하기' }).click();
await expect(rows.first()).toHaveAttribute('data-general-id', '2');
await expect(page.locator('#secret-general-list th[aria-sort="descending"]')).toContainText('자 금');
await page.getByRole('button', { name: '도시 기준 정렬' }).click();
await expect(page.locator('#secret-list-sort')).toHaveValue('3');
await expect(rows.first()).toHaveAttribute('data-general-id', '1');
await expect(page.locator('#secret-list-sort')).toHaveCSS('color', 'rgb(247, 250, 248)');
await expect(page.getByRole('button', { name: '정렬하기' })).toHaveCSS('border-bottom-width', '3px');
});