fix: 정보 화면 버튼을 공통 Lumen 계열로 통일
세력도시·세력정보·현재도시·내 정보·인사부·외교부의 raised control을 공통 semantic button family에 연결한다. 화면별 크기와 색상은 유지하고 실제 Chromium 상태 geometry 회귀를 추가한다.
This commit is contained in:
@@ -2,6 +2,7 @@ import { mkdir, writeFile } from 'node:fs/promises';
|
||||
import { resolve } from 'node:path';
|
||||
|
||||
import { expect, test, type Page, type Route } from '@playwright/test';
|
||||
import { expectLumenButtonStates } from './lumenButton.js';
|
||||
|
||||
const basePath = `/${(process.env.PLAYWRIGHT_GAME_BASE_PATH ?? 'che').replace(/^\/+|\/+$/g, '')}`;
|
||||
const artifactRoot = process.env.DIPLOMACY_ARTIFACT_DIR ? resolve(process.env.DIPLOMACY_ARTIFACT_DIR) : null;
|
||||
@@ -103,7 +104,9 @@ for (const viewport of [
|
||||
await expect(card.getByRole('link', { name: '자료' })).toHaveAttribute('href', 'https://example.com');
|
||||
await expect(card.getByRole('link', { name: '자료' })).toHaveAttribute('rel', 'noopener noreferrer nofollow');
|
||||
await expect(card.locator('.letter-text script, .letter-text svg, .letter-text math')).toHaveCount(0);
|
||||
await expect(card.locator('.letter-text [onerror], .letter-text [onclick], .letter-text [style]')).toHaveCount(0);
|
||||
await expect(card.locator('.letter-text [onerror], .letter-text [onclick], .letter-text [style]')).toHaveCount(
|
||||
0
|
||||
);
|
||||
expect(await page.evaluate(() => (globalThis as Record<string, unknown>).__diplomacyXss)).toBeUndefined();
|
||||
|
||||
const geometry = await card.evaluate((element) => {
|
||||
@@ -142,9 +145,7 @@ for (const viewport of [
|
||||
}
|
||||
|
||||
const send = page.getByRole('button', { name: '전송' });
|
||||
await send.focus();
|
||||
await expect(send).toBeFocused();
|
||||
await send.hover();
|
||||
await expectLumenButtonStates(page, send, 'rgb(55, 90, 127)');
|
||||
await screenshot(page, `diplomacy-html-${basePath.slice(1)}-${viewport.name}.png`);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { expect, test, type Page, type Route } from '@playwright/test';
|
||||
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
||||
import { dirname, resolve } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { expectLumenButtonStates } from './lumenButton.js';
|
||||
|
||||
const response = (data: unknown) => ({ result: { data } });
|
||||
const artifactRoot = process.env.CITY_PARITY_ARTIFACT_DIR;
|
||||
@@ -493,6 +494,13 @@ test('four legacy menu pages keep the 1000px desktop table contract', async ({ p
|
||||
.first()
|
||||
.evaluate((el) => getComputedStyle(el).borderCollapse)
|
||||
).toBe(borderCollapse);
|
||||
if (path === 'nation/info' || path === 'current-city') {
|
||||
await expectLumenButtonStates(
|
||||
page,
|
||||
page.getByRole('button', { name: '돌아가기' }).first(),
|
||||
'rgb(0, 88, 44)'
|
||||
);
|
||||
}
|
||||
if (path === 'nation/info') {
|
||||
await expect(page.locator(selector)).toContainText('작 위호족');
|
||||
await expect(page.locator(selector)).not.toContainText('작 위1');
|
||||
|
||||
@@ -2,6 +2,7 @@ import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
||||
import { basename, resolve } from 'node:path';
|
||||
import { expect, test, type Locator, type Page, type Route } from '@playwright/test';
|
||||
import { gameBasePath, gameProfile, gameTrpcRoute } from './gameTestPaths.js';
|
||||
import { expectLumenButtonStates } from './lumenButton.js';
|
||||
import { touchDrag } from './touchDrag.js';
|
||||
|
||||
const response = (data: unknown) => ({ result: { data } });
|
||||
@@ -1320,6 +1321,7 @@ test('내 정보&설정 keeps desktop density and becomes a 390px horizontal-ide
|
||||
expect(desktop.customCssHeight).toBe(150);
|
||||
expect(desktop.backgroundImage).toContain('back_walnut.jpg');
|
||||
expect(desktop.sectionBackgroundImage).toContain('back_green.jpg');
|
||||
await expectLumenButtonStates(page, page.locator('#set_my_setting'), 'rgb(34, 85, 0)');
|
||||
await persistParityArtifact(page, 'core-my-page-desktop', desktop);
|
||||
|
||||
const defenceSelect = page.locator('select').filter({ has: page.locator('option[value="999"]') });
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
import { expect, type Locator, type Page } from '@playwright/test';
|
||||
|
||||
type ButtonGeometry = {
|
||||
top: number;
|
||||
bottom: number;
|
||||
height: number;
|
||||
marginTop: string;
|
||||
borderBottomWidth: string;
|
||||
borderRadius: string;
|
||||
backgroundColor: string;
|
||||
};
|
||||
|
||||
const measure = (control: Locator): Promise<ButtonGeometry> =>
|
||||
control.evaluate((element) => {
|
||||
const rect = element.getBoundingClientRect();
|
||||
const style = getComputedStyle(element);
|
||||
return {
|
||||
top: rect.top,
|
||||
bottom: rect.bottom,
|
||||
height: rect.height,
|
||||
marginTop: style.marginTop,
|
||||
borderBottomWidth: style.borderBottomWidth,
|
||||
borderRadius: style.borderRadius,
|
||||
backgroundColor: style.backgroundColor,
|
||||
};
|
||||
});
|
||||
|
||||
export const expectLumenButtonStates = async (
|
||||
page: Page,
|
||||
control: Locator,
|
||||
expectedBackground: string
|
||||
): Promise<{ base: ButtonGeometry; hover: ButtonGeometry; active: ButtonGeometry }> => {
|
||||
await expect(control).toBeVisible();
|
||||
await page.mouse.move(0, 0);
|
||||
const base = await measure(control);
|
||||
expect(base).toMatchObject({
|
||||
marginTop: '0px',
|
||||
borderBottomWidth: '4px',
|
||||
borderRadius: '5.25px',
|
||||
backgroundColor: expectedBackground,
|
||||
});
|
||||
|
||||
await control.hover();
|
||||
const hover = await measure(control);
|
||||
expect(hover).toMatchObject({ marginTop: '1px', borderBottomWidth: '3px' });
|
||||
expect(hover.top).toBeCloseTo(base.top + 1, 1);
|
||||
expect(hover.bottom).toBeCloseTo(base.bottom, 1);
|
||||
|
||||
const box = await control.boundingBox();
|
||||
if (!box) throw new Error('Lumen button is not measurable');
|
||||
await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2);
|
||||
await page.mouse.down();
|
||||
const active = await measure(control);
|
||||
expect(active).toMatchObject({ marginTop: '2px', borderBottomWidth: '2px' });
|
||||
expect(active.top).toBeCloseTo(base.top + 2, 1);
|
||||
expect(active.bottom).toBeCloseTo(base.bottom, 1);
|
||||
await page.mouse.move(0, 0);
|
||||
await page.mouse.up();
|
||||
|
||||
await page.keyboard.press('Tab');
|
||||
await control.focus();
|
||||
await expect(control).toBeFocused();
|
||||
await expect.poll(() => control.evaluate((element) => getComputedStyle(element).boxShadow)).not.toBe('none');
|
||||
|
||||
return { base, hover, active };
|
||||
};
|
||||
@@ -1,6 +1,7 @@
|
||||
import { expect, test, type Page, type Route } from '@playwright/test';
|
||||
|
||||
import { gameProfile, gameTrpcRoute } from './gameTestPaths.js';
|
||||
import { expectLumenButtonStates } from './lumenButton.js';
|
||||
|
||||
type Role = 'head' | 'member';
|
||||
type AppointmentInput = { destGeneralId: number; destCityId: number; officerLevel: number };
|
||||
@@ -352,6 +353,7 @@ test('암행부 행을 도시별로 나누고 수뇌의 인사부 즉시 임명
|
||||
await expect(page.locator('.nation-cities-page')).toBeVisible();
|
||||
await expect(page.locator('.city-user-table')).toHaveCount(0);
|
||||
await expect(page.getByRole('button', { name: '인사부 연동' })).toHaveCount(0);
|
||||
await expectLumenButtonStates(page, page.getByRole('button', { name: '암행부 연동' }), 'rgb(55, 90, 127)');
|
||||
|
||||
const citySort = page.locator('#nation-city-sort');
|
||||
await expect(citySort).toHaveCSS('background-color', 'rgb(24, 35, 29)');
|
||||
|
||||
@@ -3,6 +3,7 @@ import { mkdir, readFile } from 'node:fs/promises';
|
||||
import { dirname, resolve } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { gameProfile, gameTrpcRoute } from './gameTestPaths.js';
|
||||
import { expectLumenButtonStates } from './lumenButton.js';
|
||||
|
||||
type Role = 'leader' | 'head' | 'member';
|
||||
type FixtureState = {
|
||||
@@ -290,6 +291,7 @@ test('personnel keeps the desktop frame while exposing row-level appointment con
|
||||
await page.setViewportSize({ width: 1000, height: 900 });
|
||||
await gotoOffice(page, 'nation/personnel');
|
||||
await expect(page.getByText('작위검증국')).toBeVisible();
|
||||
await expectLumenButtonStates(page, page.locator('.personnel-change-button').first(), 'rgb(49, 91, 61)');
|
||||
|
||||
const computed = await page.locator('#personnel-container').evaluate((container) => {
|
||||
const box = (selector?: string) => {
|
||||
@@ -339,7 +341,7 @@ test('personnel keeps the desktop frame while exposing row-level appointment con
|
||||
await changeButton.hover();
|
||||
expect(await changeButton.evaluate((button) => getComputedStyle(button).cursor)).toBe('pointer');
|
||||
await changeButton.focus();
|
||||
expect(await changeButton.evaluate((button) => getComputedStyle(button).outlineStyle)).not.toBe('none');
|
||||
expect(await changeButton.evaluate((button) => getComputedStyle(button).boxShadow)).not.toBe('none');
|
||||
await expect(page.getByRole('button', { name: '허창 태수 변경하기', exact: true })).toBeVisible();
|
||||
await expect(page.getByRole('button', { name: '허창 군사 변경하기', exact: true })).toHaveCount(0);
|
||||
await screenshot(page, 'core-personnel-desktop-leader.png');
|
||||
|
||||
Reference in New Issue
Block a user