merge: 전투시드 숨김 표시 일관화 통합
This commit is contained in:
@@ -63,6 +63,7 @@ type FixtureState = {
|
||||
joinConfig?: Record<string, unknown>;
|
||||
createGeneralInputs?: Array<Record<string, unknown>>;
|
||||
mainTraits?: { personal: string; specialDomestic: string; specialWar: string };
|
||||
hiddenSeedLogText?: string;
|
||||
recentRecords?: {
|
||||
global: Array<{ id: number; text: string; createdAt?: string }>;
|
||||
general: Array<{ id: number; text: string; createdAt?: string }>;
|
||||
@@ -484,7 +485,10 @@ const install = async (page: Page, state: FixtureState) => {
|
||||
});
|
||||
if (operation === 'general.getMyLog') {
|
||||
state.generalLogQueries = (state.generalLogQueries ?? 0) + 1;
|
||||
return response({ type: 'generalAction', logs: [{ id: 1, text: '<Y>기록</>' }] });
|
||||
return response({
|
||||
type: 'generalAction',
|
||||
logs: [{ id: 1, text: state.hiddenSeedLogText ?? '<Y>기록</>' }],
|
||||
});
|
||||
}
|
||||
if (operation === 'general.instantRetreat') {
|
||||
state.instantRetreatInputs?.push(jsonInput);
|
||||
@@ -560,7 +564,11 @@ const install = async (page: Page, state: FixtureState) => {
|
||||
['generalHistory', 'battleDetail', 'battleResult', 'generalAction'].includes(jsonInput.type)
|
||||
? jsonInput.type
|
||||
: 'generalAction';
|
||||
return response({ type, generalId: 7, logs: [{ id: 1, text: `<Y>${type} 감찰 기록</>` }] });
|
||||
return response({
|
||||
type,
|
||||
generalId: 7,
|
||||
logs: [{ id: 1, text: state.hiddenSeedLogText ?? `<Y>${type} 감찰 기록</>` }],
|
||||
});
|
||||
}
|
||||
return response({ ok: true });
|
||||
});
|
||||
@@ -819,6 +827,80 @@ test('메인 장수 동향과 개인 전투 기록은 Ref 행 간격·색상·
|
||||
await persistParityArtifact(page, 'core-main-personal-battle-log-inline-mobile', mobileGeometry);
|
||||
});
|
||||
|
||||
test('전투시드는 메인·내 정보·감찰부에서 숨긴 채 선택할 수 있다', async ({ page }) => {
|
||||
const seedText = '(전투시드: 0123456789abcdef)';
|
||||
const logText =
|
||||
'<D><b>위</b></>의 <Y>검증장수</>가 <G><b>낙양</b></>으로 ' +
|
||||
`진격합니다.<span class="hidden_but_copyable">${seedText}</span>`;
|
||||
const state: FixtureState = {
|
||||
permission: 'head',
|
||||
myset: 3,
|
||||
settingMutations: [],
|
||||
accessPages: [],
|
||||
hiddenSeedLogText: logText,
|
||||
recentRecords: {
|
||||
global: [{ id: 18611, text: logText }],
|
||||
general: [],
|
||||
history: [],
|
||||
},
|
||||
};
|
||||
await install(page, state);
|
||||
|
||||
const inspectHiddenSeed = async (selector: string) => {
|
||||
const seed = page.locator(selector);
|
||||
await expect(seed).toHaveCount(1);
|
||||
return seed.evaluate((element) => {
|
||||
const range = document.createRange();
|
||||
range.selectNodeContents(element);
|
||||
const selection = window.getSelection();
|
||||
selection?.removeAllRanges();
|
||||
selection?.addRange(range);
|
||||
const style = getComputedStyle(element);
|
||||
const rect = element.getBoundingClientRect();
|
||||
const result = {
|
||||
text: element.textContent,
|
||||
selectedText: selection?.toString(),
|
||||
color: style.color,
|
||||
fontSize: style.fontSize,
|
||||
width: rect.width,
|
||||
height: rect.height,
|
||||
};
|
||||
selection?.removeAllRanges();
|
||||
return result;
|
||||
});
|
||||
};
|
||||
const assertHiddenSeed = (result: Awaited<ReturnType<typeof inspectHiddenSeed>>) => {
|
||||
expect(result.text).toBe(seedText);
|
||||
expect(result.selectedText).toBe(seedText);
|
||||
expect(result.color).toBe('rgba(0, 0, 0, 0)');
|
||||
expect(result.fontSize).toBe('0px');
|
||||
expect(result.width).toBe(0);
|
||||
expect(result.height).toBe(0);
|
||||
};
|
||||
|
||||
for (const viewport of [
|
||||
{ width: 1000, height: 900 },
|
||||
{ width: 500, height: 900 },
|
||||
]) {
|
||||
await page.setViewportSize(viewport);
|
||||
|
||||
await page.goto('');
|
||||
const mainSeed = await inspectHiddenSeed('[data-record-bucket="global"] .hidden_but_copyable');
|
||||
assertHiddenSeed(mainSeed);
|
||||
await persistParityArtifact(page, `core-hidden-battle-seed-main-${viewport.width}`, mainSeed);
|
||||
|
||||
await page.goto('my-page');
|
||||
const myPageSeed = await inspectHiddenSeed('.log-panel:first-child .hidden_but_copyable');
|
||||
assertHiddenSeed(myPageSeed);
|
||||
await persistParityArtifact(page, `core-hidden-battle-seed-my-page-${viewport.width}`, myPageSeed);
|
||||
|
||||
await page.goto('battle-center');
|
||||
const battleCenterSeed = await inspectHiddenSeed('[data-log-type="generalAction"] .hidden_but_copyable');
|
||||
assertHiddenSeed(battleCenterSeed);
|
||||
await persistParityArtifact(page, `core-hidden-battle-seed-battle-center-${viewport.width}`, battleCenterSeed);
|
||||
}
|
||||
});
|
||||
|
||||
test('메인 개인 기록의 공격·수비 시각은 Ref와 같은 90% 글자 크기로 표시한다', async ({ page }) => {
|
||||
const state: FixtureState = {
|
||||
permission: 'head',
|
||||
|
||||
@@ -88,6 +88,17 @@ textarea {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ref's common stylesheet keeps diagnostic seeds in the DOM so they remain
|
||||
* copyable, while removing them from every normal log rendering surface.
|
||||
* This marker can arrive through main, personal, audit, and history log APIs,
|
||||
* so the display contract belongs to the app-wide legacy markup boundary.
|
||||
*/
|
||||
.hidden_but_copyable {
|
||||
color: rgba(0, 0, 0, 0) !important;
|
||||
font-size: 0;
|
||||
}
|
||||
|
||||
.small_war_log .war_type_attack {
|
||||
color: cyan;
|
||||
}
|
||||
|
||||
@@ -84,11 +84,6 @@ const unavailableText: Record<GeneralRecordType, string> = {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
.log-line :deep(.hidden_but_copyable) {
|
||||
color: transparent !important;
|
||||
font-size: 0;
|
||||
}
|
||||
|
||||
.empty {
|
||||
padding: 2px 8px;
|
||||
color: #999;
|
||||
|
||||
@@ -763,11 +763,6 @@ button {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.record-line :deep(.hidden_but_copyable) {
|
||||
color: transparent !important;
|
||||
font-size: 0;
|
||||
}
|
||||
|
||||
.record-empty {
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
@@ -137,6 +137,13 @@ REF_PERSONAL_WAR_LOG_ARTIFACT_DIR=/path/to/ignored/artifacts \
|
||||
`MENU_PARITY_ARTIFACT_DIR`를 지정하면 1200×900·500×900 screenshot과 computed
|
||||
style JSON을 남깁니다.
|
||||
|
||||
같은 Ref collector는 “진격합니다.” 전투 seed marker도 함께 렌더링하여
|
||||
desktop/mobile의 투명색, 0px 글자 크기, 0×0 rect와 selection text 보존을
|
||||
수집합니다. Core의 대응 test는 `inGameMenus.spec.ts`의 “전투시드는 메인·내
|
||||
정보·감찰부에서 숨긴 채 선택할 수 있다”이며 `PLAYWRIGHT_FRONTEND_MODE=production`
|
||||
과 `MENU_PARITY_ARTIFACT_DIR`를 함께 지정하면 세 화면의 1000×900·500×900
|
||||
screenshot과 computed JSON을 남깁니다.
|
||||
|
||||
개인 전투 결과 요약의 Ref 색상·글자 크기는 실제 `small_war_log` class 구조와
|
||||
빌드 CSS를 사용하는 별도 정적 Chromium fixture로 재현합니다. 방어 화살표,
|
||||
장수명 괄호, 병력 수치와 병종·장수/병력 글자 크기를 desktop/mobile에서
|
||||
|
||||
@@ -12,6 +12,8 @@ const css = await readFile(resolve(refRoot, 'dist_js/hwe_dynamic/vue/v_main.css'
|
||||
const records = [
|
||||
'<C>●</>10월:천귀병으로 <Y>ⓝ염행</>의 보병을 <M>수비</>합니다. <1>12:54</>',
|
||||
'<C>●</>10월:천귀병으로 <Y>ⓝ염행</>의 보병을 <M>공격</>합니다. <1>12:55</>',
|
||||
'<D><b>위</b></>의 <Y>검증장수</>가 <G><b>낙양</b></>으로 진격합니다.' +
|
||||
'<span class="hidden_but_copyable">(전투시드: 0123456789abcdef)</span>',
|
||||
];
|
||||
|
||||
const browser = await chromium.launch({ headless: true });
|
||||
@@ -43,6 +45,29 @@ try {
|
||||
const time = spans.find((span) => /^\d{2}:\d{2}$/u.test(span.textContent ?? ''));
|
||||
const name = spans.find((span) => span.textContent === 'ⓝ염행');
|
||||
const action = spans.find((span) => span.textContent === '수비' || span.textContent === '공격');
|
||||
const hiddenSeed = element.querySelector('.hidden_but_copyable');
|
||||
if (hiddenSeed instanceof HTMLElement) {
|
||||
const range = document.createRange();
|
||||
range.selectNodeContents(hiddenSeed);
|
||||
const selection = window.getSelection();
|
||||
selection?.removeAllRanges();
|
||||
selection?.addRange(range);
|
||||
const hiddenSeedRect = hiddenSeed.getBoundingClientRect();
|
||||
const hiddenSeedStyle = getComputedStyle(hiddenSeed);
|
||||
const result = {
|
||||
text: hiddenSeed.textContent,
|
||||
selectedText: selection?.toString(),
|
||||
color: hiddenSeedStyle.color,
|
||||
fontSize: hiddenSeedStyle.fontSize,
|
||||
width: hiddenSeedRect.width,
|
||||
height: hiddenSeedRect.height,
|
||||
};
|
||||
selection?.removeAllRanges();
|
||||
return {
|
||||
text: element.textContent,
|
||||
hiddenSeed: result,
|
||||
};
|
||||
}
|
||||
if (
|
||||
!(time instanceof HTMLElement) ||
|
||||
!(name instanceof HTMLElement) ||
|
||||
@@ -62,6 +87,7 @@ try {
|
||||
timeFontSize: getComputedStyle(time).fontSize,
|
||||
nameFontSize: getComputedStyle(name).fontSize,
|
||||
actionFontSize: getComputedStyle(action).fontSize,
|
||||
hiddenSeed: null,
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user