import "dotenv/config"; import { describe, it, expect } from "vitest"; import JSZip from "jszip"; import { promises as fs } from "fs"; import path from "path"; import type { Block, ProjectConfig } from "@/features/editor/state/editorStore"; import { createHeroTemplateBlocks } from "@/app/editor/templates/heroTemplate"; import { createFeaturesTemplateBlocks } from "@/app/editor/templates/featuresTemplate"; import { createCtaTemplateBlocks } from "@/app/editor/templates/ctaTemplate"; import { buildStaticHtml } from "@/app/api/export/route"; const BASE_URL = "http://localhost"; // /api/export 라우트 TDD: // - blocks + projectConfig 를 받아 HTML/CSS/JS 가 포함된 ZIP 을 반환해야 한다. // - index.html 의 은 projectConfig.title 을 사용해야 한다. // - ZIP 안에 builder.css 와 main.js 파일이 포함되어야 한다. describe("/api/export", () => { it("POST /api/export 로 블록과 프로젝트 설정을 전송하면 기본 정적 ZIP 파일을 반환해야 한다", async () => { const blocks: Block[] = [ { id: "blk_test", type: "text", props: { text: "ZIP 내보내기 테스트", align: "left", size: "base", }, } as any, ]; const projectConfig: ProjectConfig = { title: "내보내기 테스트 페이지", slug: "export-test", canvasPreset: "full", canvasBgColorHex: "#ffffff", bodyBgColorHex: "#111111", }; const payload = { blocks, projectConfig }; const { POST: handleExport } = await import("@/app/api/export/route"); const res = await handleExport( new Request(`${BASE_URL}/api/export`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload), }), ); expect(res.status).toBe(200); expect(res.headers.get("Content-Type")).toBe("application/zip"); const arrayBuffer = await res.arrayBuffer(); const zip = await JSZip.loadAsync(arrayBuffer); const indexEntry = zip.file("index.html"); const cssEntry = zip.file("builder.css"); const jsEntry = zip.file("main.js"); expect(indexEntry).toBeTruthy(); expect(cssEntry).toBeTruthy(); expect(jsEntry).toBeTruthy(); const html = await indexEntry!.async("string"); expect(html).toContain("<title>내보내기 테스트 페이지"); expect(html).toContain("ZIP 내보내기 테스트"); // 캔버스 래퍼 배경색 expect(html).toContain("background-color:#ffffff"); // body 스타일: 배경색 + margin/padding 0 expect(html).toContain('style="background-color:#111111;margin:0;padding:0;"'); }); it("canvasPreset / canvasWidthPx / 배경색은 정적 HTML 캔버스 wrapper 및 body 스타일에 반영되어야 한다", () => { const blocks: Block[] = []; const createConfig = (partial: Partial): ProjectConfig => ({ title: "캔버스 프리셋 테스트", slug: "canvas-preset-test", canvasPreset: "full", canvasBgColorHex: "#0f172a", bodyBgColorHex: "#020617", ...partial, }); const htmlMobile = buildStaticHtml(blocks, createConfig({ canvasPreset: "mobile" })); expect(htmlMobile).toContain("max-width:390px;"); const htmlTablet = buildStaticHtml(blocks, createConfig({ canvasPreset: "tablet" })); expect(htmlTablet).toContain("max-width:768px;"); const htmlDesktop = buildStaticHtml(blocks, createConfig({ canvasPreset: "desktop" })); expect(htmlDesktop).toContain("max-width:1200px;"); const htmlCustom = buildStaticHtml(blocks, createConfig({ canvasPreset: "custom", canvasWidthPx: 1024 })); expect(htmlCustom).toContain("max-width:1024px;"); const htmlFull = buildStaticHtml(blocks, createConfig({ canvasPreset: "full" })); // full 프리셋에서 canvasWidthPx 가 없으면 max-width 스타일이 없어야 한다. expect(htmlFull).not.toContain("max-width:"); // bodyBgColorHex / canvasBgColorHex 도 각각 body 및 캔버스 wrapper 스타일에 반영된다. const htmlColors = buildStaticHtml( blocks, createConfig({ canvasBgColorHex: "#111111", bodyBgColorHex: "#222222" }), ); expect(htmlColors).toContain("background-color:#111111;"); expect(htmlColors).toContain('style="background-color:#222222;margin:0;padding:0;"'); }); it("projectConfig.headHtml 과 trackingScript 는 index.html 의 head/body 에 그대로 포함되어야 한다", async () => { const blocks: Block[] = []; const projectConfig: ProjectConfig = { title: "헤드/트래킹 테스트", slug: "head-tracking-test", canvasPreset: "full", headHtml: '', trackingScript: '