feat(ai-character): 관리자 라우트 지연 로딩 적용
This commit is contained in:
@@ -2,6 +2,7 @@ import AxeBuilder from "@axe-core/playwright";
|
||||
import { expect, test } from "@playwright/test";
|
||||
import type { Locator, Page } from "@playwright/test";
|
||||
|
||||
// allow: SIZE_OK - Character workspace browser scenarios share one authenticated E2E surface.
|
||||
const interactiveActionRoles = ["button", "link", "menuitem"] as const;
|
||||
|
||||
async function loginThroughMockMode(page: Page): Promise<void> {
|
||||
@@ -42,6 +43,27 @@ async function zoomTo200Percent(page: Page): Promise<void> {
|
||||
});
|
||||
}
|
||||
|
||||
async function expectNoKoreanSyllableColumns(root: Locator): Promise<void> {
|
||||
const syllableColumns = await root.evaluate((element) => Array.from(element.querySelectorAll<HTMLElement>("*"))
|
||||
.filter((candidate) => candidate.childElementCount === 0 && /[가-힣]/.test(candidate.textContent ?? ""))
|
||||
.filter((candidate) => {
|
||||
const style = getComputedStyle(candidate);
|
||||
return style.display !== "none" && style.visibility !== "hidden" && candidate.clientWidth > 0 && candidate.clientHeight > 0;
|
||||
})
|
||||
.flatMap((candidate) => {
|
||||
const range = document.createRange();
|
||||
range.selectNodeContents(candidate);
|
||||
const lineWidths = Array.from(range.getClientRects(), (rect) => rect.width).filter((width) => width > 0);
|
||||
const fontSize = Number.parseFloat(getComputedStyle(candidate).fontSize);
|
||||
const koreanSyllableCount = candidate.textContent?.match(/[가-힣]/g)?.length ?? 0;
|
||||
const isSyllableColumn = koreanSyllableCount >= 3 && lineWidths.length >= 3 && Math.max(...lineWidths) <= fontSize * 2.25;
|
||||
|
||||
return isSyllableColumn ? [{ lineWidths, text: candidate.textContent?.trim() }] : [];
|
||||
}));
|
||||
|
||||
expect(syllableColumns).toEqual([]);
|
||||
}
|
||||
|
||||
async function pressTabUntilFocused(page: Page, target: Locator): Promise<void> {
|
||||
for (let attempt = 0; attempt < 80; attempt += 1) {
|
||||
if (await target.evaluate((element) => element === document.activeElement || element.contains(document.activeElement))) {
|
||||
@@ -193,6 +215,19 @@ test("mobile cards preserve the selection name and table-visible core metadata",
|
||||
await expect(lunaCard).toContainText("상담, 힐링");
|
||||
});
|
||||
|
||||
test("mobile zoom keeps Korean list and pagination text out of syllable columns", async ({ page }) => {
|
||||
// Given
|
||||
await page.setViewportSize({ width: 320, height: 640 });
|
||||
await loginThroughMockMode(page);
|
||||
|
||||
// When
|
||||
await zoomTo200Percent(page);
|
||||
|
||||
// Then
|
||||
await expectNoKoreanSyllableColumns(page.getByRole("region", { name: "AI 캐릭터 목록" }));
|
||||
await expectNoKoreanSyllableColumns(page.getByRole("navigation", { name: "페이지" }));
|
||||
});
|
||||
|
||||
test("keyboard-only path reaches the edit form and dirty-leave dialog", async ({ browserName, isMobile, page }) => {
|
||||
test.skip(isMobile, "Mobile view intentionally hides Character mutation actions.");
|
||||
test.skip(browserName === "webkit", "WebKit does not consistently tab-focus links in this keyboard path.");
|
||||
|
||||
Reference in New Issue
Block a user