import { describe, it, expect, vi, afterEach } from "vitest"; import { render, screen, fireEvent, cleanup } from "@testing-library/react"; import { FormInputPropertiesPanel } from "@/app/editor/forms/FormInputPropertiesPanel"; import { FormSelectPropertiesPanel } from "@/app/editor/forms/FormSelectPropertiesPanel"; import { FormCheckboxPropertiesPanel } from "@/app/editor/forms/FormCheckboxPropertiesPanel"; import { FormRadioPropertiesPanel } from "@/app/editor/forms/FormRadioPropertiesPanel"; import type { Block, FormInputBlockProps, FormSelectBlockProps, FormCheckboxBlockProps, FormRadioBlockProps, } from "@/features/editor/state/editorStore"; // Form*PropertiesPanel 컨트롤 TDD // - 각 패널의 색상 HEX 인풋과 필드 너비 셀렉트가 updateBlock 을 올바르게 호출하는지 검증한다. function makeBlock(id: string, type: Block["type"], props: T): Block { return { id, type, props } as any; } describe("FormInputPropertiesPanel", () => { const baseProps: FormInputBlockProps = { label: "이메일", formFieldName: "email", } as any; afterEach(() => { cleanup(); }); it("텍스트 색상 HEX 인풋 변경 시 updateBlock 이 textColorCustom 으로 호출되어야 한다", () => { const updateBlock = vi.fn(); const block = makeBlock("f-input-1", "formInput", { ...baseProps, textColorCustom: "#ffffff", }); render( , ); const hexInput = screen.getByLabelText("필드 텍스트 색상 HEX"); fireEvent.change(hexInput, { target: { value: "#112233" } }); expect(updateBlock).toHaveBeenCalledWith( "f-input-1", expect.objectContaining({ textColorCustom: "#112233" }), ); }); it("채움 색상 HEX 인풋 변경 시 updateBlock 이 fillColorCustom 으로 호출되어야 한다", () => { const updateBlock = vi.fn(); const block = makeBlock("f-input-2", "formInput", { ...baseProps, fillColorCustom: "#000000", }); render( , ); const hexInput = screen.getByLabelText("필드 채움 색상 HEX"); fireEvent.change(hexInput, { target: { value: "#445566" } }); expect(updateBlock).toHaveBeenCalledWith( "f-input-2", expect.objectContaining({ fillColorCustom: "#445566" }), ); }); it("테두리 색상 HEX 인풋 변경 시 updateBlock 이 strokeColorCustom 으로 호출되어야 한다", () => { const updateBlock = vi.fn(); const block = makeBlock("f-input-3", "formInput", { ...baseProps, strokeColorCustom: "#000000", }); render( , ); const hexInput = screen.getByLabelText("필드 테두리 색상 HEX"); fireEvent.change(hexInput, { target: { value: "#778899" } }); expect(updateBlock).toHaveBeenCalledWith( "f-input-3", expect.objectContaining({ strokeColorCustom: "#778899" }), ); }); it("필드 너비 셀렉트 변경 시 updateBlock 이 widthMode 로 호출되어야 한다", () => { const updateBlock = vi.fn(); const block = makeBlock("f-input-4", "formInput", { ...baseProps, widthMode: "auto", fullWidth: false, }); render( , ); const select = screen.getByLabelText("너비"); fireEvent.change(select, { target: { value: "fixed" } }); expect(updateBlock).toHaveBeenCalledWith( "f-input-4", expect.objectContaining({ widthMode: "fixed" }), ); }); }); describe("FormSelectPropertiesPanel", () => { const baseProps: FormSelectBlockProps = { label: "카테고리", formFieldName: "category", options: [{ label: "A", value: "a" }], } as any; afterEach(() => { cleanup(); }); it("텍스트 색상 HEX 인풋 변경 시 updateBlock 이 textColorCustom 으로 호출되어야 한다", () => { const updateBlock = vi.fn(); const block = makeBlock("f-select-1", "formSelect", { ...baseProps, textColorCustom: "#ffffff", }); render( , ); const hexInput = screen.getByLabelText("셀렉트 텍스트 색상 HEX"); fireEvent.change(hexInput, { target: { value: "#112233" } }); expect(updateBlock).toHaveBeenCalledWith( "f-select-1", expect.objectContaining({ textColorCustom: "#112233" }), ); }); it("채움 색상 HEX 인풋 변경 시 updateBlock 이 fillColorCustom 으로 호출되어야 한다", () => { const updateBlock = vi.fn(); const block = makeBlock("f-select-2", "formSelect", { ...baseProps, fillColorCustom: "#000000", }); render( , ); const hexInput = screen.getByLabelText("셀렉트 채움 색상 HEX"); fireEvent.change(hexInput, { target: { value: "#445566" } }); expect(updateBlock).toHaveBeenCalledWith( "f-select-2", expect.objectContaining({ fillColorCustom: "#445566" }), ); }); it("테두리 색상 HEX 인풋 변경 시 updateBlock 이 strokeColorCustom 으로 호출되어야 한다", () => { const updateBlock = vi.fn(); const block = makeBlock("f-select-3", "formSelect", { ...baseProps, strokeColorCustom: "#000000", }); render( , ); const hexInput = screen.getByLabelText("셀렉트 테두리 색상 HEX"); fireEvent.change(hexInput, { target: { value: "#778899" } }); expect(updateBlock).toHaveBeenCalledWith( "f-select-3", expect.objectContaining({ strokeColorCustom: "#778899" }), ); }); it("필드 너비 셀렉트 변경 시 updateBlock 이 widthMode 로 호출되어야 한다", () => { const updateBlock = vi.fn(); const block = makeBlock("f-select-4", "formSelect", { ...baseProps, widthMode: "auto", fullWidth: false, }); render( , ); const select = screen.getByLabelText("필드 너비"); fireEvent.change(select, { target: { value: "fixed" } }); expect(updateBlock).toHaveBeenCalledWith( "f-select-4", expect.objectContaining({ widthMode: "fixed" }), ); }); }); describe("FormCheckboxPropertiesPanel", () => { const baseProps: FormCheckboxBlockProps = { groupLabel: "옵션들", formFieldName: "features", options: [{ label: "옵션 1", value: "opt1" }], } as any; afterEach(() => { cleanup(); }); it("텍스트 색상 HEX 인풋 변경 시 updateBlock 이 textColorCustom 으로 호출되어야 한다", () => { const updateBlock = vi.fn(); const block = makeBlock("f-check-1", "formCheckbox", { ...baseProps, textColorCustom: "#ffffff", }); render( , ); const hexInput = screen.getByLabelText("체크박스 텍스트 색상 HEX"); fireEvent.change(hexInput, { target: { value: "#112233" } }); expect(updateBlock).toHaveBeenCalledWith( "f-check-1", expect.objectContaining({ textColorCustom: "#112233" }), ); }); it("필드 너비 셀렉트 변경 시 updateBlock 이 widthMode 로 호출되어야 한다", () => { const updateBlock = vi.fn(); const block = makeBlock("f-check-2", "formCheckbox", { ...baseProps, widthMode: "auto", fullWidth: false, }); render( , ); const select = screen.getByLabelText("필드 너비"); fireEvent.change(select, { target: { value: "fixed" } }); expect(updateBlock).toHaveBeenCalledWith( "f-check-2", expect.objectContaining({ widthMode: "fixed" }), ); }); }); describe("FormRadioPropertiesPanel", () => { const baseProps: FormRadioBlockProps = { groupLabel: "플랜", formFieldName: "plan", options: [{ label: "플랜 A", value: "a" }], } as any; afterEach(() => { cleanup(); }); it("텍스트 색상 HEX 인풋 변경 시 updateBlock 이 textColorCustom 으로 호출되어야 한다", () => { const updateBlock = vi.fn(); const block = makeBlock("f-radio-1", "formRadio", { ...baseProps, textColorCustom: "#ffffff", }); render( , ); const hexInput = screen.getByLabelText("라디오 텍스트 색상 HEX"); fireEvent.change(hexInput, { target: { value: "#112233" } }); expect(updateBlock).toHaveBeenCalledWith( "f-radio-1", expect.objectContaining({ textColorCustom: "#112233" }), ); }); it("필드 너비 셀렉트 변경 시 updateBlock 이 widthMode 로 호출되어야 한다", () => { const updateBlock = vi.fn(); const block = makeBlock("f-radio-2", "formRadio", { ...baseProps, widthMode: "auto", fullWidth: false, }); render( , ); const select = screen.getByLabelText("필드 너비"); fireEvent.change(select, { target: { value: "fixed" } }); expect(updateBlock).toHaveBeenCalledWith( "f-radio-2", expect.objectContaining({ widthMode: "fixed" }), ); }); });