import { describe, it, expect, afterEach } from "vitest"; import { render, cleanup } from "@testing-library/react"; import { PublicPageRenderer } from "@/features/editor/components/PublicPageRenderer"; import type { Block } from "@/features/editor/state/editorStore"; // 폼 필드 스타일 TDD // - formInput: 색상/패딩/너비/둥글기 스타일 // - formSelect: 색상/패딩/너비/둥글기 스타일 // - formCheckbox/formRadio: 옵션 컨테이너 색상/패딩/둥글기 스타일 function getFirstLabel(root: HTMLElement): HTMLLabelElement { const label = root.querySelector("label"); if (!label) { throw new Error("label element not found"); } return label as HTMLLabelElement; } describe("PublicPageRenderer - 폼 필드 스타일", () => { afterEach(() => { cleanup(); }); it("formInput 블록은 색상/패딩/너비/둥글기 스타일을 반영해야 한다", () => { const blocks: Block[] = [ { id: "form_input_styles", type: "formInput", props: { label: "이메일", formFieldName: "email", align: "center", widthMode: "fixed", widthPx: 320, paddingX: 16, paddingY: 8, textColorCustom: "#112233", fillColorCustom: "#123456", strokeColorCustom: "#ff0000", borderRadius: "lg", } as any, }, ]; const { getByTestId } = render(); const wrapper = getByTestId("preview-form-input-wrapper") as HTMLElement; const input = wrapper.querySelector('[data-testid="preview-form-input"]') as HTMLElement | null; expect(input).not.toBeNull(); // 정렬 expect((input as HTMLInputElement).style.textAlign).toBe("center"); // width: 320px / 16 = 20em expect((input as HTMLInputElement).style.width).toBe("20em"); // padding-inline/block: 16px/8px -> 1em / 0.5em expect((input as HTMLInputElement).style.paddingInline).toBe("1em"); expect((input as HTMLInputElement).style.paddingBlock).toBe("0.5em"); // 색상: hex -> rgb 변환 expect((input as HTMLInputElement).style.color).toBe("rgb(17, 34, 51)"); expect((input as HTMLInputElement).style.backgroundColor).toBe("rgb(18, 52, 86)"); expect((input as HTMLInputElement).style.borderColor).toBe("rgb(255, 0, 0)"); // borderRadius: lg -> 6px expect((input as HTMLInputElement).style.borderRadius).toBe("6px"); }); it("formSelect 블록은 색상/패딩/너비/둥글기 스타일을 반영해야 한다", () => { const blocks: Block[] = [ { id: "form_select_styles", type: "formSelect", props: { label: "카테고리", formFieldName: "category", options: [ { label: "A", value: "a" }, { label: "B", value: "b" }, ], widthMode: "fixed", widthPx: 240, paddingX: 16, paddingY: 8, textColorCustom: "#00ff00", fillColorCustom: "#123456", strokeColorCustom: "#ff0000", borderRadius: "lg", } as any, }, ]; const { getByTestId } = render(); const wrapper = getByTestId("preview-form-select") as HTMLElement; const select = wrapper.querySelector("select") as HTMLSelectElement | null; expect(select).not.toBeNull(); // width: 240px / 16 = 15em expect(wrapper.style.width).toBe("15em"); // padding-inline/block: 16px/8px -> 1em / 0.5em expect(select!.style.paddingInline).toBe("1em"); expect(select!.style.paddingBlock).toBe("0.5em"); // 색상 expect(select!.style.color).toBe("rgb(0, 255, 0)"); expect(select!.style.backgroundColor).toBe("rgb(18, 52, 86)"); expect(select!.style.borderColor).toBe("rgb(255, 0, 0)"); // 둥글기 expect(select!.style.borderRadius).toBe("6px"); }); it("formCheckbox 블록은 옵션 컨테이너에 색상/패딩/둥글기 스타일을 반영해야 한다", () => { const blocks: Block[] = [ { id: "form_checkbox_styles", type: "formCheckbox", props: { groupLabel: "옵션들", formFieldName: "features", options: [{ label: "옵션 1", value: "opt1" }], widthMode: "fixed", widthPx: 320, paddingX: 8, paddingY: 4, optionGapPx: 16, textColorCustom: "#ffffff", fillColorCustom: "#123456", strokeColorCustom: "#ff0000", borderRadius: "lg", } as any, }, ]; const { getByTestId } = render(); const group = getByTestId("preview-form-checkbox-group") as HTMLElement; const firstLabel = getFirstLabel(group); // width: 320px / 16 = 20em expect(group.style.width).toBe("20em"); // 옵션 간 간격: 16px -> 1em const optionsContainer = group.querySelector('[data-testid="preview-form-checkbox-options"]') as HTMLElement | null; expect(optionsContainer).not.toBeNull(); expect(optionsContainer!.style.rowGap).toBe("1em"); // 옵션 컨테이너 스타일: padding, 배경, 보더, 둥글기 expect(firstLabel.style.paddingInline).toBe("0.5em"); expect(firstLabel.style.paddingBlock).toBe("0.25em"); expect(firstLabel.style.backgroundColor).toBe("rgb(18, 52, 86)"); expect(firstLabel.style.borderColor).toBe("rgb(255, 0, 0)"); expect(firstLabel.style.borderRadius).toBe("6px"); }); it("formRadio 블록은 옵션 컨테이너에 색상/패딩/둥글기 스타일을 반영해야 한다", () => { const blocks: Block[] = [ { id: "form_radio_styles", type: "formRadio", props: { groupLabel: "플랜", formFieldName: "plan", options: [{ label: "플랜 A", value: "a" }], widthMode: "fixed", widthPx: 320, paddingX: 8, paddingY: 4, optionGapPx: 16, textColorCustom: "#ffffff", fillColorCustom: "#123456", strokeColorCustom: "#ff0000", borderRadius: "lg", } as any, }, ]; const { getByTestId } = render(); const group = getByTestId("preview-form-radio-group") as HTMLElement; const firstLabel = getFirstLabel(group); // width: 320px / 16 = 20em expect(group.style.width).toBe("20em"); // 옵션 간 간격: 16px -> 1em const optionsContainer = group.querySelector('[data-testid="preview-form-radio-options"]') as HTMLElement | null; expect(optionsContainer).not.toBeNull(); expect(optionsContainer!.style.rowGap).toBe("1em"); // 옵션 컨테이너 스타일: padding, 배경, 보더, 둥글기 expect(firstLabel.style.paddingInline).toBe("0.5em"); expect(firstLabel.style.paddingBlock).toBe("0.25em"); expect(firstLabel.style.backgroundColor).toBe("rgb(18, 52, 86)"); expect(firstLabel.style.borderColor).toBe("rgb(255, 0, 0)"); expect(firstLabel.style.borderRadius).toBe("6px"); }); });