test: prove TechLog UI migration parity
This commit is contained in:
@@ -1,58 +1,40 @@
|
||||
import AxeBuilder from "@axe-core/playwright";
|
||||
import { expect, test } from "../support/browser/strict-browser-test.ts";
|
||||
import { ROUTE_REGISTRY } from "../../src/features/installed-feature-contracts.ts";
|
||||
|
||||
for (const route of Object.values(ROUTE_REGISTRY).map((definition) => {
|
||||
if (definition.path === "*") return "/not-found";
|
||||
return definition.path.replace(":resourceId", "reference-1");
|
||||
})) {
|
||||
test(`@a11y ${route} has no critical or serious axe violations`, async ({
|
||||
import { expect, test } from "../support/browser/strict-browser-test.ts";
|
||||
import {
|
||||
gotoTechLog,
|
||||
TECH_LOG_CANONICAL_ROUTES,
|
||||
} from "../support/browser/tech-log-fixtures.ts";
|
||||
|
||||
for (const route of TECH_LOG_CANONICAL_ROUTES) {
|
||||
test(`@a11y ${route.routeId} has no critical or serious Axe violations`, async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.goto(route);
|
||||
await expect(page.getByRole("main")).toBeVisible();
|
||||
await gotoTechLog(page, route.path);
|
||||
const results = await new AxeBuilder({ page })
|
||||
.withTags(["wcag2a", "wcag2aa", "wcag21a", "wcag21aa"])
|
||||
.analyze();
|
||||
const blocking = results.violations.filter((violation) =>
|
||||
["critical", "serious"].includes(violation.impact ?? ""),
|
||||
);
|
||||
expect(blocking).toEqual([]);
|
||||
expect(
|
||||
results.violations.filter((violation) =>
|
||||
["critical", "serious"].includes(violation.impact ?? ""),
|
||||
),
|
||||
).toEqual([]);
|
||||
});
|
||||
}
|
||||
|
||||
test("@a11y keyboard reaches the primary route action with visible focus", async ({
|
||||
test("@a11y keyboard reaches a visible Public navigation focus indicator", async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.goto("/");
|
||||
const action = page.getByRole("link", { name: "플랫폼 구성 보기" });
|
||||
await expect(action).toBeVisible();
|
||||
await page.keyboard.press("Tab");
|
||||
await gotoTechLog(page, "/");
|
||||
const action = page.getByRole("link", { name: "TechLog 홈" });
|
||||
await action.focus();
|
||||
await expect(action).toBeFocused();
|
||||
await expect(action).toHaveCSS("outline-style", "solid");
|
||||
});
|
||||
|
||||
test("@a11y reduced-motion policy disables long animation", async ({ page }) => {
|
||||
await page.emulateMedia({ reducedMotion: "reduce" });
|
||||
await page.goto("/");
|
||||
const duration = await page
|
||||
.locator("body")
|
||||
.evaluate((body) => getComputedStyle(body).animationDuration);
|
||||
expect(["0s", "0.00001s", "1e-05s"]).toContain(duration);
|
||||
});
|
||||
|
||||
test("@a11y opened design-system dialog has no blocking violations", async ({
|
||||
test("@a11y reduced-motion preference leaves no running document animations", async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.goto("/examples/ui");
|
||||
await page.getByRole("button", { name: "모달 열기" }).click();
|
||||
const results = await new AxeBuilder({ page })
|
||||
.include(".ui-dialog")
|
||||
.withTags(["wcag2a", "wcag2aa", "wcag21a", "wcag21aa"])
|
||||
.analyze();
|
||||
expect(
|
||||
results.violations.filter((violation) =>
|
||||
["critical", "serious"].includes(violation.impact ?? ""),
|
||||
),
|
||||
).toEqual([]);
|
||||
await gotoTechLog(page, "/");
|
||||
expect(await page.evaluate(() => document.getAnimations().length)).toBe(0);
|
||||
});
|
||||
|
||||
+14
-17
@@ -1,7 +1,8 @@
|
||||
import { expect, test } from "../support/browser/strict-browser-test.ts";
|
||||
import { gotoTechLog } from "../support/browser/tech-log-fixtures.ts";
|
||||
|
||||
test("boots the public app shell", async ({ page }) => {
|
||||
await page.goto("/");
|
||||
await gotoTechLog(page, "/");
|
||||
await expect(page.getByRole("heading", { level: 1 })).toHaveText(
|
||||
"Tech Log",
|
||||
);
|
||||
@@ -9,36 +10,32 @@ test("boots the public app shell", async ({ page }) => {
|
||||
await expect(page.getByRole("main")).toBeVisible();
|
||||
});
|
||||
|
||||
test("navigates to a registry-backed example without a page reload", async ({
|
||||
test("navigates to a registry-backed Public route without a page reload", async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.goto("/");
|
||||
await gotoTechLog(page, "/");
|
||||
await page
|
||||
.getByRole("navigation", { name: "주요 탐색" })
|
||||
.getByRole("link", { name: "화면 상태" })
|
||||
.getByRole("link", { name: "프로젝트" })
|
||||
.click();
|
||||
|
||||
await expect(page).toHaveURL(/\/examples\/states$/);
|
||||
await expect(page).toHaveURL(/\/projects$/);
|
||||
await expect(
|
||||
page.getByRole("heading", { level: 1, name: "화면 상태" }),
|
||||
).toBeFocused();
|
||||
page.getByRole("heading", { level: 1, name: "프로젝트" }),
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test("provides an escape-dismissible mobile navigation", async ({ page }) => {
|
||||
await page.setViewportSize({ width: 390, height: 844 });
|
||||
await page.goto("/");
|
||||
const menu = page.getByRole("button", { name: "메뉴", exact: true });
|
||||
await gotoTechLog(page, "/");
|
||||
const menu = page.locator("details.mobile-nav > summary");
|
||||
|
||||
await menu.click();
|
||||
await expect(menu).toHaveAttribute("aria-expanded", "true");
|
||||
await expect(page.getByRole("navigation", { name: "주요 탐색" })).toBeVisible();
|
||||
await expect(page.locator(".ui-drawer")).toHaveJSProperty("open", true);
|
||||
expect(
|
||||
await page.locator(".ui-drawer").evaluate((drawer) => drawer.matches(":modal")),
|
||||
).toBe(true);
|
||||
await expect(page.locator("details.mobile-nav")).toHaveAttribute("open", "");
|
||||
await expect(page.getByRole("navigation", { name: "모바일 주요 탐색" })).toBeVisible();
|
||||
|
||||
await page.keyboard.press("Escape");
|
||||
await expect(menu).toHaveAttribute("aria-expanded", "false");
|
||||
await expect(page.getByRole("navigation", { name: "주요 탐색" })).toBeHidden();
|
||||
await expect(page.locator("details.mobile-nav")).not.toHaveAttribute("open", "");
|
||||
await expect(page.getByRole("navigation", { name: "모바일 주요 탐색" })).toBeHidden();
|
||||
await expect(menu).toBeFocused();
|
||||
});
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
import { expect, test } from "../support/browser/strict-browser-test.ts";
|
||||
|
||||
test("boots and navigates the compact production shell", async ({ page }) => {
|
||||
await page.goto("/");
|
||||
await expect(page.getByRole("main")).toBeVisible();
|
||||
const menu = page.getByRole("button", { name: "메뉴", exact: true });
|
||||
await expect(menu).toHaveCSS("min-width", "44px");
|
||||
await menu.click();
|
||||
const navigation = page.getByRole("navigation", { name: "주요 탐색" });
|
||||
await expect(navigation).toBeVisible();
|
||||
// The drawer is a non-modal dialog, so page content stays in the
|
||||
// accessibility tree while it is open. Scope to the navigation and match the
|
||||
// whole name, or a route call to action on the page behind it also matches.
|
||||
await navigation
|
||||
.getByRole("link", { name: "UI 구성요소", exact: true })
|
||||
.click();
|
||||
await expect(page).toHaveURL(/\/examples\/ui$/);
|
||||
await expect(page.locator("html")).toHaveAttribute("data-build-id", "local-build");
|
||||
const overflow = await page.evaluate(
|
||||
() => document.documentElement.scrollWidth - window.innerWidth,
|
||||
);
|
||||
expect(overflow).toBeLessThanOrEqual(1);
|
||||
});
|
||||
@@ -1,29 +0,0 @@
|
||||
import { expect, test } from "../support/browser/strict-browser-test.ts";
|
||||
|
||||
test("runs menu typeahead, tabs and duplicate toast interactions", async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.goto("/examples/ui");
|
||||
|
||||
const menuTrigger = page.getByRole("button", { name: "작업 메뉴" });
|
||||
await menuTrigger.focus();
|
||||
await page.keyboard.press("ArrowDown");
|
||||
await page.keyboard.type("toast");
|
||||
const toastItem = page.getByRole("menuitem", { name: "Toast 표시" });
|
||||
await expect(toastItem).toBeFocused();
|
||||
await page.keyboard.press("Enter");
|
||||
await expect(page.getByText("예제가 저장되었습니다.")).toBeVisible();
|
||||
await expect(menuTrigger).toBeFocused();
|
||||
|
||||
const tokenTab = page.getByRole("tab", { name: "토큰" });
|
||||
await tokenTab.focus();
|
||||
await page.keyboard.press("ArrowRight");
|
||||
const primitiveTab = page.getByRole("tab", { name: "프리미티브" });
|
||||
await expect(primitiveTab).toBeFocused();
|
||||
await expect(primitiveTab).toHaveAttribute("aria-selected", "false");
|
||||
await page.keyboard.press("Enter");
|
||||
await expect(primitiveTab).toHaveAttribute("aria-selected", "true");
|
||||
await expect(
|
||||
page.getByRole("tabpanel", { name: "프리미티브" }),
|
||||
).toContainText("native semantics");
|
||||
});
|
||||
@@ -1,41 +0,0 @@
|
||||
import { expect, test } from "../support/browser/strict-browser-test.ts";
|
||||
|
||||
test("switches the shell locale and keeps pseudo-locale copy within compact layout", async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.setViewportSize({ width: 320, height: 720 });
|
||||
await page.goto("/");
|
||||
|
||||
const locale = page.getByRole("combobox", { name: "언어" });
|
||||
await locale.selectOption("en-US");
|
||||
await expect(page.locator("html")).toHaveAttribute("lang", "en-US");
|
||||
await expect(page.locator("html")).toHaveAttribute("dir", "ltr");
|
||||
await expect(page.getByRole("button", { name: "Menu" })).toBeVisible();
|
||||
|
||||
await page.getByRole("combobox", { name: "Language" }).selectOption("en-XA");
|
||||
await expect(page.locator("html")).toHaveAttribute("lang", "en-XA");
|
||||
const dimensions = await page.evaluate(() => ({
|
||||
clientWidth: document.documentElement.clientWidth,
|
||||
scrollWidth: document.documentElement.scrollWidth,
|
||||
}));
|
||||
expect(dimensions.scrollWidth).toBeLessThanOrEqual(dimensions.clientWidth);
|
||||
await expect(page.getByRole("button", { name: /Ménú/ })).toBeVisible();
|
||||
});
|
||||
|
||||
test("applies RTL to the shell and modal navigation without changing action semantics", async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.setViewportSize({ width: 390, height: 844 });
|
||||
await page.goto("/");
|
||||
await page.getByRole("combobox", { name: "언어" }).selectOption("ar-EG");
|
||||
|
||||
await expect(page.locator("html")).toHaveAttribute("lang", "ar-EG");
|
||||
await expect(page.locator("html")).toHaveAttribute("dir", "rtl");
|
||||
const trigger = page.getByRole("button", { name: "Menu" });
|
||||
await trigger.click();
|
||||
await expect(page.getByRole("dialog", { name: "Menu" })).toBeVisible();
|
||||
await expect(page.getByRole("navigation", { name: "Primary navigation" }))
|
||||
.toHaveCount(1);
|
||||
await page.keyboard.press("Escape");
|
||||
await expect(trigger).toBeFocused();
|
||||
});
|
||||
@@ -1,36 +1,36 @@
|
||||
import { expect, test } from "../support/browser/strict-browser-test.ts";
|
||||
import {
|
||||
gotoTechLog,
|
||||
horizontalOverflow,
|
||||
TECH_LOG_CANONICAL_ROUTES,
|
||||
} from "../support/browser/tech-log-fixtures.ts";
|
||||
|
||||
test("reflows the UI gallery at the 320px minimum without horizontal overflow", async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.setViewportSize({ width: 320, height: 720 });
|
||||
await page.goto("/examples/ui");
|
||||
await expect(
|
||||
page.getByRole("heading", { level: 1, name: "UI 구성요소" }),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole("button", { name: "메뉴", exact: true }),
|
||||
).toBeVisible();
|
||||
for (const route of TECH_LOG_CANONICAL_ROUTES) {
|
||||
for (const width of [360, 1440] as const) {
|
||||
test(`${route.routeId} has no horizontal overflow at ${width}px`, async ({ page }) => {
|
||||
await page.setViewportSize({ width, height: 900 });
|
||||
await gotoTechLog(page, route.path);
|
||||
const dimensions = await horizontalOverflow(page);
|
||||
expect(dimensions.scrollWidth).toBeLessThanOrEqual(dimensions.clientWidth);
|
||||
if (route.routeId !== "NOT_FOUND") {
|
||||
await expect(page.getByRole("main")).toBeVisible();
|
||||
await expect(page.getByRole("heading", { level: 1 })).toBeVisible();
|
||||
} else {
|
||||
await expect(page.getByText("Not Found", { exact: true })).toBeVisible();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const viewport = await page.evaluate(() => ({
|
||||
clientWidth: document.documentElement.clientWidth,
|
||||
scrollWidth: document.documentElement.scrollWidth,
|
||||
}));
|
||||
expect(viewport.scrollWidth).toBeLessThanOrEqual(viewport.clientWidth);
|
||||
});
|
||||
|
||||
test("keeps desktop navigation and two-column examples at wide viewports", async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.setViewportSize({ width: 1440, height: 900 });
|
||||
await page.goto("/examples/ui");
|
||||
|
||||
await expect(page.getByRole("navigation", { name: "주요 탐색" })).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole("button", { name: "메뉴", exact: true }),
|
||||
).toBeHidden();
|
||||
await expect(page.locator(".component-grid--two").first()).toHaveCSS(
|
||||
"grid-template-columns",
|
||||
/.+px .+px/,
|
||||
test("compact Public controls retain 44px targets", async ({ page }) => {
|
||||
await page.setViewportSize({ width: 375, height: 812 });
|
||||
await gotoTechLog(page, "/");
|
||||
await expect(page.locator("details.mobile-nav > summary")).toHaveCSS(
|
||||
"min-height",
|
||||
"44px",
|
||||
);
|
||||
await expect(page.getByRole("button", { name: "검색 열기" })).toHaveCSS(
|
||||
"min-height",
|
||||
"44px",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
import AxeBuilder from "@axe-core/playwright";
|
||||
|
||||
import { expect, test } from "../support/browser/strict-browser-test.ts";
|
||||
import { gotoTechLog } from "../support/browser/tech-log-fixtures.ts";
|
||||
|
||||
function blockingViolations(results: Awaited<ReturnType<AxeBuilder["analyze"]>>) {
|
||||
return results.violations.filter((violation) =>
|
||||
["critical", "serious"].includes(violation.impact ?? ""),
|
||||
);
|
||||
}
|
||||
|
||||
test("Public shell exposes one main landmark, one h1, labelled navigation, and current link", async ({
|
||||
page,
|
||||
}) => {
|
||||
await gotoTechLog(page, "/projects");
|
||||
await expect(page.getByRole("main")).toHaveCount(1);
|
||||
await expect(page.getByRole("heading", { level: 1 })).toHaveCount(1);
|
||||
await expect(page.getByRole("navigation", { name: "주요 탐색" })).toBeVisible();
|
||||
await expect(page.getByRole("link", { name: "프로젝트", exact: true })).toHaveAttribute(
|
||||
"aria-current",
|
||||
"page",
|
||||
);
|
||||
});
|
||||
|
||||
test("Studio mobile navigation is keyboard reachable and exposes its expanded state", async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.setViewportSize({ width: 390, height: 844 });
|
||||
await gotoTechLog(page, "/studio");
|
||||
const trigger = page.locator(".studio-menu-trigger");
|
||||
await trigger.focus();
|
||||
await page.keyboard.press("Enter");
|
||||
await expect(trigger).toHaveAttribute("aria-expanded", "true");
|
||||
await expect(page.getByRole("navigation", { name: "Studio 모바일 탐색" })).toBeVisible();
|
||||
});
|
||||
|
||||
test("dirty-leave dialog is labelled, traps focus, and restores the invoking link", async ({
|
||||
page,
|
||||
}) => {
|
||||
await gotoTechLog(
|
||||
page,
|
||||
"/studio/documents/11111111-1111-4111-8111-111111111111/edit",
|
||||
);
|
||||
await page.getByLabel("요약").fill("저장하지 않은 접근성 검증 변경");
|
||||
const trigger = page.getByRole("link", { name: "게시 기록", exact: true }).first();
|
||||
await trigger.click();
|
||||
const dialog = page.getByRole("dialog", { name: "저장하지 않은 변경" });
|
||||
await expect(dialog).toBeVisible();
|
||||
await expect(dialog.getByRole("button", { name: "이 페이지에 머무르기" })).toBeFocused();
|
||||
await page.keyboard.press("Escape");
|
||||
await expect(dialog).toBeHidden();
|
||||
await expect(trigger).toBeFocused();
|
||||
});
|
||||
|
||||
test("open Public search and Studio leave dialogs have no blocking Axe findings", async ({
|
||||
page,
|
||||
}) => {
|
||||
await gotoTechLog(page, "/");
|
||||
await page.getByRole("button", { name: "검색 열기" }).click();
|
||||
let results = await new AxeBuilder({ page })
|
||||
.include("dialog")
|
||||
.withTags(["wcag2a", "wcag2aa", "wcag21a", "wcag21aa"])
|
||||
.analyze();
|
||||
expect(blockingViolations(results)).toEqual([]);
|
||||
|
||||
await gotoTechLog(
|
||||
page,
|
||||
"/studio/documents/11111111-1111-4111-8111-111111111111/edit",
|
||||
);
|
||||
await page.getByLabel("요약").fill("dialog axe state");
|
||||
await page.getByRole("link", { name: "게시 기록", exact: true }).first().click();
|
||||
results = await new AxeBuilder({ page })
|
||||
.include("dialog")
|
||||
.withTags(["wcag2a", "wcag2aa", "wcag21a", "wcag21aa"])
|
||||
.analyze();
|
||||
expect(blockingViolations(results)).toEqual([]);
|
||||
});
|
||||
@@ -0,0 +1,44 @@
|
||||
import { expect, test } from "../support/browser/strict-browser-test.ts";
|
||||
import {
|
||||
gotoTechLog,
|
||||
TECH_LOG_PUBLIC_FIXTURE_PATHS,
|
||||
} from "../support/browser/tech-log-fixtures.ts";
|
||||
|
||||
for (const path of TECH_LOG_PUBLIC_FIXTURE_PATHS) {
|
||||
test(`renders known Public fixture ${path}`, async ({ page }) => {
|
||||
await gotoTechLog(page, path);
|
||||
await expect(page.locator(".site-frame")).toBeVisible();
|
||||
await expect(page.getByRole("heading", { level: 1 })).toBeVisible();
|
||||
await expect(page.getByText("404", { exact: true })).toHaveCount(0);
|
||||
});
|
||||
}
|
||||
|
||||
test("search dialog traps focus, closes with Escape, and restores its trigger", async ({
|
||||
page,
|
||||
}) => {
|
||||
await gotoTechLog(page, "/");
|
||||
const trigger = page.getByRole("button", { name: "TechLog 검색 열기" });
|
||||
await trigger.click();
|
||||
const dialog = page.getByRole("dialog", { name: "TechLog 검색" });
|
||||
await expect(dialog).toBeVisible();
|
||||
await expect(dialog.getByRole("searchbox", { name: "검색어" })).toBeFocused();
|
||||
await page.keyboard.press("Tab");
|
||||
await page.keyboard.press("Shift+Tab");
|
||||
await expect(dialog.getByRole("searchbox", { name: "검색어" })).toBeFocused();
|
||||
await page.keyboard.press("Escape");
|
||||
await expect(dialog).toBeHidden();
|
||||
await expect(trigger).toBeFocused();
|
||||
});
|
||||
|
||||
test("search and explore navigation preserve canonical URL state", async ({ page }) => {
|
||||
await gotoTechLog(page, "/explore");
|
||||
await page.getByLabel("유형").selectOption("CASE");
|
||||
await page.getByRole("button", { name: "적용" }).click();
|
||||
await expect(page).toHaveURL(/\/explore\?type=CASE$/);
|
||||
|
||||
await page.getByRole("button", { name: "TechLog 검색 열기" }).click();
|
||||
await page.getByRole("searchbox", { name: "검색어" }).fill("JPA");
|
||||
await page.getByRole("link", { name: /전체 검색 결과 보기/ }).click();
|
||||
await expect(page).toHaveURL(/\/search\?q=JPA$/);
|
||||
await expect(page.getByRole("heading", { level: 1, name: "검색" })).toBeVisible();
|
||||
});
|
||||
@@ -0,0 +1,41 @@
|
||||
import { expect, test } from "../support/browser/strict-browser-test.ts";
|
||||
import {
|
||||
gotoTechLog,
|
||||
horizontalOverflow,
|
||||
TECH_LOG_BREAKPOINT_WIDTHS,
|
||||
} from "../support/browser/tech-log-fixtures.ts";
|
||||
|
||||
for (const width of TECH_LOG_BREAKPOINT_WIDTHS) {
|
||||
test(`Public shell has no overflow and preserves the ${width}px breakpoint contract`, async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.setViewportSize({ width, height: 900 });
|
||||
await gotoTechLog(page, "/");
|
||||
const dimensions = await horizontalOverflow(page);
|
||||
expect(dimensions.scrollWidth).toBeLessThanOrEqual(dimensions.clientWidth);
|
||||
|
||||
if (width <= 1050) {
|
||||
await expect(page.locator("details.mobile-nav > summary")).toBeVisible();
|
||||
await expect(page.getByRole("navigation", { name: "주요 탐색" })).toBeHidden();
|
||||
} else {
|
||||
await expect(page.locator("details.mobile-nav > summary")).toBeHidden();
|
||||
await expect(page.getByRole("navigation", { name: "주요 탐색" })).toBeVisible();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for (const width of [1050, 1024, 980, 900, 820, 768, 767, 420, 390, 375] as const) {
|
||||
test(`Studio editor has no overflow and preserves the ${width}px editor transition`, async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.setViewportSize({ width, height: 1000 });
|
||||
await gotoTechLog(
|
||||
page,
|
||||
"/studio/documents/11111111-1111-4111-8111-111111111111/edit",
|
||||
);
|
||||
const dimensions = await horizontalOverflow(page);
|
||||
expect(dimensions.scrollWidth).toBeLessThanOrEqual(dimensions.clientWidth);
|
||||
await expect(page.getByRole("heading", { level: 1 })).toBeVisible();
|
||||
await expect(page.getByRole("button", { name: "저장" })).toBeVisible();
|
||||
});
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
import AxeBuilder from "@axe-core/playwright";
|
||||
import { expect, test } from "../support/browser/strict-browser-test.ts";
|
||||
|
||||
test("persists an explicit color scheme through the storage contract", async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.goto("/");
|
||||
const selector = page.getByRole("combobox", { name: "색상 테마" });
|
||||
|
||||
await selector.selectOption("dark");
|
||||
await expect(page.locator("html")).toHaveAttribute("data-theme", "dark");
|
||||
await expect(page.locator("html")).toHaveCSS("color-scheme", "dark");
|
||||
const results = await new AxeBuilder({ page })
|
||||
.withTags(["wcag2a", "wcag2aa", "wcag21a", "wcag21aa"])
|
||||
.analyze();
|
||||
expect(
|
||||
results.violations.filter((violation) =>
|
||||
["critical", "serious"].includes(violation.impact ?? ""),
|
||||
),
|
||||
).toEqual([]);
|
||||
|
||||
await page.reload();
|
||||
await expect(selector).toHaveValue("dark");
|
||||
await expect(page.locator("html")).toHaveAttribute("data-theme", "dark");
|
||||
});
|
||||
|
||||
test("tracks operating-system changes while system preference is selected", async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.emulateMedia({ colorScheme: "dark" });
|
||||
await page.goto("/");
|
||||
const selector = page.getByRole("combobox", { name: "색상 테마" });
|
||||
await selector.selectOption("system");
|
||||
await expect(page.locator("html")).toHaveAttribute("data-theme", "dark");
|
||||
|
||||
await page.emulateMedia({ colorScheme: "light" });
|
||||
await expect(page.locator("html")).toHaveAttribute("data-theme", "light");
|
||||
});
|
||||
Reference in New Issue
Block a user