Files
page-builder/tests/api/formsSubmit.spec.ts
T
jaybe 2f59e3781e
CI / test (push) Successful in 11m5s
CI / pr_and_merge (push) Successful in 1m40s
CI / test (pull_request) Failing after 35m42s
CI / pr_and_merge (pull_request) Has been skipped
텍스트 스타일 및 프리뷰 렌더링 정리
2025-11-20 23:36:47 +09:00

148 lines
5.3 KiB
TypeScript

import "dotenv/config";
import { describe, it, expect, vi } from "vitest";
import type { FormBlockProps } from "@/features/editor/state/editorStore";
const BASE_URL = "http://localhost";
// /api/forms/submit 라우트에 대한 기본 TDD:
// - internal 모드에서 임의 필드가 포함된 FormData 를 받아도 에러 없이 ok:true 를 반환해야 한다.
// - webhook 모드에서는 config.extraParams / headers 와 함께 x-www-form-urlencoded 로 전달해야 한다.
describe("/api/forms/submit", () => {
it("internal 모드에서는 v2 필드 이름(email_address 등)을 포함한 FormData 를 받아도 ok:true 를 반환해야 한다", async () => {
const config: FormBlockProps = {
kind: "contact",
submitTarget: "internal",
successMessage: "성공적으로 전송되었습니다.",
errorMessage: "전송 중 오류가 발생했습니다.",
fieldIds: [],
submitButtonId: null,
};
const formData = new FormData();
formData.append("email_address", "test@example.com");
formData.append("__config", JSON.stringify(config));
const { POST: handleSubmit } = await import("@/app/api/forms/submit/route");
const res = await handleSubmit(
new Request(`${BASE_URL}/api/forms/submit`, {
method: "POST",
body: formData,
}),
);
expect(res.status).toBe(200);
const json = (await res.json()) as any;
expect(json.ok).toBe(true);
});
it("webhook 모드에서는 extraParams 와 FormData 필드들이 x-www-form-urlencoded payload 로 전달되어야 한다", async () => {
const destinationUrl = `${BASE_URL}/webhook-test`;
const config: FormBlockProps = {
kind: "contact",
submitTarget: "webhook",
destinationUrl,
method: "POST",
headers: { Authorization: "Bearer test-token" },
extraParams: { source: "builder", formId: "contact-hero" },
successMessage: "성공적으로 전송되었습니다.",
errorMessage: "전송 중 오류가 발생했습니다.",
fieldIds: [],
submitButtonId: null,
};
// fetch 를 목킹하여 payload 와 헤더가 기대대로 전달되는지 검증한다.
const fetchMock = vi.fn(async () => new Response("ok", { status: 200 }));
// @ts-expect-error - 글로벌 fetch 재정의
global.fetch = fetchMock;
const formData = new FormData();
formData.append("email_address", "test@example.com");
formData.append("__config", JSON.stringify(config));
const { POST: handleSubmit } = await import("@/app/api/forms/submit/route");
const res = await handleSubmit(
new Request(`${BASE_URL}/api/forms/submit`, {
method: "POST",
body: formData,
}),
);
expect(res.status).toBe(200);
const json = (await res.json()) as any;
expect(json.ok).toBe(true);
expect(fetchMock).toHaveBeenCalledTimes(1);
const [url, options] = fetchMock.mock.calls[0] as [string, RequestInit];
expect(url).toBe(destinationUrl);
expect(options.method).toBe("POST");
expect(options.headers).toMatchObject({
"Content-Type": "application/x-www-form-urlencoded",
Authorization: "Bearer test-token",
});
const body = options.body as string;
// payload 안에 폼 필드와 extraParams 가 모두 포함되어야 한다.
expect(body).toContain("email_address=test%40example.com");
expect(body).toContain("source=builder");
expect(body).toContain("formId=contact-hero");
});
it("webhook 모드에서 payloadFormat 이 json 인 경우 application/json 으로 JSON body 를 전송해야 한다", async () => {
const destinationUrl = `${BASE_URL}/webhook-json-test`;
const config: FormBlockProps = {
kind: "contact",
submitTarget: "webhook",
destinationUrl,
method: "POST",
headers: { Authorization: "Bearer json-token" },
extraParams: { source: "builder", formId: "contact-json" },
successMessage: "성공적으로 전송되었습니다.",
errorMessage: "전송 중 오류가 발생했습니다.",
fieldIds: [],
submitButtonId: null,
payloadFormat: "json",
};
const fetchMock = vi.fn(async () => new Response("ok", { status: 200 }));
// @ts-expect-error - 글로벌 fetch 재정의
global.fetch = fetchMock;
const formData = new FormData();
formData.append("email_address", "json@example.com");
formData.append("__config", JSON.stringify(config));
const { POST: handleSubmit } = await import("@/app/api/forms/submit/route");
const res = await handleSubmit(
new Request(`${BASE_URL}/api/forms/submit`, {
method: "POST",
body: formData,
}),
);
expect(res.status).toBe(200);
const json = (await res.json()) as any;
expect(json.ok).toBe(true);
expect(fetchMock).toHaveBeenCalledTimes(1);
const [url, options] = fetchMock.mock.calls[0] as unknown as [string, RequestInit];
expect(url).toBe(destinationUrl);
expect(options.method).toBe("POST");
expect(options.headers).toMatchObject({
"Content-Type": "application/json",
Authorization: "Bearer json-token",
});
const body = options.body as string;
const parsed = JSON.parse(body) as Record<string, string>;
expect(parsed.email_address).toBe("json@example.com");
expect(parsed.source).toBe("builder");
expect(parsed.formId).toBe("contact-json");
});
});