test(game-ui): 모바일 패널 CSS 순서 검증 강화
This commit is contained in:
@@ -792,6 +792,103 @@ const gridColumnCount = async (page: Page, selector: string) =>
|
|||||||
.first()
|
.first()
|
||||||
.evaluate((element) => getComputedStyle(element).gridTemplateColumns.split(' ').length);
|
.evaluate((element) => getComputedStyle(element).gridTemplateColumns.split(' ').length);
|
||||||
|
|
||||||
|
const setMobilePanelOrder = async (page: Page, order: readonly string[]) => {
|
||||||
|
await page.evaluate((nextOrder) => {
|
||||||
|
localStorage.setItem('sam.mobileMainPanelOrder.v1', JSON.stringify(nextOrder));
|
||||||
|
document.dispatchEvent(new CustomEvent('sam-mobile-main-panel-order-changed'));
|
||||||
|
}, order);
|
||||||
|
};
|
||||||
|
|
||||||
|
const inspectMobilePanelLayout = async (page: Page) =>
|
||||||
|
page.locator('.layout-mobile').evaluate((container) => {
|
||||||
|
const containerStyle = getComputedStyle(container);
|
||||||
|
const panels = [...container.querySelectorAll<HTMLElement>(':scope > [data-mobile-panel-id]')].map(
|
||||||
|
(element, domIndex) => {
|
||||||
|
const rect = element.getBoundingClientRect();
|
||||||
|
const style = getComputedStyle(element);
|
||||||
|
const content = element.firstElementChild as HTMLElement | null;
|
||||||
|
const contentStyle = content ? getComputedStyle(content) : null;
|
||||||
|
return {
|
||||||
|
id: element.dataset.mobilePanelId ?? '',
|
||||||
|
domIndex,
|
||||||
|
top: rect.top,
|
||||||
|
bottom: rect.bottom,
|
||||||
|
left: rect.left,
|
||||||
|
right: rect.right,
|
||||||
|
width: rect.width,
|
||||||
|
height: rect.height,
|
||||||
|
display: style.display,
|
||||||
|
position: style.position,
|
||||||
|
inset: [style.top, style.right, style.bottom, style.left],
|
||||||
|
order: style.order,
|
||||||
|
transform: style.transform,
|
||||||
|
float: style.cssFloat,
|
||||||
|
gridRow: `${style.gridRowStart} / ${style.gridRowEnd}`,
|
||||||
|
gridColumn: `${style.gridColumnStart} / ${style.gridColumnEnd}`,
|
||||||
|
marginTop: style.marginTop,
|
||||||
|
marginBottom: style.marginBottom,
|
||||||
|
content: contentStyle
|
||||||
|
? {
|
||||||
|
position: contentStyle.position,
|
||||||
|
order: contentStyle.order,
|
||||||
|
transform: contentStyle.transform,
|
||||||
|
marginTop: contentStyle.marginTop,
|
||||||
|
marginBottom: contentStyle.marginBottom,
|
||||||
|
height: contentStyle.height,
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
container: {
|
||||||
|
display: containerStyle.display,
|
||||||
|
flexDirection: containerStyle.flexDirection,
|
||||||
|
position: containerStyle.position,
|
||||||
|
transform: containerStyle.transform,
|
||||||
|
},
|
||||||
|
panels,
|
||||||
|
visualOrder: [...panels]
|
||||||
|
.sort((left, right) => left.top - right.top || left.left - right.left)
|
||||||
|
.map(({ id }) => id),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const expectMobilePanelVisualOrder = async (page: Page, expectedOrder: readonly string[]) => {
|
||||||
|
await expect
|
||||||
|
.poll(() =>
|
||||||
|
page
|
||||||
|
.locator('.layout-mobile > [data-mobile-panel-id]')
|
||||||
|
.evaluateAll((elements) => elements.map((element) => element.getAttribute('data-mobile-panel-id')))
|
||||||
|
)
|
||||||
|
.toEqual(expectedOrder);
|
||||||
|
const audit = await inspectMobilePanelLayout(page);
|
||||||
|
expect(audit.container).toEqual({
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
position: 'static',
|
||||||
|
transform: 'none',
|
||||||
|
});
|
||||||
|
expect(audit.panels.map(({ id }) => id)).toEqual(expectedOrder);
|
||||||
|
expect(audit.visualOrder).toEqual(expectedOrder);
|
||||||
|
expect(audit.panels.every(({ left, right, width }) => left >= 0 && right <= 500 && width === 500)).toBe(true);
|
||||||
|
expect(
|
||||||
|
audit.panels.every((panel, index) => index === 0 || panel.top >= audit.panels[index - 1]!.bottom)
|
||||||
|
).toBe(true);
|
||||||
|
for (const panel of audit.panels) {
|
||||||
|
expect(panel.display, `${panel.id}: display`).not.toBe('none');
|
||||||
|
expect(['static', 'relative'], `${panel.id}: position`).toContain(panel.position);
|
||||||
|
expect(
|
||||||
|
panel.inset.every((value) => value === 'auto' || value === '0px'),
|
||||||
|
`${panel.id}: inset ${panel.inset.join(' ')}`
|
||||||
|
).toBe(true);
|
||||||
|
expect(panel.order, `${panel.id}: order`).toBe('0');
|
||||||
|
expect(panel.transform, `${panel.id}: transform`).toBe('none');
|
||||||
|
expect(panel.float, `${panel.id}: float`).toBe('none');
|
||||||
|
}
|
||||||
|
return audit;
|
||||||
|
};
|
||||||
|
|
||||||
const raisedButtonState = async (target: Locator) =>
|
const raisedButtonState = async (target: Locator) =>
|
||||||
target.evaluate((element) => {
|
target.evaluate((element) => {
|
||||||
const rect = element.getBoundingClientRect();
|
const rect = element.getBoundingClientRect();
|
||||||
@@ -2213,17 +2310,18 @@ test('the 939/940 boundary switches to the Ref-style 500px single document', asy
|
|||||||
]) {
|
]) {
|
||||||
await expect(page.locator(selector)).toBeVisible();
|
await expect(page.locator(selector)).toBeVisible();
|
||||||
}
|
}
|
||||||
const readMobilePanelOrder = () =>
|
const defaultOrder = [
|
||||||
page
|
'commands',
|
||||||
.locator('.layout-mobile > [data-mobile-panel-id]')
|
'nation-menu',
|
||||||
.evaluateAll((elements) => elements.map((element) => element.getAttribute('data-mobile-panel-id')));
|
'nation',
|
||||||
await expect
|
'general',
|
||||||
.poll(readMobilePanelOrder)
|
'city',
|
||||||
.toEqual(['commands', 'nation-menu', 'nation', 'general', 'city', 'map', 'records', 'global-menu', 'messages']);
|
'map',
|
||||||
await page.evaluate(() => {
|
'records',
|
||||||
localStorage.setItem(
|
'global-menu',
|
||||||
'sam.mobileMainPanelOrder.v1',
|
'messages',
|
||||||
JSON.stringify([
|
];
|
||||||
|
const customOrder = [
|
||||||
'messages',
|
'messages',
|
||||||
'map',
|
'map',
|
||||||
'commands',
|
'commands',
|
||||||
@@ -2233,29 +2331,24 @@ test('the 939/940 boundary switches to the Ref-style 500px single document', asy
|
|||||||
'city',
|
'city',
|
||||||
'records',
|
'records',
|
||||||
'global-menu',
|
'global-menu',
|
||||||
])
|
];
|
||||||
);
|
const reverseOrder = [...defaultOrder].reverse();
|
||||||
document.dispatchEvent(new CustomEvent('sam-mobile-main-panel-order-changed'));
|
const mobilePanelAudits = {
|
||||||
});
|
default: await expectMobilePanelVisualOrder(page, defaultOrder),
|
||||||
await expect
|
custom: null as Awaited<ReturnType<typeof inspectMobilePanelLayout>> | null,
|
||||||
.poll(readMobilePanelOrder)
|
reverse: null as Awaited<ReturnType<typeof inspectMobilePanelLayout>> | null,
|
||||||
.toEqual(['messages', 'map', 'commands', 'nation-menu', 'nation', 'general', 'city', 'records', 'global-menu']);
|
|
||||||
const customPanelGeometry = await page.locator('.layout-mobile > [data-mobile-panel-id]').evaluateAll((elements) =>
|
|
||||||
elements.map((element) => {
|
|
||||||
const rect = element.getBoundingClientRect();
|
|
||||||
return {
|
|
||||||
id: element.getAttribute('data-mobile-panel-id'),
|
|
||||||
top: rect.top,
|
|
||||||
bottom: rect.bottom,
|
|
||||||
left: rect.left,
|
|
||||||
right: rect.right,
|
|
||||||
};
|
};
|
||||||
})
|
await setMobilePanelOrder(page, customOrder);
|
||||||
|
mobilePanelAudits.custom = await expectMobilePanelVisualOrder(page, customOrder);
|
||||||
|
await setMobilePanelOrder(page, reverseOrder);
|
||||||
|
mobilePanelAudits.reverse = await expectMobilePanelVisualOrder(page, reverseOrder);
|
||||||
|
if (artifactRoot) {
|
||||||
|
await mkdir(artifactRoot, { recursive: true });
|
||||||
|
await writeFile(
|
||||||
|
resolve(artifactRoot, `${basePath.slice(1)}-mobile-panel-css-order-audit.json`),
|
||||||
|
`${JSON.stringify(mobilePanelAudits, null, 2)}\n`
|
||||||
);
|
);
|
||||||
expect(customPanelGeometry.every(({ left, right }) => left >= 0 && right <= 500)).toBe(true);
|
}
|
||||||
expect(
|
|
||||||
customPanelGeometry.every((panel, index) => index === 0 || panel.top >= customPanelGeometry[index - 1]!.bottom)
|
|
||||||
).toBe(true);
|
|
||||||
await persistArtifact(page, `${basePath.slice(1)}-mobile-500`);
|
await persistArtifact(page, `${basePath.slice(1)}-mobile-500`);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -2556,6 +2649,27 @@ test('real mobile devices initially fit the complete 500px game canvas', async (
|
|||||||
expect(mainGeometry.documentScrollWidth).toBeLessThanOrEqual(mainGeometry.innerWidth);
|
expect(mainGeometry.documentScrollWidth).toBeLessThanOrEqual(mainGeometry.innerWidth);
|
||||||
expect(mainGeometry.canvas).toEqual({ left: 0, right: 500, width: 500 });
|
expect(mainGeometry.canvas).toEqual({ left: 0, right: 500, width: 500 });
|
||||||
expect(mainGeometry.canvas.right).toBeLessThanOrEqual((mainGeometry.visualViewportWidth ?? 0) + 0.01);
|
expect(mainGeometry.canvas.right).toBeLessThanOrEqual((mainGeometry.visualViewportWidth ?? 0) + 0.01);
|
||||||
|
let physicalPanelOrderAudit: unknown = null;
|
||||||
|
if (deviceWidth === 390) {
|
||||||
|
const defaultOrder = [
|
||||||
|
'commands',
|
||||||
|
'nation-menu',
|
||||||
|
'nation',
|
||||||
|
'general',
|
||||||
|
'city',
|
||||||
|
'map',
|
||||||
|
'records',
|
||||||
|
'global-menu',
|
||||||
|
'messages',
|
||||||
|
];
|
||||||
|
const reverseOrder = [...defaultOrder].reverse();
|
||||||
|
const defaultAudit = await expectMobilePanelVisualOrder(mobilePage, defaultOrder);
|
||||||
|
await setMobilePanelOrder(mobilePage, reverseOrder);
|
||||||
|
const reverseAudit = await expectMobilePanelVisualOrder(mobilePage, reverseOrder);
|
||||||
|
physicalPanelOrderAudit = { default: defaultAudit, reverse: reverseAudit };
|
||||||
|
await setMobilePanelOrder(mobilePage, defaultOrder);
|
||||||
|
await expectMobilePanelVisualOrder(mobilePage, defaultOrder);
|
||||||
|
}
|
||||||
if (artifactRoot) {
|
if (artifactRoot) {
|
||||||
await mkdir(artifactRoot, { recursive: true });
|
await mkdir(artifactRoot, { recursive: true });
|
||||||
await mobilePage.screenshot({
|
await mobilePage.screenshot({
|
||||||
@@ -2599,7 +2713,11 @@ test('real mobile devices initially fit the complete 500px game canvas', async (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
measurements[String(deviceWidth)] = { main: mainGeometry, routes: routeGeometry };
|
measurements[String(deviceWidth)] = {
|
||||||
|
main: mainGeometry,
|
||||||
|
mobilePanelOrder: physicalPanelOrderAudit,
|
||||||
|
routes: routeGeometry,
|
||||||
|
};
|
||||||
await context.close();
|
await context.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user