import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
import { render, screen, fireEvent, cleanup, waitFor } from "@testing-library/react";
import type { Block, ProjectConfig } from "@/features/editor/state/editorStore";
import PreviewPage from "@/app/preview/page";
// PreviewPage Export ZIP TDD
// - 프리뷰 헤더에 "페이지 파일로 내보내기 (ZIP)" 버튼이 노출되어야 한다.
// - 버튼 클릭 시 /api/export 로 blocks + projectConfig 를 payload 로 POST 요청을 보내야 한다.
let mockState: any;
beforeEach(() => {
const baseProjectConfig: ProjectConfig = {
title: "프리뷰 Export 테스트",
slug: "preview-export-test",
canvasPreset: "full",
} as ProjectConfig;
mockState = {
blocks: [
{
id: "blk_text_1",
type: "text",
props: {
text: "프리뷰 Export 본문",
align: "left",
size: "base",
},
},
] as Block[],
projectConfig: baseProjectConfig,
};
});
afterEach(() => {
cleanup();
vi.unstubAllGlobals();
});
vi.mock("@/features/editor/state/editorStore", () => {
const useEditorStore = (selector: (state: any) => any) => selector(mockState);
return {
__esModule: true,
useEditorStore,
};
});
vi.mock("next/link", () => {
return {
__esModule: true,
default: ({ href, children }: any) => {children},
};
});
vi.mock("next/navigation", () => {
return {
__esModule: true,
useRouter: () => ({
push: vi.fn(),
}),
};
});
describe("PreviewPage - ZIP Export", () => {
it("프리뷰 헤더에 프로젝트 목록 링크가 노출되고 /projects 로 연결되어야 한다", () => {
render();
const link = screen.getByText("프로젝트 목록");
expect(link).toBeTruthy();
expect(link.closest("a")?.getAttribute("href")).toBe("/projects");
});
it("프리뷰 헤더에 ZIP Export 버튼이 노출되어야 한다", () => {
render();
const exportButton = screen.getByText("페이지 파일로 내보내기 (ZIP)");
expect(exportButton).toBeTruthy();
});
it("ZIP Export 버튼 클릭 시 /api/export 로 blocks + projectConfig 를 payload 로 POST 해야 한다", async () => {
const fetchMock = vi.fn().mockResolvedValue({
ok: true,
// JSDOM 환경에서 blob() 호출을 안전하게 처리하기 위한 최소 mock
blob: vi.fn().mockResolvedValue(new Blob(["dummy-zip"])),
} as any);
vi.stubGlobal("fetch", fetchMock);
render();
const exportButton = screen.getByText("페이지 파일로 내보내기 (ZIP)");
fireEvent.click(exportButton);
await waitFor(() => {
expect(
fetchMock.mock.calls.some(
([url, options]: any[]) => url === "/api/export" && options?.method === "POST",
),
).toBe(true);
});
const exportCall = fetchMock.mock.calls.find(
([url, options]: any[]) => url === "/api/export" && options?.method === "POST",
) as any;
const [url, options] = exportCall;
expect(url).toBe("/api/export");
expect(options.method).toBe("POST");
expect(options.headers["Content-Type"]).toBe("application/json");
const parsed = JSON.parse(options.body);
expect(Array.isArray(parsed.blocks)).toBe(true);
expect(parsed.blocks[0].id).toBe("blk_text_1");
expect(parsed.projectConfig.slug).toBe("preview-export-test");
});
});