import AxeBuilder from "@axe-core/playwright"; import { expect, test } from "@playwright/test"; import { apiBaseUrl } from "./api-base-url"; const sessionStorageKey = "ai-character-admin-auth-session"; async function openShell(page: import("@playwright/test").Page): Promise { await page.route(`${apiBaseUrl}/api/v2/admin/ai-characters?page=0&size=20`, async (route) => { await route.fulfill({ contentType: "application/json", json: { success: true, message: null, data: null, errorProperty: null }, }); }); await page.addInitScript( ([key, value]) => { window.sessionStorage.setItem(key, value); }, [sessionStorageKey, JSON.stringify({ token: "admin-token", role: "ADMIN" })], ); await page.goto("/ai-characters"); await expect(page.getByRole("heading", { name: "AI 캐릭터", exact: true })).toBeVisible(); } test("keeps shell controls visible without horizontal overflow at 320px and 200 percent zoom", async ({ page }) => { await page.setViewportSize({ width: 320, height: 640 }); await openShell(page); await page.evaluate(() => { document.documentElement.style.zoom = "2"; }); const overflow = await page.evaluate(() => document.documentElement.scrollWidth > document.documentElement.clientWidth); await expect(page.getByRole("button", { name: "모바일 메뉴 열기" })).toBeInViewport(); await expect(page.getByRole("button", { name: "로그아웃" })).toBeInViewport(); expect(overflow).toBe(false); }); test("keeps the open mobile menu visible and keyboard-contained at 320px and 200 percent zoom", async ({ page }) => { await page.setViewportSize({ width: 320, height: 640 }); await openShell(page); await page.evaluate(() => { document.documentElement.style.zoom = "2"; }); await page.getByRole("button", { name: "모바일 메뉴 열기" }).focus(); await page.keyboard.press("Enter"); const closeButton = page.getByRole("button", { name: "모바일 메뉴 닫기" }); const navLink = page.getByRole("navigation", { name: "모바일 주 메뉴" }).getByRole("link", { name: "AI 캐릭터" }); await expect(closeButton).toBeFocused(); await expect(closeButton).toBeInViewport(); await expect(navLink).toBeInViewport(); await page.keyboard.press("Shift+Tab"); await expect(navLink).toBeFocused(); await page.keyboard.press("Tab"); await expect(closeButton).toBeFocused(); const overflow = await page.evaluate(() => document.documentElement.scrollWidth > document.documentElement.clientWidth); expect(overflow).toBe(false); await page.keyboard.press("Escape"); await expect(page.getByRole("navigation", { name: "모바일 주 메뉴" })).toBeHidden(); await expect(page.getByRole("button", { name: "모바일 메뉴 열기" })).toBeFocused(); }); test("has no critical or serious axe violations on the shell route", async ({ page }) => { await openShell(page); const results = await new AxeBuilder({ page }).analyze(); const blockingViolations = results.violations.filter( (violation) => violation.impact === "critical" || violation.impact === "serious", ); expect(blockingViolations).toEqual([]); });