merge: 장수 동향 줄 간격과 임관 월 표기를 반영
This commit is contained in:
@@ -656,17 +656,32 @@ test('메인 카드의 국가·수도·관직·계급·병종은 Ref 출력명
|
||||
await expect(page.locator('.main-page')).not.toContainText('che_');
|
||||
});
|
||||
|
||||
test('메인 개인 기록의 전투 결과는 월 표제와 시각을 한 줄에 표시한다', async ({ page }) => {
|
||||
test('메인 장수 동향과 개인 전투 기록은 모두 21px 행 간격을 유지한다', async ({ page }) => {
|
||||
const state: FixtureState = {
|
||||
permission: 'head',
|
||||
myset: 3,
|
||||
settingMutations: [],
|
||||
accessPages: [],
|
||||
recentRecords: {
|
||||
global: [],
|
||||
general: [
|
||||
global: [
|
||||
{
|
||||
id: 18611,
|
||||
text:
|
||||
'<C>●</>9월:<D><b>위</b></>의 <Y>Administrator</>가 <G><b>낙양</b></>으로 ' +
|
||||
'진격합니다.<span class="hidden_but_copyable">(전투시드: 0123456789abcdef)</span>',
|
||||
},
|
||||
{
|
||||
id: 18610,
|
||||
text: '<C>●</>9월:<Y>Administrator</>가 <D><b>위</b></>에 <S>임관</>했습니다.',
|
||||
},
|
||||
{
|
||||
id: 18609,
|
||||
text: '<C>●</>9월:<Y>뇌동</>의 기병이 퇴각했습니다.',
|
||||
},
|
||||
],
|
||||
general: [
|
||||
{
|
||||
id: 18608,
|
||||
text:
|
||||
'<S>◆</>186년 9월:<div class="small_war_log">' +
|
||||
'<span class="me"><span class="crew_type">귀병</span> ' +
|
||||
@@ -686,6 +701,31 @@ test('메인 개인 기록의 전투 결과는 월 표제와 시각을 한 줄
|
||||
await page.setViewportSize({ width: 1200, height: 900 });
|
||||
await page.goto('');
|
||||
|
||||
const inspectGlobalRhythm = async (selector: string) => {
|
||||
const lines = page.locator(selector);
|
||||
await expect(lines).toHaveCount(3);
|
||||
return lines.evaluateAll((elements) =>
|
||||
elements.map((element) => {
|
||||
const rect = element.getBoundingClientRect();
|
||||
return {
|
||||
top: rect.top,
|
||||
height: rect.height,
|
||||
lineHeight: getComputedStyle(element).lineHeight,
|
||||
};
|
||||
})
|
||||
);
|
||||
};
|
||||
const assertUniformGlobalRhythm = (geometry: Awaited<ReturnType<typeof inspectGlobalRhythm>>) => {
|
||||
expect(geometry.map((line) => line.height)).toEqual([21, 21, 21]);
|
||||
expect(geometry.map((line) => line.lineHeight)).toEqual(['21px', '21px', '21px']);
|
||||
expect(geometry[1]!.top - geometry[0]!.top).toBe(21);
|
||||
expect(geometry[2]!.top - geometry[1]!.top).toBe(21);
|
||||
};
|
||||
|
||||
const desktopGlobalGeometry = await inspectGlobalRhythm('.record-zone [data-record-bucket="global"] .record-line');
|
||||
assertUniformGlobalRhythm(desktopGlobalGeometry);
|
||||
await persistParityArtifact(page, 'core-main-trend-log-rhythm-desktop', desktopGlobalGeometry);
|
||||
|
||||
const expectedText = '◆186년 9월:귀병 【Administrator】 0(-2209) ← 1361(-5539) 기병 【ⓝ뇌동】 12:54';
|
||||
const inspect = async (line: Locator) => {
|
||||
await expect(line).toContainText(expectedText);
|
||||
@@ -723,6 +763,11 @@ test('메인 개인 기록의 전투 결과는 월 표제와 시각을 한 줄
|
||||
page.locator('.record-zone-mobile [data-record-bucket="general"] .record-line').first()
|
||||
);
|
||||
assertSingleLine(mobileGeometry);
|
||||
const mobileGlobalGeometry = await inspectGlobalRhythm(
|
||||
'.record-zone-mobile [data-record-bucket="global"] .record-line'
|
||||
);
|
||||
assertUniformGlobalRhythm(mobileGlobalGeometry);
|
||||
await persistParityArtifact(page, 'core-main-trend-log-rhythm-mobile', mobileGlobalGeometry);
|
||||
await persistParityArtifact(page, 'core-main-personal-battle-log-inline-mobile', mobileGeometry);
|
||||
});
|
||||
|
||||
|
||||
@@ -666,11 +666,21 @@ button {
|
||||
}
|
||||
|
||||
.record-line {
|
||||
box-sizing: border-box;
|
||||
height: 21px;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
overflow-wrap: normal;
|
||||
line-height: 21px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.record-line :deep(.small_war_log) {
|
||||
height: 21px;
|
||||
line-height: 21px;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.record-line :deep(.hidden_but_copyable) {
|
||||
color: transparent !important;
|
||||
font-size: 0;
|
||||
|
||||
@@ -272,7 +272,7 @@ export class ActionDefinition<
|
||||
{
|
||||
scope: LogScope.SYSTEM,
|
||||
category: LogCategory.SUMMARY,
|
||||
format: LogFormat.PLAIN,
|
||||
format: LogFormat.MONTH,
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
@@ -104,7 +104,7 @@ export class ActionDefinition<
|
||||
context.addLog(`<Y>${context.general.name}</>${josaYi} <D><b>${destNationName}</b></>에 <S>임관</>했습니다.`, {
|
||||
scope: LogScope.SYSTEM,
|
||||
category: LogCategory.SUMMARY,
|
||||
format: LogFormat.RAWTEXT,
|
||||
format: LogFormat.MONTH,
|
||||
});
|
||||
|
||||
tryApplyUniqueLottery(context, {
|
||||
|
||||
@@ -183,6 +183,7 @@ export class ActionDefinition<
|
||||
context.addLog(`<Y>${general.name}</>${josaYi} <D><b>${destNation.name}</b></>에 <S>임관</>했습니다.`, {
|
||||
scope: LogScope.SYSTEM,
|
||||
category: LogCategory.SUMMARY,
|
||||
format: LogFormat.MONTH,
|
||||
});
|
||||
|
||||
const initialNationGenLimit = context.initialNationGenLimit ?? 10;
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import type { General, Nation } from '../../../src/domain/entities.js';
|
||||
import type { GeneralActionResolveContext } from '../../../src/actions/engine.js';
|
||||
import { finalizeLogEntry } from '../../../src/logging/entries.js';
|
||||
import { LogCategory, type LogEntryDraft, LogFormat, LogScope } from '../../../src/logging/types.js';
|
||||
import type { TurnCommandEnv } from '../../../src/actions/turn/commandEnv.js';
|
||||
import { ActionDefinition as AppointmentAction } from '../../../src/actions/turn/general/che_임관.js';
|
||||
import { ActionDefinition as RandomAppointmentAction } from '../../../src/actions/turn/general/che_랜덤임관.js';
|
||||
import { ActionDefinition as FollowAppointmentAction } from '../../../src/actions/turn/general/che_장수대상임관.js';
|
||||
|
||||
const actor = {
|
||||
id: 1,
|
||||
name: '검증장수',
|
||||
nationId: 0,
|
||||
cityId: 1,
|
||||
npcState: 0,
|
||||
officerLevel: 1,
|
||||
experience: 100,
|
||||
stats: { leadership: 50, strength: 50, intelligence: 50 },
|
||||
role: {
|
||||
personality: null,
|
||||
specialDomestic: null,
|
||||
specialWar: null,
|
||||
items: { horse: null, weapon: null, book: null, item: null },
|
||||
},
|
||||
meta: {},
|
||||
} as General;
|
||||
|
||||
const nation = {
|
||||
id: 2,
|
||||
name: '검증국',
|
||||
capitalCityId: 2,
|
||||
meta: { gennum: 1 },
|
||||
} as unknown as Nation;
|
||||
|
||||
const createLogSink =
|
||||
(logs: LogEntryDraft[]): GeneralActionResolveContext['addLog'] =>
|
||||
(text, options = {}) => {
|
||||
logs.push({
|
||||
scope: options.scope ?? LogScope.GENERAL,
|
||||
category: options.category ?? LogCategory.ACTION,
|
||||
text,
|
||||
format: options.format ?? LogFormat.MONTH,
|
||||
});
|
||||
};
|
||||
|
||||
const expectMonthlySummary = (logs: LogEntryDraft[]): void => {
|
||||
const summary = logs.find((entry) => entry.scope === LogScope.SYSTEM && entry.category === LogCategory.SUMMARY);
|
||||
expect(summary).toBeDefined();
|
||||
expect(summary?.format).toBe(LogFormat.MONTH);
|
||||
expect(finalizeLogEntry(summary!, { year: 186, month: 9 })?.text).toMatch(/^<C>●<\/>9월:/u);
|
||||
};
|
||||
|
||||
describe('appointment global summary log format', () => {
|
||||
it('adds the legacy month prefix to direct appointment', () => {
|
||||
const logs: LogEntryDraft[] = [];
|
||||
const action = new AppointmentAction({} as TurnCommandEnv);
|
||||
|
||||
action.resolve(
|
||||
{
|
||||
general: actor,
|
||||
destNation: nation,
|
||||
destNationGeneralCount: 1,
|
||||
destCityId: 2,
|
||||
addLog: createLogSink(logs),
|
||||
} as unknown as Parameters<typeof action.resolve>[0],
|
||||
{ destNationId: nation.id }
|
||||
);
|
||||
|
||||
expectMonthlySummary(logs);
|
||||
});
|
||||
|
||||
it('adds the legacy month prefix to random appointment', () => {
|
||||
const action = new RandomAppointmentAction({} as TurnCommandEnv);
|
||||
const result = action.resolve(
|
||||
{
|
||||
general: actor,
|
||||
rng: {
|
||||
nextFloat1: () => 0,
|
||||
nextInt: () => 0,
|
||||
},
|
||||
candidateNations: [
|
||||
{
|
||||
nation,
|
||||
generals: [],
|
||||
generalCount: 1,
|
||||
monarchCityId: 2,
|
||||
monarchAffinity: 0,
|
||||
},
|
||||
],
|
||||
relYear: 1,
|
||||
initialNationGenLimit: 10,
|
||||
historicalNpcAffinityMode: false,
|
||||
} as unknown as Parameters<typeof action.resolve>[0],
|
||||
{}
|
||||
);
|
||||
const logs = result.effects.flatMap((effect) => (effect.type === 'log' ? [effect.entry] : []));
|
||||
|
||||
expectMonthlySummary(logs);
|
||||
});
|
||||
|
||||
it('keeps the same month prefix when following another general', () => {
|
||||
const logs: LogEntryDraft[] = [];
|
||||
const action = new FollowAppointmentAction();
|
||||
|
||||
action.resolve(
|
||||
{
|
||||
general: actor,
|
||||
destNation: nation,
|
||||
destNationGeneralCount: 1,
|
||||
initialNationGenLimit: 10,
|
||||
addLog: createLogSink(logs),
|
||||
} as unknown as Parameters<typeof action.resolve>[0],
|
||||
{ destGeneralID: 9 }
|
||||
);
|
||||
|
||||
expectMonthlySummary(logs);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user