import { expect, test } from "@playwright/test"; import type { Page } from "@playwright/test"; import { apiBaseUrl } from "./api-base-url"; type ApiProbe = { readonly message: string | null; readonly status: number; }; async function fetchMockApi(page: Page, path: string, init?: RequestInit): Promise { return page.evaluate( async ({ apiBaseUrl, init, path }) => { const response = await fetch(`${apiBaseUrl}${path}`, init); const body = (await response.json()) as { readonly message?: string | null }; return { message: body.message ?? null, status: response.status }; }, { apiBaseUrl, init, path }, ); } async function waitForMockWorker(page: Page): Promise { await page.goto("/"); await expect .poll(() => page.evaluate(async () => { const registration = await navigator.serviceWorker.ready; return registration.active?.scriptURL.endsWith("/mockServiceWorker.js") ?? false; })) .toBe(true); if (!(await page.evaluate(() => navigator.serviceWorker.controller !== null))) { await page.reload(); } await expect.poll(() => page.evaluate(() => navigator.serviceWorker.controller !== null)).toBe(true); } test("mock API exposes Korean auth and media error messages without backend fallback", async ({ page }) => { // Given await waitForMockWorker(page); // When const missingBearer = await fetchMockApi(page, "/api/v2/admin/ai-characters?page=0&size=20"); const forbiddenLogout = await fetchMockApi(page, "/member/logout", { headers: { Authorization: "Bearer mock-member-jwt" }, method: "POST", }); const unsupportedLoginMedia = await fetchMockApi(page, "/admin/member/login", { body: "not-json", headers: { "Content-Type": "text/plain" }, method: "POST", }); // Then expect(missingBearer).toEqual({ message: "인증 정보가 없습니다.", status: 401 }); expect(forbiddenLogout).toEqual({ message: "접근 권한이 없습니다.", status: 403 }); expect(unsupportedLoginMedia).toEqual({ message: "지원하지 않는 미디어 타입입니다.", status: 415 }); }); test("mock preview does not persist secrets or file bodies in browser storage", async ({ page }) => { // Given 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$/); // When const storageDump = await page.evaluate(() => JSON.stringify({ local: { ...localStorage }, session: { ...sessionStorage } })); // Then expect(storageDump).not.toContain("password"); expect(storageDump).not.toContain("data:audio"); expect(storageDump).not.toContain("data:image"); });