fix: 도시정보 장수 표시와 폰트를 바로잡음
메인 화면과 같은 Pretendard 14px을 사용하고 장수 이름 목록의 줄바꿈을 허용한다. 실제 예약 턴에만 작은 글씨를 적용해 NPC 장수 표기는 본문 크기로 유지하며 Chromium 회귀 검증을 추가한다.
This commit is contained in:
@@ -160,7 +160,8 @@ const install = async (
|
||||
mode: 'member' | 'wanderer' | 'admin' = 'member',
|
||||
trade: number | null = 100,
|
||||
globalNationCount = 2,
|
||||
mapFixture = map
|
||||
mapFixture = map,
|
||||
denseCurrentCity = false
|
||||
) => {
|
||||
await page.addInitScript((profile) => {
|
||||
localStorage.setItem('sammo-game-token', 'ga_info');
|
||||
@@ -372,8 +373,32 @@ const install = async (
|
||||
crew: 500,
|
||||
train: 90,
|
||||
atmos: 90,
|
||||
turns: ['징병'],
|
||||
turns: denseCurrentCity ? ['징병', '훈련'] : ['징병'],
|
||||
},
|
||||
...(denseCurrentCity
|
||||
? Array.from({ length: 12 }, (_, index) => ({
|
||||
id: index + 2,
|
||||
name: `NPC장수이름이긴${index + 1}`,
|
||||
npcState: 2,
|
||||
picture: null,
|
||||
imageServer: 0,
|
||||
nationId: 1,
|
||||
nationName: '아국',
|
||||
leadership: 60,
|
||||
strength: 60,
|
||||
intelligence: 60,
|
||||
injury: 0,
|
||||
officerLevel: 1,
|
||||
leadershipBonus: 0,
|
||||
defenceTrain: 80,
|
||||
crewTypeId: 1,
|
||||
crewTypeName: '보병',
|
||||
crew: 500,
|
||||
train: 90,
|
||||
atmos: 90,
|
||||
turns: [],
|
||||
}))
|
||||
: []),
|
||||
],
|
||||
forceSummary: {
|
||||
enemyCrew: 0,
|
||||
@@ -407,7 +432,7 @@ test('four legacy menu pages keep the 1000px desktop table contract', async ({ p
|
||||
['nation/info', '.legacy-info-page', '14px', 'Pretendard', 'collapse'],
|
||||
['nation/cities', '.nation-cities-page', '14px', 'Pretendard', 'collapse'],
|
||||
['global-info', '.global-page', '14px', 'Pretendard', 'collapse'],
|
||||
['current-city', '.city-page', '16px', 'Times New Roman', 'separate'],
|
||||
['current-city', '.city-page', '14px', 'Pretendard', 'separate'],
|
||||
] as const) {
|
||||
await go(page, path);
|
||||
await expect(page.locator(selector)).toBeVisible();
|
||||
@@ -774,8 +799,8 @@ test('current-city exposes own general details to a member and admin fixture', a
|
||||
};
|
||||
});
|
||||
expect(legacyGeometry.selector).toMatchObject({ width: 400, height: 19 });
|
||||
expect(legacyGeometry.stats).toEqual({ x: 100, y: 178, width: 1000, height: 136 });
|
||||
expect(legacyGeometry.generals).toMatchObject({ x: 88, y: 332, width: 1024 });
|
||||
expect(legacyGeometry.stats).toEqual({ x: 100, y: 165.375, width: 1000, height: 106.9375 });
|
||||
expect(legacyGeometry.generals).toMatchObject({ x: 88, y: 290.3125, width: 1024 });
|
||||
expect(legacyGeometry.titleAlign).toBe('start');
|
||||
expect(legacyGeometry.icon).toMatchObject({ width: 64, height: 64, naturalWidth: 64, naturalHeight: 64 });
|
||||
if (artifactRoot) {
|
||||
@@ -834,6 +859,67 @@ test('current-city exposes own general details to a member and admin fixture', a
|
||||
}
|
||||
});
|
||||
|
||||
test('current-city wraps dense general names and only shrinks reserved turns', async ({ page }) => {
|
||||
await install(page, 'member', 100, 2, map, true);
|
||||
for (const viewport of [
|
||||
{ name: 'desktop', width: 1200, height: 900 },
|
||||
{ name: 'mobile', width: 500, height: 900 },
|
||||
]) {
|
||||
await page.setViewportSize({ width: viewport.width, height: viewport.height });
|
||||
await go(page, 'current-city');
|
||||
|
||||
const pageStyle = await page.locator('.city-page').evaluate((element) => {
|
||||
const style = getComputedStyle(element);
|
||||
return { fontFamily: style.fontFamily, fontSize: style.fontSize };
|
||||
});
|
||||
expect(pageStyle.fontFamily).toContain('Pretendard');
|
||||
expect(pageStyle.fontSize).toBe('14px');
|
||||
|
||||
const names = page.locator('.general-names');
|
||||
await expect(names).toHaveCSS('white-space', 'normal');
|
||||
const nameLineCount = await names.locator('span').evaluateAll((elements) => {
|
||||
const tops = elements.map((element) => Math.round(element.getBoundingClientRect().top));
|
||||
return new Set(tops).size;
|
||||
});
|
||||
expect(nameLineCount).toBeGreaterThan(1);
|
||||
|
||||
const rows = page.locator('.generals tbody tr');
|
||||
const reservedTurns = rows.nth(0).locator('.turns');
|
||||
const npcTurns = rows.nth(1).locator('.turns');
|
||||
await expect(reservedTurns).toContainText('1 : 징병');
|
||||
await expect(reservedTurns).toContainText('2 : 훈련');
|
||||
await expect(reservedTurns).toHaveClass(/turns--reserved/);
|
||||
await expect(npcTurns).toHaveText('NPC 장수');
|
||||
await expect(npcTurns).not.toHaveClass(/turns--reserved/);
|
||||
|
||||
const turnFontSizes = await Promise.all([
|
||||
reservedTurns.evaluate((element) => getComputedStyle(element).fontSize),
|
||||
npcTurns.evaluate((element) => getComputedStyle(element).fontSize),
|
||||
]);
|
||||
expect(Number.parseFloat(turnFontSizes[0])).toBeLessThan(Number.parseFloat(pageStyle.fontSize));
|
||||
expect(turnFontSizes[1]).toBe(pageStyle.fontSize);
|
||||
|
||||
const reservedLineTops = await reservedTurns
|
||||
.locator('.turn-line')
|
||||
.evaluateAll((elements) => elements.map((element) => Math.round(element.getBoundingClientRect().top)));
|
||||
expect(new Set(reservedLineTops).size).toBe(2);
|
||||
|
||||
if (artifactRoot) {
|
||||
await mkdir(artifactRoot, { recursive: true });
|
||||
await writeFile(
|
||||
resolve(artifactRoot, `core-current-city-dense-${viewport.name}.json`),
|
||||
`${JSON.stringify({ pageStyle, nameLineCount, turnFontSizes, reservedLineTops }, null, 2)}\n`,
|
||||
'utf8'
|
||||
);
|
||||
await page.screenshot({
|
||||
path: resolve(artifactRoot, `core-current-city-dense-${viewport.name}.png`),
|
||||
fullPage: true,
|
||||
animations: 'disabled',
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test('current-city renders a missing merchant rate with the legacy dash and percent text', async ({ page }) => {
|
||||
await install(page, 'member', null);
|
||||
await page.setViewportSize({ width: 1200, height: 900 });
|
||||
|
||||
@@ -299,7 +299,7 @@ const generalImage = (general: General): string => resolveGeneralIconUrl(general
|
||||
<td>{{ general.crew ?? '?' }}</td>
|
||||
<td>{{ general.train ?? '?' }}</td>
|
||||
<td>{{ general.atmos ?? '?' }}</td>
|
||||
<td class="turns">
|
||||
<td class="turns" :class="{ 'turns--reserved': general.turns.length > 0 }">
|
||||
<template v-if="general.turns.length">
|
||||
<span v-for="(turn, index) in general.turns" :key="index" class="turn-line"
|
||||
>{{ index + 1 }} : {{ turn }}</span
|
||||
@@ -321,8 +321,8 @@ const generalImage = (general: General): string => resolveGeneralIconUrl(general
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="legacy-banner">
|
||||
삼국지 모의전투 HiDCHe / KOEI의 이미지를 사용, 응용하였습니다 / 제작 :
|
||||
HideD(hided62@gmail.com) /
|
||||
삼국지 모의전투 HiDCHe / KOEI의 이미지를 사용, 응용하였습니다 / 제작 : HideD(hided62@gmail.com)
|
||||
/
|
||||
<a href="https://sam.hided.net/wiki/hidche/credit" target="_blank" rel="noreferrer">Credit</a>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -335,9 +335,9 @@ const generalImage = (general: General): string => resolveGeneralIconUrl(general
|
||||
.city-page {
|
||||
width: 1000px;
|
||||
margin: 0 auto;
|
||||
font-family: 'Times New Roman', serif;
|
||||
font-size: 16px;
|
||||
line-height: normal;
|
||||
font-family: var(--sammo-font-sans);
|
||||
font-size: 14px;
|
||||
line-height: 1.3;
|
||||
}
|
||||
.legacy-table {
|
||||
width: 100%;
|
||||
@@ -419,6 +419,7 @@ const generalImage = (general: General): string => resolveGeneralIconUrl(general
|
||||
}
|
||||
.general-names {
|
||||
text-align: left !important;
|
||||
white-space: normal !important;
|
||||
}
|
||||
.unknown {
|
||||
color: gray;
|
||||
@@ -447,7 +448,7 @@ const generalImage = (general: General): string => resolveGeneralIconUrl(general
|
||||
height: 64px;
|
||||
object-fit: fill;
|
||||
}
|
||||
.turns {
|
||||
.turns--reserved {
|
||||
font-size: x-small;
|
||||
}
|
||||
.turn-line {
|
||||
|
||||
Reference in New Issue
Block a user