test(ai-character): 리소스 운영 E2E 보강
This commit is contained in:
84
tests/e2e/accessibility.spec.ts
Normal file
84
tests/e2e/accessibility.spec.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import AxeBuilder from "@axe-core/playwright";
|
||||
import { expect, test } from "@playwright/test";
|
||||
import type { Page } from "@playwright/test";
|
||||
|
||||
async function loginThroughMockMode(page: Page): Promise<void> {
|
||||
await page.goto("/login");
|
||||
await page.getByLabel("이메일").fill("admin@test.com");
|
||||
await page.getByLabel("비밀번호").fill("password");
|
||||
await page.getByRole("button", { name: "로그인" }).click();
|
||||
await expect(page).toHaveURL(/\/ai-characters$/);
|
||||
}
|
||||
|
||||
async function expectNoBlockingAxeViolations(page: Page): Promise<void> {
|
||||
const results = await new AxeBuilder({ page }).analyze();
|
||||
const blockingViolations = results.violations.filter((violation) => violation.impact === "critical" || violation.impact === "serious");
|
||||
|
||||
expect(blockingViolations).toEqual([]);
|
||||
}
|
||||
|
||||
async function storageDump(page: Page): Promise<string> {
|
||||
return page.evaluate(() => JSON.stringify({ cookies: document.cookie, local: { ...localStorage }, session: { ...sessionStorage } }));
|
||||
}
|
||||
|
||||
test("active mock routes have no critical or serious axe violations", async ({ page }) => {
|
||||
// Given
|
||||
await loginThroughMockMode(page);
|
||||
|
||||
for (const route of ["/ai-characters", "/ai-characters/101", "/ai-characters/101/audio-contents/9001", "/ai-characters/101/community-posts", "/ai-characters/101/fan-talks"] as const) {
|
||||
// When
|
||||
await page.goto(route);
|
||||
|
||||
// Then
|
||||
await expectNoBlockingAxeViolations(page);
|
||||
}
|
||||
});
|
||||
|
||||
test("keyboard skip link and representative sheet focus remain usable with reduced motion", async ({ browserName, isMobile, page }) => {
|
||||
test.skip(browserName !== "chromium" || isMobile, "Keyboard focus traversal is covered with desktop Chromium evidence.");
|
||||
|
||||
// Given
|
||||
await page.emulateMedia({ reducedMotion: "reduce" });
|
||||
await loginThroughMockMode(page);
|
||||
|
||||
// When / Then
|
||||
await page.getByRole("link", { name: "본문으로 건너뛰기" }).focus();
|
||||
await expect(page.getByRole("link", { name: "본문으로 건너뛰기" })).toBeFocused();
|
||||
await page.keyboard.press("Enter");
|
||||
await expect(page.getByRole("main", { name: "AI 캐릭터 관리" })).toBeFocused();
|
||||
|
||||
await page.goto("/ai-characters/101/community-posts");
|
||||
const trigger = page.getByRole("button", { name: "오늘의 상담 기록입니다. 게시글 열기" });
|
||||
await trigger.click();
|
||||
await expect(page.getByRole("dialog", { name: "커뮤니티 게시글" })).toBeVisible();
|
||||
await page.keyboard.press("Escape");
|
||||
await expect(page.getByRole("dialog", { name: "커뮤니티 게시글" })).toBeHidden();
|
||||
await expect(trigger).toBeFocused();
|
||||
});
|
||||
|
||||
test("mock preview keeps bright theme and avoids sensitive persistence", async ({ page }) => {
|
||||
// Given
|
||||
const consoleMessages: string[] = [];
|
||||
page.on("console", (message) => consoleMessages.push(message.text()));
|
||||
await page.emulateMedia({ colorScheme: "dark" });
|
||||
|
||||
// When
|
||||
await loginThroughMockMode(page);
|
||||
await page.goto("/ai-characters/101/fan-talks");
|
||||
await page.getByRole("button", { name: "첫 번째 응원입니다. 답변하기" }).click();
|
||||
const dialog = page.getByRole("dialog", { name: "FanTalk 답변" });
|
||||
await dialog.getByLabel("답변 내용").fill("보안 smoke 답변입니다.");
|
||||
await dialog.getByRole("button", { name: "답변 등록" }).click();
|
||||
await expect(dialog.getByRole("status", { name: "답변 저장 성공" })).toBeVisible();
|
||||
|
||||
// Then
|
||||
const dump = await storageDump(page);
|
||||
const visibleText = await page.locator("body").innerText();
|
||||
expect(await page.evaluate(() => document.documentElement.classList.contains("dark"))).toBe(false);
|
||||
await expect(page.getByRole("button", { name: /dark|theme|테마|다크/i })).toHaveCount(0);
|
||||
expect(dump).not.toContain("password");
|
||||
expect(dump).not.toContain("data:audio");
|
||||
expect(dump).not.toContain("data:image");
|
||||
expect(visibleText).not.toContain("password");
|
||||
expect(consoleMessages.join("\n")).not.toContain("password");
|
||||
});
|
||||
Reference in New Issue
Block a user