모바일 메뉴와 주요 화면 UX 개선 및 시각 검수
This commit is contained in:
parent
89b5093ec7
commit
e8b770e4d9
50 changed files with 3189 additions and 457 deletions
|
|
@ -47,6 +47,111 @@ async function expectNoLocalStageDemoControl(page: Page) {
|
|||
await expect(page.locator(".sx-track__advance")).toHaveCount(0);
|
||||
}
|
||||
|
||||
async function expectPrestartSummaryReadableOnMobile(page: Page) {
|
||||
if (!(await page.evaluate(() => window.matchMedia("(max-width: 720px)").matches))) return;
|
||||
|
||||
await page.evaluate(() => {
|
||||
const section = document.querySelector(".sx-page--prestart");
|
||||
let owner = document.scrollingElement;
|
||||
for (let node = section?.parentElement; node; node = node.parentElement) {
|
||||
const style = window.getComputedStyle(node);
|
||||
if (/(auto|scroll)/.test(style.overflowY) && node.scrollHeight > node.clientHeight + 1) {
|
||||
owner = node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!owner) throw new Error("Expected prestart scroll owner");
|
||||
owner.scrollTop = Math.round((owner.scrollHeight - owner.clientHeight) / 2);
|
||||
});
|
||||
await page.evaluate(() => new Promise((resolve) => requestAnimationFrame(resolve)));
|
||||
|
||||
const metrics = await page.evaluate(() => {
|
||||
const facts = document.querySelector<HTMLElement>(".sx-page--prestart .sx-prestart__facts");
|
||||
const complaint = facts?.querySelector<HTMLElement>(":scope > div:first-child");
|
||||
const complaintText = complaint?.querySelector<HTMLElement>("dd");
|
||||
const actions = document.querySelector<HTMLElement>(".sx-page--prestart .sx-prestart__actions");
|
||||
if (!facts || !complaint || !complaintText || !actions) return null;
|
||||
|
||||
const complaintRect = complaint.getBoundingClientRect();
|
||||
const factsRect = facts.getBoundingClientRect();
|
||||
const complaintStyle = window.getComputedStyle(complaintText);
|
||||
const actionsStyle = window.getComputedStyle(actions);
|
||||
const actionsRect = actions.getBoundingClientRect();
|
||||
const navigation = document.querySelector<HTMLElement>(".vg-nav");
|
||||
const navigationRect = navigation?.getBoundingClientRect();
|
||||
const startButton = actions.querySelector<HTMLElement>(".vg-btn");
|
||||
const actionDescription = actions.querySelector<HTMLElement>("span");
|
||||
const alphaMatch = actionsStyle.backgroundColor.match(/^rgba\([^,]+,\s*[^,]+,\s*[^,]+,\s*([0-9.]+)\)$/);
|
||||
const actionsAlpha = actionsStyle.backgroundColor === "transparent"
|
||||
? 0
|
||||
: alphaMatch
|
||||
? Number.parseFloat(alphaMatch[1])
|
||||
: 1;
|
||||
const hitPoints = [startButton, actionDescription, actions]
|
||||
.filter((element): element is HTMLElement => Boolean(element))
|
||||
.map((element) => {
|
||||
const rect = element.getBoundingClientRect();
|
||||
const x = Math.round(rect.left + rect.width / 2);
|
||||
const y = Math.round(rect.top + rect.height / 2);
|
||||
const hit = document.elementFromPoint(x, y);
|
||||
return {
|
||||
x,
|
||||
y,
|
||||
hitClass: hit instanceof HTMLElement ? hit.className : hit?.nodeName ?? null,
|
||||
belongsToAction: Boolean(hit && actions.contains(hit)),
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
complaintWidth: Math.round(complaintRect.width),
|
||||
factsWidth: Math.round(factsRect.width),
|
||||
fontSize: Number.parseFloat(complaintStyle.fontSize),
|
||||
lineHeight: Number.parseFloat(complaintStyle.lineHeight),
|
||||
actionsBackground: actionsStyle.backgroundColor,
|
||||
actionsAlpha,
|
||||
actionsPosition: actionsStyle.position,
|
||||
actionsBottom: Math.round(actionsRect.bottom),
|
||||
navigationTop: navigationRect ? Math.round(navigationRect.top) : null,
|
||||
hitPoints,
|
||||
};
|
||||
});
|
||||
|
||||
expect(metrics, "Expected mobile prestart facts and start action").not.toBeNull();
|
||||
expect(metrics!.complaintWidth, `Complaint should span the facts row: ${JSON.stringify(metrics)}`).toBeGreaterThanOrEqual(
|
||||
metrics!.factsWidth - 2,
|
||||
);
|
||||
expect(metrics!.fontSize, `Complaint text is too small: ${JSON.stringify(metrics)}`).toBeGreaterThanOrEqual(14);
|
||||
expect(metrics!.lineHeight, `Complaint line height is too tight: ${JSON.stringify(metrics)}`).toBeGreaterThanOrEqual(21);
|
||||
expect(metrics!.actionsAlpha, `Start action must be opaque: ${JSON.stringify(metrics)}`).toBe(1);
|
||||
expect(metrics!.actionsPosition, `Start action must stay at the mobile bottom: ${JSON.stringify(metrics)}`).toBe("fixed");
|
||||
expect(metrics!.navigationTop, `Expected mobile navigation: ${JSON.stringify(metrics)}`).not.toBeNull();
|
||||
expect(metrics!.actionsBottom, `Start action must stay above mobile navigation: ${JSON.stringify(metrics)}`).toBeLessThanOrEqual(
|
||||
metrics!.navigationTop! + 1,
|
||||
);
|
||||
expect(metrics!.hitPoints, `Expected three start action hit points: ${JSON.stringify(metrics)}`).toHaveLength(3);
|
||||
expect(metrics!.hitPoints.every((point) => point.belongsToAction), `Start action is covered at its controls: ${JSON.stringify(metrics)}`).toBe(true);
|
||||
|
||||
const goals = page.locator(".sx-page--prestart .sx-goals__grid button");
|
||||
for (const goal of await goals.all()) {
|
||||
await goal.scrollIntoViewIfNeeded();
|
||||
const geometry = await goal.evaluate((button) => {
|
||||
const action = document.querySelector<HTMLElement>(".sx-page--prestart .sx-prestart__actions");
|
||||
const buttonRect = button.getBoundingClientRect();
|
||||
const actionRect = action?.getBoundingClientRect();
|
||||
return {
|
||||
top: Math.round(buttonRect.top),
|
||||
bottom: Math.round(buttonRect.bottom),
|
||||
actionTop: actionRect ? Math.round(actionRect.top) : null,
|
||||
};
|
||||
});
|
||||
expect(geometry.top, `Goal is above the viewport: ${JSON.stringify(geometry)}`).toBeGreaterThanOrEqual(0);
|
||||
expect(geometry.actionTop, `Expected fixed start action: ${JSON.stringify(geometry)}`).not.toBeNull();
|
||||
expect(geometry.bottom, `Goal is hidden behind the start action: ${JSON.stringify(geometry)}`).toBeLessThanOrEqual(
|
||||
geometry.actionTop! - 1,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async function expectMobileContextIfNarrow(page: Page) {
|
||||
const isNarrow = await page.evaluate(() =>
|
||||
window.matchMedia("(max-width: 1180px)").matches,
|
||||
|
|
@ -473,6 +578,7 @@ test.describe("learner session full-screen layout", () => {
|
|||
|
||||
await expect(page.getByRole("button", { name: "회기 시작" })).toBeVisible();
|
||||
await expectNoHorizontalOverflow(page);
|
||||
await expectPrestartSummaryReadableOnMobile(page);
|
||||
|
||||
await page.getByRole("button", { name: "회기 시작" }).click();
|
||||
await completeAlliancePreCheckpoint(page);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue