Files
page-builder/tests/unit/FormControllerPanel.spec.tsx
T
jaybe 4bb4c83054
CI / test (push) Successful in 4m50s
CI / e2e (push) Has been skipped
CI / pr_and_merge (push) Successful in 1m40s
ci 수정
2025-12-07 19:46:56 +09:00

226 lines
6.4 KiB
TypeScript

import { describe, it, expect, vi, afterEach } from "vitest";
import { render, screen, fireEvent, cleanup } from "@testing-library/react";
import { FormControllerPanel } from "@/app/editor/forms/FormControllerPanel";
import type { Block, FormBlockProps } from "@/features/editor/state/editorStore";
// FormControllerPanel 컨트롤 TDD
// - 성공/에러 메시지 인풋이 updateBlock 을 통해 FormBlockProps.successMessage / errorMessage 를 갱신하는지 검증한다.
function makeFormBlock(id: string, props: Partial<FormBlockProps> = {}): Block {
const base: FormBlockProps = {
kind: "contact",
submitTarget: "internal",
fieldIds: [],
submitButtonId: null,
} as any;
return {
id,
type: "form",
props: { ...base, ...props } as any,
} as any;
}
describe("FormControllerPanel", () => {
afterEach(() => {
cleanup();
});
it("성공 메시지 인풋 변경 시 updateBlock 이 successMessage 로 호출되어야 한다", () => {
const formBlock = makeFormBlock("form-1", {
successMessage: "기존 성공 메시지",
});
const updateBlock = vi.fn();
render(
<FormControllerPanel
block={formBlock}
blocks={[formBlock]}
selectedBlockId="form-1"
updateBlock={updateBlock}
/>,
);
const input = screen.getByLabelText("성공 메시지");
fireEvent.change(input, { target: { value: "새 성공 메시지" } });
expect(updateBlock).toHaveBeenCalledWith(
"form-1",
expect.objectContaining({ successMessage: "새 성공 메시지" }),
);
});
it("에러 메시지 인풋 변경 시 updateBlock 이 errorMessage 로 호출되어야 한다", () => {
const formBlock = makeFormBlock("form-2", {
errorMessage: "기존 에러 메시지",
});
const updateBlock = vi.fn();
render(
<FormControllerPanel
block={formBlock}
blocks={[formBlock]}
selectedBlockId="form-2"
updateBlock={updateBlock}
/>,
);
const input = screen.getByLabelText("에러 메시지");
fireEvent.change(input, { target: { value: "새 에러 메시지" } });
expect(updateBlock).toHaveBeenCalledWith(
"form-2",
expect.objectContaining({ errorMessage: "새 에러 메시지" }),
);
});
it("폼 필드 매핑 UI 에서 필드 라벨 대신 전송 키(formFieldName)를 우선적으로 표시해야 한다", () => {
const formBlock = makeFormBlock("form-controller", {
fieldIds: ["input-1", "checkbox-1"],
});
const inputBlock: Block = {
id: "input-1",
type: "formInput",
props: {
label: "이름",
formFieldName: "name",
} as any,
} as any;
const checkboxBlock: Block = {
id: "checkbox-1",
type: "formCheckbox",
props: {
groupLabel: "옵션",
formFieldName: "options",
options: [],
} as any,
} as any;
const updateBlock = vi.fn();
render(
<FormControllerPanel
block={formBlock}
blocks={[formBlock, inputBlock, checkboxBlock]}
selectedBlockId="form-controller"
updateBlock={updateBlock}
/>,
);
const nameLabel = screen.getByText("name (이름)");
const optionsLabel = screen.getByText("options (옵션)");
expect(nameLabel).toBeTruthy();
expect(optionsLabel).toBeTruthy();
});
it("Submit 버튼 셀렉트에서 버튼 블록의 전송 키(formFieldName)를 기준으로 표시해야 한다", () => {
const formBlock = makeFormBlock("form-controller", {
submitButtonId: "btn-1",
});
const buttonBlock: Block = {
id: "btn-1",
type: "button",
props: {
label: "제출하기",
formFieldName: "submit-main",
} as any,
} as any;
const updateBlock = vi.fn();
render(
<FormControllerPanel
block={formBlock}
blocks={[formBlock, buttonBlock]}
selectedBlockId="form-controller"
updateBlock={updateBlock}
/>,
);
const select = screen.getByLabelText("Submit 버튼") as HTMLSelectElement;
const submitMainOption = screen.getByText("submit-main (제출하기)");
expect(submitMainOption).toBeTruthy();
// 기본 선택 값은 FormBlock.submitButtonId 이어야 한다.
expect(select.value).toBe("btn-1");
});
it("submitTarget 이 webhook 인 경우 Google Sheets 연동 가이드 텍스트를 보여줘야 한다", () => {
const formBlock = makeFormBlock("form-webhook", {
submitTarget: "webhook",
} as any);
const updateBlock = vi.fn();
render(
<FormControllerPanel
block={formBlock}
blocks={[formBlock]}
selectedBlockId="form-webhook"
updateBlock={updateBlock}
/>,
);
const guideButton = screen.getByRole("button", { name: "Google Sheets 연동 가이드" });
guideButton.click();
// Google Sheets 연동 가이드 헤딩과 Apps Script 안내 문구가 노출되어야 한다.
expect(screen.getByText("Google Sheets 연동 가이드")).toBeTruthy();
expect(
screen.getByText(/Apps Script 웹 앱 URL/i),
).toBeTruthy();
});
it("Google Sheets 가이드 모달의 Apps Script 코드가 컨트롤러에 매핑된 전송 키와 작성일시를 포함해야 한다", async () => {
const formBlock = makeFormBlock("form-webhook", {
submitTarget: "webhook",
fieldIds: ["input-name", "input-email"],
} as any);
const inputNameBlock: Block = {
id: "input-name",
type: "formInput",
props: {
label: "이름",
formFieldName: "name",
} as any,
} as any;
const inputEmailBlock: Block = {
id: "input-email",
type: "formInput",
props: {
label: "이메일",
formFieldName: "email",
} as any,
} as any;
const updateBlock = vi.fn();
render(
<FormControllerPanel
block={formBlock}
blocks={[formBlock, inputNameBlock, inputEmailBlock]}
selectedBlockId="form-webhook"
updateBlock={updateBlock}
/>,
);
const guideButton = screen.getByRole("button", { name: "Google Sheets 연동 가이드" });
fireEvent.click(guideButton);
const scriptTextarea = (await screen.findByDisplayValue(/function doPost/)) as HTMLTextAreaElement;
expect(scriptTextarea.value).toContain("params.name || \"\"");
expect(scriptTextarea.value).toContain("params.email || \"\"");
expect(scriptTextarea.value).toContain("new Date()");
});
});