Files
voiceon-character-admin/tests/e2e/resource-workflows.spec.ts
2026-08-01 01:30:52 +09:00

94 lines
4.8 KiB
TypeScript

import { expect, test } from "@playwright/test";
import type { Page } from "@playwright/test";
type ApiRequest = {
readonly method: string;
readonly path: string;
readonly search: string;
};
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$/);
}
function collectAdminApiRequests(page: Page): ApiRequest[] {
const requests: ApiRequest[] = [];
page.on("request", (request) => {
const url = new URL(request.url());
if (url.pathname.startsWith("/api/v2/admin/")) {
requests.push({ method: request.method(), path: url.pathname, search: url.search });
}
});
return requests;
}
test("active mock resources complete the cross-domain journey without uncontracted requests", async ({ page }, testInfo) => {
test.skip(testInfo.project.name.startsWith("mobile-"), "Desktop-only mutation journey.");
// Given
const requests = collectAdminApiRequests(page);
await page.setViewportSize({ width: 1280, height: 900 });
await loginThroughMockMode(page);
// When / Then
await page.getByRole("link", { name: "루나 선택" }).click();
await expect(page.getByRole("heading", { name: "루나", exact: true })).toBeVisible();
await page.goto("/ai-characters/101/audio-contents");
await expect(page.getByRole("group", { name: "달빛 상담 오디오 오디오 플레이어" })).toBeVisible();
await page.getByRole("link", { name: "달빛 상담 오디오 상세 보기" }).click();
await expect(page.getByRole("heading", { name: "달빛 상담 오디오", exact: true })).toBeVisible();
await page.goto("/ai-characters/101/series/5001");
await expect(page.getByRole("heading", { name: "연결된 오디오", exact: true })).toBeVisible();
await page.getByRole("searchbox", { name: "검색어" }).fill("아침");
await page.getByRole("checkbox", { name: /아침 안내 오디오/ }).click();
await page.getByRole("button", { name: "선택한 오디오 연결" }).click();
await expect(page.getByRole("button", { name: "아침 안내 오디오 연결 해제" })).toBeVisible();
await page.getByRole("link", { name: "전체 시리즈 순서 관리" }).click();
await page.getByRole("button", { name: "아침 루틴 시리즈 위로" }).click();
await page.getByRole("button", { name: "순서 저장" }).click();
await expect(page.getByText("시리즈 순서를 저장했습니다")).toBeVisible();
await page.goto("/ai-characters/101/community-posts");
await page.getByRole("button", { name: "오늘의 상담 기록입니다. 게시글 열기" }).click();
const communityDialog = page.getByRole("dialog", { name: "커뮤니티 게시글" });
await communityDialog.getByRole("textbox", { name: "내용" }).fill("교차 회귀 수정 내용입니다.");
await communityDialog.getByRole("button", { name: "수정 저장" }).click();
await expect.poll(() => requests.some((request) => request.method === "PUT" && request.path.includes("/community-posts/"))).toBe(true);
await page.goto("/ai-characters/101/fan-talks");
await page.getByRole("button", { name: "첫 번째 응원입니다. 답변하기" }).click();
const fanTalkDialog = page.getByRole("dialog", { name: "FanTalk 답변" });
await fanTalkDialog.getByLabel("답변 내용").fill("교차 회귀 답변입니다.");
await fanTalkDialog.getByRole("button", { name: "답변 등록" }).click();
await expect(fanTalkDialog.getByRole("status", { name: "답변 저장 성공" })).toBeVisible();
expect(requests.some((request) => /\/community-posts\/\d+$/.test(request.path) && request.method === "GET")).toBe(false);
expect(requests.some((request) => request.path.includes("/fan-talks/7001") && request.method === "GET")).toBe(false);
expect(requests.every((request) => !request.search.includes("status=") && !request.search.includes("sort="))).toBe(true);
});
test("inactive character workspace blocks child mutation requests", async ({ page }) => {
// Given
const requests = collectAdminApiRequests(page);
await page.setViewportSize({ width: 1280, height: 900 });
await loginThroughMockMode(page);
// When / Then
await page.goto("/ai-characters/202/community-posts/new");
await expect(page.getByRole("heading", { name: "커뮤니티 게시글 생성 차단" })).toBeVisible();
await expect(page.getByRole("button", { name: "생성" })).toBeHidden();
await page.goto("/ai-characters/202/fan-talks");
await expect(page.getByRole("heading", { name: "FanTalk", exact: true })).toBeVisible();
await expect(page.getByRole("button", { name: /답변하기/ })).toBeHidden();
expect(requests.some((request) => request.method !== "GET" && request.path.includes("/ai-characters/202/"))).toBe(false);
});