merge: 최신 main을 인게임 폰트 통일에 반영
This commit is contained in:
@@ -58,7 +58,7 @@ const simulatorOptions = {
|
||||
{ id: 200, name: '궁병', armType: 2 },
|
||||
],
|
||||
},
|
||||
nationTypes: [{ key: 'che_중립', name: '중립', info: '특별한 효과 없음' }],
|
||||
nationTypes: [{ key: 'che_도적', name: '도적', info: '금 수입 증가, 쌀 수입 감소' }],
|
||||
eventDomesticTraits: [{ key: 'che_event_신산', name: '신산', info: '계략 강화' }],
|
||||
warTraits: [{ key: 'che_필살', name: '필살', info: '필살 확률 증가' }],
|
||||
personalities: [{ key: 'che_대담', name: '대담', info: '공격적인 성격' }],
|
||||
@@ -356,6 +356,10 @@ test('operates independent/game presets, imports my general, and renders battle
|
||||
await page.setViewportSize({ width: 1280, height: 900 });
|
||||
await gotoSimulator(page);
|
||||
|
||||
const nationTypeSelects = page.locator('[data-parity-id="attacker-nation"] select').first();
|
||||
await expect(nationTypeSelects).toHaveValue('che_도적');
|
||||
await expect(nationTypeSelects.locator('option[value="che_중립"]')).toHaveCount(0);
|
||||
|
||||
const notice = page.getByLabel('시뮬레이터 데이터 안내');
|
||||
const noticeRect = await notice.boundingBox();
|
||||
expect(noticeRect?.width).toBeLessThan(100);
|
||||
|
||||
@@ -43,6 +43,7 @@ const inputOptions = {
|
||||
cities: [
|
||||
{ value: 1, label: '업 (아국)' },
|
||||
{ value: 2, label: '허창 (적국)', description: '적국 · 예주 · 대도시' },
|
||||
{ value: 3, label: '단양 (오)' },
|
||||
],
|
||||
nations: [
|
||||
{ value: 1, label: '아국', color: '#008000' },
|
||||
@@ -54,7 +55,7 @@ const inputOptions = {
|
||||
],
|
||||
crewTypes: [{ value: 1100, label: '보병' }],
|
||||
armTypes: [{ value: 1, label: '보병' }],
|
||||
nationTypes: [{ value: 'che_중립', label: '중립' }],
|
||||
nationTypes: [{ value: 'che_도적', label: '도적', description: '금 수입 증가, 쌀 수입 감소' }],
|
||||
colors: [{ value: 0, label: '색상 1', color: '#ff0000' }],
|
||||
items: { horse: [{ value: 'None', label: '판매/해제' }] },
|
||||
recruitment: {
|
||||
@@ -182,6 +183,27 @@ const commandTable = {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
category: '군사',
|
||||
values: [
|
||||
{
|
||||
key: 'che_출병',
|
||||
name: '출병',
|
||||
reqArg: true,
|
||||
possible: true,
|
||||
status: 'needsInput',
|
||||
inputFields: [
|
||||
{
|
||||
key: 'destCityId',
|
||||
label: '대상 도시',
|
||||
kind: 'select',
|
||||
required: true,
|
||||
optionSource: 'cities',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
nation: [
|
||||
{
|
||||
@@ -312,6 +334,7 @@ const generalContext = {
|
||||
experience: 0,
|
||||
dedication: 0,
|
||||
items: { horse: 'None', weapon: 'None', book: 'None', item: 'None' },
|
||||
turnTime: '2026-08-17T12:00:00.000Z',
|
||||
},
|
||||
city: {
|
||||
id: 1,
|
||||
@@ -483,7 +506,7 @@ const install = async (page: Page, rejectGeneral = false, commandTableResponse:
|
||||
if (name === 'turns.getCommandTable') return response(commandTableResponse);
|
||||
if (name === 'nation.getChiefCenter') return response(chiefCenter);
|
||||
if (name === 'turns.reserved.getGeneral')
|
||||
return response({ turns: generalTurns, revision: generalRevision });
|
||||
return response({ turns: generalTurns, revision: generalRevision, autorunLimit: 2403 });
|
||||
if (name === 'turns.reserved.getNation') return response({ turns: nationTurns, revision: nationRevision });
|
||||
if (name === 'general.getRecentRecords') return response({ global: [], general: [], history: [] });
|
||||
if (name === 'general.getFrontStatus')
|
||||
@@ -520,7 +543,7 @@ const install = async (page: Page, rejectGeneral = false, commandTableResponse:
|
||||
generalTurns[index] = { index, action: entry.action, args: entry.args ?? {} };
|
||||
}
|
||||
generalRevision += 1;
|
||||
return response({ ok: true, revision: generalRevision, turns: generalTurns });
|
||||
return response({ ok: true, revision: generalRevision, turns: generalTurns, autorunLimit: 2403 });
|
||||
}
|
||||
if (name === 'turns.reserved.setNationBulk') {
|
||||
requests.push(body);
|
||||
@@ -561,12 +584,67 @@ test('renders and accepts every Ref strategy command at mobile width', async ({
|
||||
await button.click();
|
||||
const form = picker.getByTestId('command-argument-form');
|
||||
await expect(form.getByTestId('command-argument-guidance')).toContainText(strategy.guidance);
|
||||
await expect(form.locator('select option')).toHaveCount(2);
|
||||
await expect(form.locator('select option')).toHaveCount(3);
|
||||
await picker.getByRole('button', { name: '명령 다시 선택', exact: true }).click();
|
||||
}
|
||||
await picker.screenshot({ path: test.info().outputPath('all-strategy-commands-mobile.png') });
|
||||
});
|
||||
|
||||
test('defaults founding to a Ref-selectable nation trait without exposing the neutral storage trait', async ({
|
||||
page,
|
||||
}) => {
|
||||
const foundingCommandTable = {
|
||||
general: [
|
||||
{
|
||||
category: '국가',
|
||||
values: [
|
||||
{
|
||||
key: 'che_건국',
|
||||
name: '건국',
|
||||
reqArg: true,
|
||||
possible: true,
|
||||
status: 'needsInput',
|
||||
inputFields: [
|
||||
{ key: 'nationName', label: '국가명', kind: 'text', required: true, min: 1, max: 18 },
|
||||
{
|
||||
key: 'nationType',
|
||||
label: '국가 성향',
|
||||
kind: 'select',
|
||||
required: true,
|
||||
optionSource: 'nationTypes',
|
||||
},
|
||||
{
|
||||
key: 'colorType',
|
||||
label: '국기 색상',
|
||||
kind: 'select',
|
||||
required: true,
|
||||
optionSource: 'colors',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
nation: [],
|
||||
inputOptions,
|
||||
};
|
||||
await install(page, false, foundingCommandTable);
|
||||
await page.setViewportSize({ width: 1200, height: 900 });
|
||||
await page.goto('/');
|
||||
await page.getByRole('button', { name: '1턴 명령 입력', exact: true }).click();
|
||||
|
||||
const picker = page.getByTestId('command-picker');
|
||||
await picker.getByRole('button', { name: '국가', exact: true }).click();
|
||||
await picker.getByRole('button', { name: '건국', exact: true }).click();
|
||||
const nationType = picker.getByLabel('국가 성향');
|
||||
await expect(nationType).toHaveValue('che_도적');
|
||||
await expect(nationType.locator('option[value="che_중립"]')).toHaveCount(0);
|
||||
await expect(nationType.locator('option')).toHaveText(['도적']);
|
||||
await nationType.focus();
|
||||
await expect(nationType).toBeFocused();
|
||||
await picker.screenshot({ path: test.info().outputPath('founding-selectable-nation-trait-desktop-1200.png') });
|
||||
});
|
||||
|
||||
test('reserves force move, retirement, and resignation from the user command picker', async ({ page }) => {
|
||||
const specialCommandTable = {
|
||||
general: [
|
||||
@@ -654,7 +732,7 @@ test('reserves force move, retirement, and resignation from the user command pic
|
||||
await expect(forceMoveForm.getByTestId('command-argument-guidance')).toContainText('선택한 도시로 강행합니다.');
|
||||
await forceMoveForm.locator('select').selectOption('2');
|
||||
await picker.getByRole('button', { name: '입력', exact: true }).click();
|
||||
await expect(editor.locator('.action-column > div').nth(2)).toHaveText('강행');
|
||||
await expect(editor.locator('.action-column > div').nth(2)).toHaveText('【허창】으로 강행');
|
||||
|
||||
const serialized = JSON.stringify(requests);
|
||||
expect(serialized).toContain('"action":"che_은퇴","args":{}');
|
||||
@@ -801,7 +879,9 @@ test('enters general and nation command arguments and sends exact values', async
|
||||
return { width: rect.width, height: rect.height };
|
||||
});
|
||||
await page.getByTestId('command-picker').getByRole('button', { name: '입력', exact: true }).click();
|
||||
await expect(page.locator('[data-command-scope="general"] .action-column > div').first()).toHaveText('화계');
|
||||
await expect(page.locator('[data-command-scope="general"] .action-column > div').first()).toHaveText(
|
||||
'【허창】에 화계실행'
|
||||
);
|
||||
|
||||
await page.goto('/che/chief-center');
|
||||
await page.getByRole('button', { name: '1턴 명령 입력', exact: true }).click();
|
||||
@@ -824,7 +904,9 @@ test('enters general and nation command arguments and sends exact values', async
|
||||
};
|
||||
});
|
||||
await chiefPicker.getByRole('button', { name: '입력', exact: true }).click();
|
||||
await expect(page.locator('[data-command-scope="nation"] .action-column > div').first()).toHaveText('포상');
|
||||
await expect(page.locator('[data-command-scope="nation"] .action-column > div').first()).toHaveText(
|
||||
'【관우】 쌀 300 포상'
|
||||
);
|
||||
|
||||
expect(JSON.stringify(requests)).toContain('"destCityId":2');
|
||||
expect(JSON.stringify(requests)).toContain('"isGold":false');
|
||||
@@ -906,7 +988,9 @@ test('uses a Ref-style full recruitment page without horizontal overflow on desk
|
||||
await infantry.getByRole('button', { name: '절반', exact: true }).click();
|
||||
await page.screenshot({ path: testInfo.outputPath('recruitment-desktop.png') });
|
||||
await picker.getByRole('button', { name: '입력', exact: true }).click();
|
||||
await expect(page.locator('[data-command-scope="general"] .action-column > div').first()).toHaveText('징병');
|
||||
await expect(page.locator('[data-command-scope="general"] .action-column > div').first()).toHaveText(
|
||||
'【보병】 3500명 징병'
|
||||
);
|
||||
expect(JSON.stringify(requests)).toContain('"crewType":1100');
|
||||
expect(JSON.stringify(requests)).toContain('"amount":3500');
|
||||
|
||||
@@ -1112,6 +1196,53 @@ test('keeps the entered command visible and reports a server validation error',
|
||||
await expect(page.getByTestId('command-argument-form').locator('select')).toHaveValue('2');
|
||||
});
|
||||
|
||||
test('keeps Ref command briefs and autonomous-action state after a turn mutation', async ({ page }) => {
|
||||
await install(page);
|
||||
await page.setViewportSize({ width: 1200, height: 900 });
|
||||
await page.goto('/');
|
||||
|
||||
const editor = page.locator('[data-command-scope="general"]');
|
||||
const status = editor.locator('[data-command-autorun-status]');
|
||||
const firstRow = editor.locator('.action-column > div').first();
|
||||
await expect(status).toHaveText(/자율 행동: 200年 3月 · \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}까지/u);
|
||||
await expect(firstRow).toContainText('휴식(자율 행동)');
|
||||
expect(await firstRow.evaluate((element) => getComputedStyle(element).color)).toBe('rgb(170, 255, 255)');
|
||||
|
||||
await page.getByRole('button', { name: '1턴 명령 입력', exact: true }).click();
|
||||
const 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(firstRow).toHaveText('【단양】으로 출병');
|
||||
await expect(firstRow).toHaveAttribute('title', /자율 행동/u);
|
||||
expect(await firstRow.evaluate((element) => getComputedStyle(element).color)).toBe('rgb(170, 255, 255)');
|
||||
await expect(status).toHaveText(/자율 행동: 200年 3月 · .*까지/u);
|
||||
|
||||
const desktopGeometry = await editor.evaluate((element) => {
|
||||
const statusElement = element.querySelector<HTMLElement>('[data-command-autorun-status]');
|
||||
const row = element.querySelector<HTMLElement>('.action-column > div');
|
||||
if (!statusElement || !row) throw new Error('autonomous command geometry is missing');
|
||||
return {
|
||||
horizontalOverflow: element.scrollWidth - element.clientWidth,
|
||||
statusWidth: statusElement.getBoundingClientRect().width,
|
||||
editorWidth: element.getBoundingClientRect().width,
|
||||
rowHeight: row.getBoundingClientRect().height,
|
||||
};
|
||||
});
|
||||
expect(desktopGeometry.horizontalOverflow).toBeLessThanOrEqual(0);
|
||||
expect(desktopGeometry.statusWidth).toBeLessThanOrEqual(desktopGeometry.editorWidth);
|
||||
expect(desktopGeometry.rowHeight).toBeGreaterThanOrEqual(20);
|
||||
await page.screenshot({ path: test.info().outputPath('command-brief-autorun-desktop-1200.png'), fullPage: true });
|
||||
|
||||
await page.setViewportSize({ width: 500, height: 900 });
|
||||
await expect(firstRow).toHaveText('【단양】으로 출병');
|
||||
await expect(status).toBeVisible();
|
||||
expect(await editor.evaluate((element) => element.scrollWidth - element.clientWidth)).toBeLessThanOrEqual(0);
|
||||
await page.screenshot({ path: test.info().outputPath('command-brief-autorun-mobile-500.png'), fullPage: true });
|
||||
});
|
||||
|
||||
test('uses drag selection, clipboard paste, and a stored template in advanced mode', async ({ page }) => {
|
||||
const requests = await install(page);
|
||||
await page.goto('/');
|
||||
@@ -1139,7 +1270,7 @@ test('uses drag selection, clipboard paste, and a stored template in advanced mo
|
||||
await blockedFire.click();
|
||||
await picker.getByTestId('command-argument-form').locator('select').selectOption('2');
|
||||
await picker.getByRole('button', { name: '입력', exact: true }).click();
|
||||
await expect(editor.locator('.action-column > div').nth(2)).toHaveText('화계');
|
||||
await expect(editor.locator('.action-column > div').nth(2)).toHaveText('【허창】에 화계실행');
|
||||
|
||||
await drag(0, 2);
|
||||
await editor.locator('details.selected-menu > summary').click();
|
||||
@@ -1150,7 +1281,7 @@ test('uses drag selection, clipboard paste, and a stored template in advanced mo
|
||||
await expect(editor.locator('.index-column > button.selected')).toHaveCount(15);
|
||||
await editor.locator('details.selected-menu > summary').click();
|
||||
await editor.getByRole('button', { name: '붙여넣기', exact: true }).click();
|
||||
await expect(editor.locator('.action-column > div').nth(5)).toHaveText('화계');
|
||||
await expect(editor.locator('.action-column > div').nth(5)).toHaveText('【허창】에 화계실행');
|
||||
|
||||
await drag(0, 2);
|
||||
page.once('dialog', (dialog) => dialog.accept('화계 세트'));
|
||||
@@ -1229,7 +1360,9 @@ test('keeps the shared main and chief shell geometry and interaction states', as
|
||||
await chiefArgumentForm.locator('input[type=number]').fill('300');
|
||||
await chiefArgumentForm.locator('select').selectOption('2');
|
||||
await page.getByTestId('command-picker').getByRole('button', { name: '입력', exact: true }).click();
|
||||
await expect(page.locator('[data-command-scope="nation"] .action-column > div').first()).toHaveText('포상');
|
||||
await expect(page.locator('[data-command-scope="nation"] .action-column > div').first()).toHaveText(
|
||||
'【관우】 쌀 300 포상'
|
||||
);
|
||||
expect(JSON.stringify(requests)).toContain('"action":"che_포상"');
|
||||
expect(JSON.stringify(requests)).toContain('"destGeneralId":2');
|
||||
const chiefDesktop = await page.locator('.chief-page').evaluate((element) => ({
|
||||
|
||||
@@ -810,6 +810,7 @@ test('desktop menus preserve ref columns, prefix-safe routes, and controlled dro
|
||||
await installFixture(page, state);
|
||||
await page.setViewportSize({ width: 1200, height: 900 });
|
||||
await waitForMain(page);
|
||||
if (artifactRoot) await mkdir(resolve(artifactRoot), { recursive: true });
|
||||
|
||||
await expect(page.locator('.main-global-menu')).toHaveCount(3);
|
||||
expect(await gridColumnCount(page, '.main-global-menu')).toBe(8);
|
||||
@@ -2238,6 +2239,222 @@ test('nation menu presentation follows the server-derived permission matrix', as
|
||||
);
|
||||
});
|
||||
|
||||
test('all main Lumen button families share the rounded pressed geometry', async ({ page }) => {
|
||||
const state: NavigationFixture = {
|
||||
officerLevel: 5,
|
||||
permission: 2,
|
||||
nationLevel: 3,
|
||||
stage: 0,
|
||||
npcMode: 1,
|
||||
generalMeCalls: 0,
|
||||
operations: [],
|
||||
nationColor: '#663399',
|
||||
};
|
||||
await installFixture(page, state);
|
||||
await page.setViewportSize({ width: 1200, height: 900 });
|
||||
await waitForMain(page);
|
||||
|
||||
const controls: Array<[string, Locator]> = [
|
||||
[
|
||||
'천통국 베팅',
|
||||
page.locator('.main-global-menu[data-menu-position="top"] [data-navigation-id="nation-betting"]'),
|
||||
],
|
||||
[
|
||||
'게임정보',
|
||||
page.locator('.main-global-menu[data-menu-position="top"]').getByRole('button', {
|
||||
name: '게임정보',
|
||||
exact: true,
|
||||
}),
|
||||
],
|
||||
['회 의 실', page.locator('.layout-desktop [data-navigation-id="meeting"]')],
|
||||
['기 밀 실', page.locator('.layout-desktop [data-navigation-id="secret-board"]')],
|
||||
[
|
||||
'당기기',
|
||||
page.locator('[data-main-target="commands"] .bottom-actions').getByRole('button', { name: '당기기' }),
|
||||
],
|
||||
[
|
||||
'미루기',
|
||||
page.locator('[data-main-target="commands"] .bottom-actions').getByRole('button', { name: '미루기' }),
|
||||
],
|
||||
[
|
||||
'펼치기',
|
||||
page.locator('[data-main-target="commands"] .bottom-actions').getByRole('button', { name: '펼치기' }),
|
||||
],
|
||||
['실시간 동기화', page.locator('.desktop-action-controls').getByRole('button', { name: /실시간 동기화:/u })],
|
||||
['갱 신', page.locator('.desktop-action-controls').getByRole('button', { name: '갱 신' })],
|
||||
['로비로', page.locator('.desktop-action-controls').getByRole('button', { name: '로비로' })],
|
||||
];
|
||||
|
||||
const measure = (control: Locator) =>
|
||||
control.evaluate((element) => {
|
||||
const rect = element.getBoundingClientRect();
|
||||
const style = getComputedStyle(element);
|
||||
return {
|
||||
top: rect.top,
|
||||
bottom: rect.bottom,
|
||||
height: rect.height,
|
||||
marginTop: style.marginTop,
|
||||
borderTop: style.borderTopWidth,
|
||||
borderRight: style.borderRightWidth,
|
||||
borderBottom: style.borderBottomWidth,
|
||||
borderLeft: style.borderLeftWidth,
|
||||
radius: style.borderRadius,
|
||||
background: style.backgroundColor,
|
||||
filter: style.filter,
|
||||
};
|
||||
});
|
||||
|
||||
const evidence: Record<string, Record<string, unknown>> = {};
|
||||
for (const [index, [label, control]] of controls.entries()) {
|
||||
await expect(control, `${label} control`).toBeVisible();
|
||||
await expect(control).toHaveClass(/legacy-button/u);
|
||||
await control.scrollIntoViewIfNeeded();
|
||||
await page.mouse.move(1195, 895);
|
||||
const base = await measure(control);
|
||||
evidence[label] = { default: base };
|
||||
if (artifactRoot) {
|
||||
await control.screenshot({ path: resolve(artifactRoot, `${index + 1}-default.png`) });
|
||||
}
|
||||
expect(base, `${label} default geometry`).toMatchObject({
|
||||
marginTop: '0px',
|
||||
borderTop: '0px',
|
||||
borderRight: '1px',
|
||||
borderBottom: '4px',
|
||||
borderLeft: '1px',
|
||||
radius: '5.25px',
|
||||
filter: 'none',
|
||||
});
|
||||
|
||||
await control.focus();
|
||||
await expect(control, `${label} keyboard focus`).toBeFocused();
|
||||
const focused = await measure(control);
|
||||
evidence[label].focus = focused;
|
||||
if (artifactRoot) {
|
||||
await control.screenshot({ path: resolve(artifactRoot, `${index + 1}-focus.png`) });
|
||||
}
|
||||
expect(focused.borderBottom, `${label} focus edge`).toBe('4px');
|
||||
expect(focused.marginTop, `${label} focus position`).toBe('0px');
|
||||
|
||||
await control.hover();
|
||||
const hovered = await measure(control);
|
||||
evidence[label].hover = hovered;
|
||||
if (artifactRoot) {
|
||||
await control.screenshot({ path: resolve(artifactRoot, `${index + 1}-hover.png`) });
|
||||
}
|
||||
expect(hovered.borderBottom, `${label} hover edge`).toBe('3px');
|
||||
expect(hovered.marginTop, `${label} hover position`).toBe('1px');
|
||||
expect(hovered.top, `${label} hover top`).toBeCloseTo(base.top + 1, 2);
|
||||
expect(hovered.height, `${label} hover height`).toBeCloseTo(base.height - 1, 2);
|
||||
expect(hovered.bottom, `${label} hover bottom`).toBeCloseTo(base.bottom, 2);
|
||||
expect(hovered.background, `${label} hover face`).toBe(base.background);
|
||||
|
||||
const box = await control.boundingBox();
|
||||
if (!box) throw new Error(`${label} control has no bounding box`);
|
||||
await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2);
|
||||
await page.mouse.down();
|
||||
const pressed = await measure(control);
|
||||
evidence[label].pointerDown = pressed;
|
||||
if (artifactRoot) {
|
||||
await control.screenshot({ path: resolve(artifactRoot, `${index + 1}-pointer-down.png`) });
|
||||
}
|
||||
expect(pressed.borderBottom, `${label} pressed edge`).toBe('2px');
|
||||
expect(pressed.marginTop, `${label} pressed position`).toBe('2px');
|
||||
expect(pressed.top, `${label} pressed top`).toBeCloseTo(base.top + 2, 2);
|
||||
expect(pressed.height, `${label} pressed height`).toBeCloseTo(base.height - 2, 2);
|
||||
expect(pressed.bottom, `${label} pressed bottom`).toBeCloseTo(base.bottom, 2);
|
||||
expect(pressed.background, `${label} pressed face`).toBe(base.background);
|
||||
await page.mouse.move(1195, 895);
|
||||
await page.mouse.up();
|
||||
}
|
||||
|
||||
state.permission = 0;
|
||||
await page.locator('.desktop-action-controls').getByRole('button', { name: '갱 신' }).click();
|
||||
const disabledSecret = page.locator('.layout-desktop [data-navigation-id="secret-board"]');
|
||||
await expect(disabledSecret).toHaveAttribute('aria-disabled', 'true');
|
||||
await disabledSecret.scrollIntoViewIfNeeded();
|
||||
const disabledBase = await measure(disabledSecret);
|
||||
await disabledSecret.hover({ force: true });
|
||||
const disabledHover = await measure(disabledSecret);
|
||||
evidence['기 밀 실 disabled'] = { default: disabledBase, hover: disabledHover };
|
||||
expect(disabledHover.borderBottom).toBe('4px');
|
||||
expect(disabledHover.marginTop).toBe('0px');
|
||||
expect(disabledHover.top).toBeCloseTo(disabledBase.top, 2);
|
||||
if (artifactRoot) {
|
||||
await disabledSecret.screenshot({ path: resolve(artifactRoot, 'disabled-secret-hover.png') });
|
||||
await writeFile(
|
||||
resolve(artifactRoot, 'main-lumen-button-states.json'),
|
||||
`${JSON.stringify(evidence, null, 2)}\n`
|
||||
);
|
||||
}
|
||||
|
||||
await persistArtifact(page, `${basePath.slice(1)}-main-lumen-button-families`);
|
||||
});
|
||||
|
||||
test('mobile main Lumen button families keep the same state geometry without overflow', async ({ page }) => {
|
||||
const state: NavigationFixture = {
|
||||
officerLevel: 5,
|
||||
permission: 2,
|
||||
nationLevel: 3,
|
||||
stage: 0,
|
||||
npcMode: 1,
|
||||
generalMeCalls: 0,
|
||||
operations: [],
|
||||
nationColor: '#663399',
|
||||
};
|
||||
await installFixture(page, state);
|
||||
await page.setViewportSize({ width: 500, height: 900 });
|
||||
await waitForMain(page);
|
||||
|
||||
const controls = [
|
||||
page.locator('.main-global-menu[data-menu-position="top"] [data-navigation-id="nation-betting"]'),
|
||||
page.locator('.main-global-menu[data-menu-position="top"]').getByRole('button', {
|
||||
name: '게임정보',
|
||||
exact: true,
|
||||
}),
|
||||
page.locator('.layout-mobile [data-navigation-id="meeting"]'),
|
||||
page.locator('.layout-mobile [data-navigation-id="secret-board"]'),
|
||||
page.locator('[data-main-target="commands"] .bottom-actions').getByRole('button', { name: '당기기' }),
|
||||
page.locator('[data-main-target="commands"] .bottom-actions').getByRole('button', { name: '미루기' }),
|
||||
page.locator('[data-main-target="commands"] .bottom-actions').getByRole('button', { name: '펼치기' }),
|
||||
page.locator('.desktop-action-controls').getByRole('button', { name: /실시간 동기화:/u }),
|
||||
page.locator('.desktop-action-controls').getByRole('button', { name: '갱 신' }),
|
||||
page.locator('.desktop-action-controls').getByRole('button', { name: '로비로' }),
|
||||
];
|
||||
for (const control of controls) {
|
||||
await expect(control).toBeVisible();
|
||||
await expect(control).toHaveClass(/legacy-button/u);
|
||||
await expect(control).toHaveCSS('border-radius', '5.25px');
|
||||
await expect(control).toHaveCSS('border-bottom-width', '4px');
|
||||
}
|
||||
|
||||
for (const control of [controls[0], controls[2], controls[4], controls[7]]) {
|
||||
if (!control) throw new Error('mobile Lumen control is missing');
|
||||
await control.scrollIntoViewIfNeeded();
|
||||
await control.focus();
|
||||
await expect(control).toBeFocused();
|
||||
await expect(control).toHaveCSS('border-bottom-width', '4px');
|
||||
await control.hover();
|
||||
await expect(control).toHaveCSS('border-bottom-width', '3px');
|
||||
await expect(control).toHaveCSS('margin-top', '1px');
|
||||
const box = await control.boundingBox();
|
||||
if (!box) throw new Error('mobile Lumen control is not measurable');
|
||||
await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2);
|
||||
await page.mouse.down();
|
||||
await expect(control).toHaveCSS('border-bottom-width', '2px');
|
||||
await expect(control).toHaveCSS('margin-top', '2px');
|
||||
await page.mouse.move(499, 899);
|
||||
await page.mouse.up();
|
||||
}
|
||||
|
||||
expect(
|
||||
await page.evaluate(() => ({
|
||||
document: document.documentElement.scrollWidth - document.documentElement.clientWidth,
|
||||
body: document.body.scrollWidth - document.body.clientWidth,
|
||||
}))
|
||||
).toEqual({ document: 0, body: 0 });
|
||||
await persistArtifact(page, `${basePath.slice(1)}-mobile-main-lumen-button-families`);
|
||||
});
|
||||
|
||||
test('mobile single document refreshes once and preserves tokens on lobby return', async ({ page }) => {
|
||||
const state: NavigationFixture = {
|
||||
officerLevel: 5,
|
||||
|
||||
Reference in New Issue
Block a user