test(ai-character): 리소스 운영 E2E 보강

This commit is contained in:
Yu Sung
2026-08-01 01:30:52 +09:00
parent 3faca90135
commit 3759abf8b7
16 changed files with 1962 additions and 62 deletions

View File

@@ -0,0 +1,83 @@
import { expect, test } from "@playwright/test";
import type { Locator, Page } from "@playwright/test";
type ViewportCase = {
readonly height: number;
readonly name: string;
readonly width: number;
};
const viewports = [
{ height: 640, name: "mobile 320", width: 320 },
{ height: 640, name: "mobile 640", width: 640 },
{ height: 900, name: "tablet 768", width: 768 },
{ height: 900, name: "desktop 1024", width: 1024 },
{ height: 900, name: "desktop 1280", width: 1280 },
{ height: 390, name: "mobile landscape", width: 844 },
] as const satisfies readonly ViewportCase[];
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 expectNoHorizontalOverflow(page: Page): Promise<void> {
expect(await page.evaluate(() => document.documentElement.scrollWidth > document.documentElement.clientWidth)).toBe(false);
}
async function expectTouchTarget(locator: Locator): Promise<void> {
const box = await locator.boundingBox();
expect(box).not.toBeNull();
expect(box?.height ?? 0).toBeGreaterThanOrEqual(44);
}
test("mock shell stays usable across the P9 viewport matrix", async ({ page }) => {
// Given
await loginThroughMockMode(page);
for (const viewport of viewports) {
// When
await page.setViewportSize(viewport);
await page.goto("/ai-characters");
// Then
await expect(page.getByRole("main", { name: "AI 캐릭터 관리" })).toBeVisible();
await expect(page.getByRole("status", { name: /Mock Preview/ })).toBeVisible();
await expectNoHorizontalOverflow(page);
if (viewport.width < 768) {
await expectTouchTarget(page.getByRole("button", { name: "모바일 메뉴 열기" }));
await expectTouchTarget(page.getByRole("button", { name: "로그아웃" }));
}
}
});
test("route capability matches mobile read-only and tablet desktop management rules", async ({ page }) => {
// Given
await loginThroughMockMode(page);
// When / Then
await page.setViewportSize({ width: 320, height: 640 });
await page.goto("/ai-characters/101/audio-contents/new");
await expect(page.getByText(/데스크톱.*(생성|업로드|이용)/)).toBeVisible();
await expect(page.getByRole("button", { name: "생성" })).toBeHidden();
await page.goto("/ai-characters/101/community-posts/new");
await expect(page.getByRole("heading", { name: "모바일에서는 커뮤니티 게시글 생성을 제한합니다" })).toBeVisible();
await expect(page.getByRole("button", { name: "생성" })).toBeHidden();
await page.goto("/ai-characters/101/fan-talks");
await expect(page.getByRole("button", { name: "첫 번째 응원입니다. 답변하기" })).toBeVisible();
await page.setViewportSize({ width: 768, height: 900 });
await page.goto("/ai-characters/101/community-posts");
await expect(page.getByRole("link", { name: "커뮤니티 게시글 생성" })).toBeVisible();
await page.setViewportSize({ width: 1280, height: 900 });
await page.goto("/ai-characters/101/series/5001");
await expect(page.getByRole("searchbox", { name: "검색어" })).toBeVisible();
await expectNoHorizontalOverflow(page);
});