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("내보내기 테스트 페이지");
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: '