1739 lines
56 KiB
TypeScript
1739 lines
56 KiB
TypeScript
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<T extends Block["props"]>(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<FormInputBlockProps>("f-input-1", "formInput", {
|
|
...baseProps,
|
|
textColorCustom: "#ffffff",
|
|
});
|
|
|
|
render(
|
|
<FormInputPropertiesPanel
|
|
block={block}
|
|
selectedBlockId="f-input-1"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
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<FormInputBlockProps>("f-input-2", "formInput", {
|
|
...baseProps,
|
|
fillColorCustom: "#000000",
|
|
});
|
|
|
|
render(
|
|
<FormInputPropertiesPanel
|
|
block={block}
|
|
selectedBlockId="f-input-2"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
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<FormInputBlockProps>("f-input-3", "formInput", {
|
|
...baseProps,
|
|
strokeColorCustom: "#000000",
|
|
});
|
|
|
|
render(
|
|
<FormInputPropertiesPanel
|
|
block={block}
|
|
selectedBlockId="f-input-3"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const hexInput = screen.getByLabelText("필드 테두리 색상 HEX");
|
|
fireEvent.change(hexInput, { target: { value: "#778899" } });
|
|
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-input-3",
|
|
expect.objectContaining({ strokeColorCustom: "#778899" }),
|
|
);
|
|
});
|
|
|
|
it("텍스트 색상 팔레트에서 \"없음\" 을 선택하면 FormInput textColorCustom 이 빈 문자열로 업데이트되어야 한다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
const block = makeBlock<FormInputBlockProps>("f-input-none", "formInput", {
|
|
...baseProps,
|
|
textColorCustom: "#ff0000",
|
|
} as any);
|
|
|
|
render(
|
|
<FormInputPropertiesPanel
|
|
block={block}
|
|
selectedBlockId="f-input-none"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
// 첫 번째 ColorPickerField(필드 텍스트 색상)의 팔레트 버튼을 연다.
|
|
const paletteButtons = screen.getAllByText("색상 팔레트");
|
|
fireEvent.click(paletteButtons[0].closest("button") as HTMLButtonElement);
|
|
|
|
// 팔레트에서 "없음" 항목을 버튼 role 기준으로 선택한다.
|
|
const noneButtons = screen.getAllByRole("button", { name: "없음" });
|
|
fireEvent.click(noneButtons[0]);
|
|
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-input-none",
|
|
expect.objectContaining({ textColorCustom: "" }),
|
|
);
|
|
});
|
|
|
|
it("FormInput textColorCustom 이 비어 있으면 필드 텍스트 색상 HEX 인풋 값은 빈 문자열이어야 한다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
const block = makeBlock<FormInputBlockProps>("f-input-empty", "formInput", {
|
|
...baseProps,
|
|
textColorCustom: "",
|
|
} as any);
|
|
|
|
render(
|
|
<FormInputPropertiesPanel
|
|
block={block}
|
|
selectedBlockId="f-input-empty"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const hexInput = screen.getByLabelText("필드 텍스트 색상 HEX") as HTMLInputElement;
|
|
expect(hexInput.value).toBe("");
|
|
});
|
|
|
|
it("필드 너비 셀렉트 변경 시 updateBlock 이 widthMode 로 호출되어야 한다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
const block = makeBlock<FormInputBlockProps>("f-input-4", "formInput", {
|
|
...baseProps,
|
|
widthMode: "auto",
|
|
fullWidth: false,
|
|
});
|
|
|
|
render(
|
|
<FormInputPropertiesPanel
|
|
block={block}
|
|
selectedBlockId="f-input-4"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const select = screen.getByLabelText("너비");
|
|
fireEvent.change(select, { target: { value: "fixed" } });
|
|
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-input-4",
|
|
expect.objectContaining({ widthMode: "fixed" }),
|
|
);
|
|
});
|
|
|
|
it("FormInput 레이아웃 셀렉트는 라벨 표시 방식이 visible 일 때만 보여야 한다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
const visibleBlock = makeBlock<FormInputBlockProps>("f-input-layout-visible", "formInput", {
|
|
...baseProps,
|
|
labelDisplay: "visible",
|
|
labelLayout: "inline",
|
|
} as any);
|
|
|
|
render(
|
|
<FormInputPropertiesPanel
|
|
block={visibleBlock}
|
|
selectedBlockId="f-input-layout-visible"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const layoutSelect = screen.getByLabelText("레이아웃");
|
|
expect(layoutSelect).toBeTruthy();
|
|
|
|
cleanup();
|
|
|
|
const floatingBlock = makeBlock<FormInputBlockProps>("f-input-layout-floating", "formInput", {
|
|
...baseProps,
|
|
labelDisplay: "floating",
|
|
labelLayout: "inline",
|
|
} as any);
|
|
|
|
render(
|
|
<FormInputPropertiesPanel
|
|
block={floatingBlock}
|
|
selectedBlockId="f-input-layout-floating"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
expect(screen.queryByLabelText("레이아웃")).toBeNull();
|
|
});
|
|
|
|
it("폼 입력 텍스트/줄간격/자간 px 컨트롤이 fontSizeCustom/lineHeightCustom/letterSpacingCustom 으로 저장되어야 한다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
const block = makeBlock<FormInputBlockProps>("f-input-typo-1", "formInput", {
|
|
...baseProps,
|
|
fontSizeCustom: "14px",
|
|
lineHeightCustom: "20px",
|
|
letterSpacingCustom: "0px",
|
|
} as any);
|
|
|
|
render(
|
|
<FormInputPropertiesPanel
|
|
block={block}
|
|
selectedBlockId="f-input-typo-1"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const fontSizeInput = screen.getByLabelText("필드 텍스트 크기 (px) 커스텀 (px)");
|
|
fireEvent.change(fontSizeInput, { target: { value: "24" } });
|
|
|
|
const lineHeightInput = screen.getByLabelText("필드 줄간격 (px) 커스텀 (px)");
|
|
fireEvent.change(lineHeightInput, { target: { value: "30" } });
|
|
|
|
const letterSpacingInput = screen.getByLabelText("필드 자간 (px) 커스텀 (px)");
|
|
fireEvent.change(letterSpacingInput, { target: { value: "1.5" } });
|
|
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-input-typo-1",
|
|
expect.objectContaining({ fontSizeCustom: "24px" }),
|
|
);
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-input-typo-1",
|
|
expect.objectContaining({ lineHeightCustom: "30px" }),
|
|
);
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-input-typo-1",
|
|
expect.objectContaining({ letterSpacingCustom: "1.5px" }),
|
|
);
|
|
});
|
|
|
|
it("폼 입력 정렬/고정 너비/패딩 컨트롤이 align/widthPx/paddingX/paddingY 로 저장되어야 한다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
const block = makeBlock<FormInputBlockProps>("f-input-layout-1", "formInput", {
|
|
...baseProps,
|
|
align: "left",
|
|
widthMode: "auto",
|
|
widthPx: 240,
|
|
paddingX: 12,
|
|
paddingY: 10,
|
|
} as any);
|
|
|
|
render(
|
|
<FormInputPropertiesPanel
|
|
block={block}
|
|
selectedBlockId="f-input-layout-1"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const alignSelect = screen.getByLabelText("텍스트 정렬");
|
|
fireEvent.change(alignSelect, { target: { value: "center" } });
|
|
|
|
const widthModeSelect = screen.getByLabelText("너비");
|
|
fireEvent.change(widthModeSelect, { target: { value: "fixed" } });
|
|
|
|
const widthPxInput = screen.getByLabelText("필드 고정 너비 커스텀 (px)");
|
|
fireEvent.change(widthPxInput, { target: { value: "320" } });
|
|
|
|
const paddingXInput = screen.getByLabelText("필드 가로 패딩 (px) 커스텀 (px)");
|
|
fireEvent.change(paddingXInput, { target: { value: "16" } });
|
|
|
|
const paddingYInput = screen.getByLabelText("필드 세로 패딩 (px) 커스텀 (px)");
|
|
fireEvent.change(paddingYInput, { target: { value: "12" } });
|
|
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-input-layout-1",
|
|
expect.objectContaining({ align: "center" }),
|
|
);
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-input-layout-1",
|
|
expect.objectContaining({ widthMode: "fixed" }),
|
|
);
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-input-layout-1",
|
|
expect.objectContaining({ widthPx: 320 }),
|
|
);
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-input-layout-1",
|
|
expect.objectContaining({ paddingX: 16 }),
|
|
);
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-input-layout-1",
|
|
expect.objectContaining({ paddingY: 12 }),
|
|
);
|
|
});
|
|
|
|
it("FormInput 기본 인풋/셀렉트 컨트롤은 라이트/다크 듀얼 테마 크롬을 사용해야 한다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
const block = makeBlock<FormInputBlockProps>("f-input-theme", "formInput", {
|
|
...baseProps,
|
|
} as any);
|
|
|
|
render(
|
|
<FormInputPropertiesPanel
|
|
block={block}
|
|
selectedBlockId="f-input-theme"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const labelTypeSelect = screen.getByLabelText("라벨 타입") as HTMLSelectElement;
|
|
const labelTypeClass = labelTypeSelect.getAttribute("class") ?? "";
|
|
expect(labelTypeClass).toContain("bg-white");
|
|
expect(labelTypeClass).toContain("text-slate-900");
|
|
expect(labelTypeClass).toContain("border-slate-300");
|
|
expect(labelTypeClass).toContain("dark:bg-slate-900");
|
|
expect(labelTypeClass).toContain("dark:text-slate-100");
|
|
expect(labelTypeClass).toContain("dark:border-slate-700");
|
|
|
|
const labelInput = screen.getByLabelText("필드 라벨") as HTMLInputElement;
|
|
const labelInputClass = labelInput.getAttribute("class") ?? "";
|
|
expect(labelInputClass).toContain("bg-white");
|
|
expect(labelInputClass).toContain("text-slate-900");
|
|
expect(labelInputClass).toContain("border-slate-300");
|
|
expect(labelInputClass).toContain("dark:bg-slate-900");
|
|
expect(labelInputClass).toContain("dark:text-slate-100");
|
|
expect(labelInputClass).toContain("dark:border-slate-700");
|
|
|
|
const labelDisplaySelect = screen.getByLabelText("라벨 표시 방식") as HTMLSelectElement;
|
|
const labelDisplayClass = labelDisplaySelect.getAttribute("class") ?? "";
|
|
expect(labelDisplayClass).toContain("bg-white");
|
|
expect(labelDisplayClass).toContain("text-slate-900");
|
|
expect(labelDisplayClass).toContain("border-slate-300");
|
|
expect(labelDisplayClass).toContain("dark:bg-slate-900");
|
|
expect(labelDisplayClass).toContain("dark:text-slate-100");
|
|
expect(labelDisplayClass).toContain("dark:border-slate-700");
|
|
|
|
const fieldTypeSelect = screen.getByLabelText("필드 타입") as HTMLSelectElement;
|
|
const fieldTypeClass = fieldTypeSelect.getAttribute("class") ?? "";
|
|
expect(fieldTypeClass).toContain("bg-white");
|
|
expect(fieldTypeClass).toContain("text-slate-900");
|
|
expect(fieldTypeClass).toContain("border-slate-300");
|
|
expect(fieldTypeClass).toContain("dark:bg-slate-900");
|
|
expect(fieldTypeClass).toContain("dark:text-slate-100");
|
|
expect(fieldTypeClass).toContain("dark:border-slate-700");
|
|
|
|
const placeholderInput = screen.getByLabelText("Placeholder") as HTMLInputElement;
|
|
const placeholderClass = placeholderInput.getAttribute("class") ?? "";
|
|
expect(placeholderClass).toContain("bg-white");
|
|
expect(placeholderClass).toContain("text-slate-900");
|
|
expect(placeholderClass).toContain("border-slate-300");
|
|
expect(placeholderClass).toContain("dark:bg-slate-900");
|
|
expect(placeholderClass).toContain("dark:text-slate-100");
|
|
expect(placeholderClass).toContain("dark:border-slate-700");
|
|
});
|
|
});
|
|
|
|
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<FormSelectBlockProps>("f-select-1", "formSelect", {
|
|
...baseProps,
|
|
textColorCustom: "#ffffff",
|
|
});
|
|
|
|
render(
|
|
<FormSelectPropertiesPanel
|
|
block={block}
|
|
selectedBlockId="f-select-1"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
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<FormSelectBlockProps>("f-select-2", "formSelect", {
|
|
...baseProps,
|
|
fillColorCustom: "#000000",
|
|
});
|
|
|
|
render(
|
|
<FormSelectPropertiesPanel
|
|
block={block}
|
|
selectedBlockId="f-select-2"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
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<FormSelectBlockProps>("f-select-3", "formSelect", {
|
|
...baseProps,
|
|
strokeColorCustom: "#000000",
|
|
});
|
|
|
|
render(
|
|
<FormSelectPropertiesPanel
|
|
block={block}
|
|
selectedBlockId="f-select-3"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
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<FormSelectBlockProps>("f-select-4", "formSelect", {
|
|
...baseProps,
|
|
widthMode: "auto",
|
|
fullWidth: false,
|
|
});
|
|
|
|
render(
|
|
<FormSelectPropertiesPanel
|
|
block={block}
|
|
selectedBlockId="f-select-4"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const select = screen.getByLabelText("필드 너비");
|
|
fireEvent.change(select, { target: { value: "fixed" } });
|
|
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-select-4",
|
|
expect.objectContaining({ widthMode: "fixed" }),
|
|
);
|
|
});
|
|
|
|
it("폼 셀렉트 텍스트/줄간격/자간 px 컨트롤이 fontSizeCustom/lineHeightCustom/letterSpacingCustom 으로 저장되어야 한다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
const block = makeBlock<FormSelectBlockProps>("f-select-typo-1", "formSelect", {
|
|
...baseProps,
|
|
fontSizeCustom: "14px",
|
|
lineHeightCustom: "20px",
|
|
letterSpacingCustom: "0px",
|
|
} as any);
|
|
|
|
render(
|
|
<FormSelectPropertiesPanel
|
|
block={block}
|
|
selectedBlockId="f-select-typo-1"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const fontSizeInput = screen.getByLabelText("셀렉트 텍스트 크기 (px) 커스텀 (px)");
|
|
fireEvent.change(fontSizeInput, { target: { value: "24" } });
|
|
|
|
const lineHeightInput = screen.getByLabelText("셀렉트 줄간격 (px) 커스텀 (px)");
|
|
fireEvent.change(lineHeightInput, { target: { value: "30" } });
|
|
|
|
const letterSpacingInput = screen.getByLabelText("셀렉트 자간 (px) 커스텀 (px)");
|
|
fireEvent.change(letterSpacingInput, { target: { value: "1.5" } });
|
|
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-select-typo-1",
|
|
expect.objectContaining({ fontSizeCustom: "24px" }),
|
|
);
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-select-typo-1",
|
|
expect.objectContaining({ lineHeightCustom: "30px" }),
|
|
);
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-select-typo-1",
|
|
expect.objectContaining({ letterSpacingCustom: "1.5px" }),
|
|
);
|
|
});
|
|
|
|
it("폼 셀렉트 너비/패딩 컨트롤이 widthPx/paddingX/paddingY 로 저장되어야 한다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
const block = makeBlock<FormSelectBlockProps>("f-select-layout-1", "formSelect", {
|
|
...baseProps,
|
|
widthMode: "fixed",
|
|
widthPx: 240,
|
|
paddingX: 12,
|
|
paddingY: 10,
|
|
} as any);
|
|
|
|
render(
|
|
<FormSelectPropertiesPanel
|
|
block={block}
|
|
selectedBlockId="f-select-layout-1"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const widthPxInput = screen.getByLabelText("필드 고정 너비 커스텀 (px)");
|
|
fireEvent.change(widthPxInput, { target: { value: "320" } });
|
|
|
|
const paddingXInput = screen.getByLabelText("셀렉트 가로 패딩 (px) 커스텀 (px)");
|
|
fireEvent.change(paddingXInput, { target: { value: "16" } });
|
|
|
|
const paddingYInput = screen.getByLabelText("셀렉트 세로 패딩 (px) 커스텀 (px)");
|
|
fireEvent.change(paddingYInput, { target: { value: "12" } });
|
|
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-select-layout-1",
|
|
expect.objectContaining({ widthPx: 320 }),
|
|
);
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-select-layout-1",
|
|
expect.objectContaining({ paddingX: 16 }),
|
|
);
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-select-layout-1",
|
|
expect.objectContaining({ paddingY: 12 }),
|
|
);
|
|
});
|
|
|
|
it("FormSelect 레이아웃 셀렉트는 라벨 표시 방식이 visible 일 때만 보여야 한다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
const visibleBlock = makeBlock<FormSelectBlockProps>("f-select-layout-visible", "formSelect", {
|
|
...baseProps,
|
|
labelDisplay: "visible",
|
|
labelLayout: "inline",
|
|
} as any);
|
|
|
|
render(
|
|
<FormSelectPropertiesPanel
|
|
block={visibleBlock}
|
|
selectedBlockId="f-select-layout-visible"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const layoutSelect = screen.getByLabelText("레이아웃");
|
|
expect(layoutSelect).toBeTruthy();
|
|
|
|
cleanup();
|
|
|
|
const hiddenBlock = makeBlock<FormSelectBlockProps>("f-select-layout-hidden", "formSelect", {
|
|
...baseProps,
|
|
labelDisplay: "hidden",
|
|
labelLayout: "inline",
|
|
} as any);
|
|
|
|
render(
|
|
<FormSelectPropertiesPanel
|
|
block={hiddenBlock}
|
|
selectedBlockId="f-select-layout-hidden"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
expect(screen.queryByLabelText("레이아웃")).toBeNull();
|
|
});
|
|
|
|
it("라벨 타입 셀렉트 변경 시 updateBlock 이 labelMode 로 호출되어야 한다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
const block = makeBlock<FormSelectBlockProps>("f-select-label-mode", "formSelect", {
|
|
...baseProps,
|
|
labelMode: "text",
|
|
} as any);
|
|
|
|
render(
|
|
<FormSelectPropertiesPanel
|
|
block={block}
|
|
selectedBlockId="f-select-label-mode"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const select = screen.getByLabelText("라벨 타입");
|
|
fireEvent.change(select, { target: { value: "image" } });
|
|
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-select-label-mode",
|
|
expect.objectContaining({ labelMode: "image" }),
|
|
);
|
|
});
|
|
|
|
it("라벨 표시 방식 셀렉트 변경 시 updateBlock 이 labelDisplay 로 호출되어야 한다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
const block = makeBlock<FormSelectBlockProps>("f-select-label-display", "formSelect", {
|
|
...baseProps,
|
|
labelDisplay: "visible",
|
|
} as any);
|
|
|
|
render(
|
|
<FormSelectPropertiesPanel
|
|
block={block}
|
|
selectedBlockId="f-select-label-display"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const select = screen.getByLabelText("라벨 표시 방식");
|
|
fireEvent.change(select, { target: { value: "hidden" } });
|
|
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-select-label-display",
|
|
expect.objectContaining({ labelDisplay: "hidden" }),
|
|
);
|
|
});
|
|
|
|
it("레이아웃 셀렉트 변경 시 updateBlock 이 labelLayout 으로 호출되어야 한다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
const block = makeBlock<FormSelectBlockProps>("f-select-label-layout", "formSelect", {
|
|
...baseProps,
|
|
labelDisplay: "visible",
|
|
labelLayout: "stacked",
|
|
} as any);
|
|
|
|
render(
|
|
<FormSelectPropertiesPanel
|
|
block={block}
|
|
selectedBlockId="f-select-label-layout"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const select = screen.getByLabelText("레이아웃");
|
|
fireEvent.change(select, { target: { value: "inline" } });
|
|
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-select-label-layout",
|
|
expect.objectContaining({ labelLayout: "inline" }),
|
|
);
|
|
});
|
|
|
|
it("라벨/필드 간격 (px) 컨트롤 변경 시 updateBlock 이 labelGapPx 로 호출되어야 한다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
const block = makeBlock<FormSelectBlockProps>("f-select-label-gap", "formSelect", {
|
|
...baseProps,
|
|
labelDisplay: "visible",
|
|
labelLayout: "inline",
|
|
labelGapPx: 8,
|
|
} as any);
|
|
|
|
render(
|
|
<FormSelectPropertiesPanel
|
|
block={block}
|
|
selectedBlockId="f-select-label-gap"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const gapInput = screen.getByLabelText("라벨/필드 간격 (px) 커스텀 (px)");
|
|
fireEvent.change(gapInput, { target: { value: "16" } });
|
|
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-select-label-gap",
|
|
expect.objectContaining({ labelGapPx: 16 }),
|
|
);
|
|
});
|
|
|
|
it("필드 라벨 인풋 변경 시 updateBlock 이 label 로 호출되어야 한다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
const block = makeBlock<FormSelectBlockProps>("f-select-label", "formSelect", {
|
|
...baseProps,
|
|
label: "카테고리",
|
|
} as any);
|
|
|
|
render(
|
|
<FormSelectPropertiesPanel
|
|
block={block}
|
|
selectedBlockId="f-select-label"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const input = screen.getByLabelText("필드 라벨");
|
|
fireEvent.change(input, { target: { value: "새 카테고리" } });
|
|
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-select-label",
|
|
expect.objectContaining({ label: "새 카테고리" }),
|
|
);
|
|
});
|
|
|
|
it("전송 키 인풋 변경 시 updateBlock 이 formFieldName 으로 호출되어야 한다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
const block = makeBlock<FormSelectBlockProps>("f-select-name", "formSelect", {
|
|
...baseProps,
|
|
formFieldName: "category",
|
|
} as any);
|
|
|
|
render(
|
|
<FormSelectPropertiesPanel
|
|
block={block}
|
|
selectedBlockId="f-select-name"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const input = screen.getByLabelText("전송 키");
|
|
fireEvent.change(input, { target: { value: "category_new" } });
|
|
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-select-name",
|
|
expect.objectContaining({ formFieldName: "category_new" }),
|
|
);
|
|
});
|
|
|
|
it("필드 모서리 둥글기 컨트롤 변경 시 updateBlock 이 borderRadius 로 호출되어야 한다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
const block = makeBlock<FormSelectBlockProps>("f-select-radius", "formSelect", {
|
|
...baseProps,
|
|
borderRadius: "md",
|
|
} as any);
|
|
|
|
render(
|
|
<FormSelectPropertiesPanel
|
|
block={block}
|
|
selectedBlockId="f-select-radius"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const radiusInput = screen.getByLabelText("필드 모서리 둥글기 커스텀");
|
|
fireEvent.change(radiusInput, { target: { value: "8" } });
|
|
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-select-radius",
|
|
expect.objectContaining({ borderRadius: "full" }),
|
|
);
|
|
});
|
|
|
|
it("FormSelect 기본 인풋/셀렉트/옵션 편집 컨트롤은 라이트/다크 듀얼 테마 크롬을 사용해야 한다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
const block = makeBlock<FormSelectBlockProps>("f-select-theme", "formSelect", {
|
|
...baseProps,
|
|
} as any);
|
|
|
|
render(
|
|
<FormSelectPropertiesPanel
|
|
block={block}
|
|
selectedBlockId="f-select-theme"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const labelTypeSelect = screen.getByLabelText("라벨 타입") as HTMLSelectElement;
|
|
const labelTypeClass = labelTypeSelect.getAttribute("class") ?? "";
|
|
expect(labelTypeClass).toContain("bg-white");
|
|
expect(labelTypeClass).toContain("text-slate-900");
|
|
expect(labelTypeClass).toContain("border-slate-300");
|
|
expect(labelTypeClass).toContain("dark:bg-slate-900");
|
|
expect(labelTypeClass).toContain("dark:text-slate-100");
|
|
expect(labelTypeClass).toContain("dark:border-slate-700");
|
|
|
|
const labelInput = screen.getByLabelText("필드 라벨") as HTMLInputElement;
|
|
const labelInputClass = labelInput.getAttribute("class") ?? "";
|
|
expect(labelInputClass).toContain("bg-white");
|
|
expect(labelInputClass).toContain("text-slate-900");
|
|
expect(labelInputClass).toContain("border-slate-300");
|
|
expect(labelInputClass).toContain("dark:bg-slate-900");
|
|
expect(labelInputClass).toContain("dark:text-slate-100");
|
|
expect(labelInputClass).toContain("dark:border-slate-700");
|
|
|
|
const labelDisplaySelect = screen.getByLabelText("라벨 표시 방식") as HTMLSelectElement;
|
|
const labelDisplayClass = labelDisplaySelect.getAttribute("class") ?? "";
|
|
expect(labelDisplayClass).toContain("bg-white");
|
|
expect(labelDisplayClass).toContain("text-slate-900");
|
|
expect(labelDisplayClass).toContain("border-slate-300");
|
|
expect(labelDisplayClass).toContain("dark:bg-slate-900");
|
|
expect(labelDisplayClass).toContain("dark:text-slate-100");
|
|
expect(labelDisplayClass).toContain("dark:border-slate-700");
|
|
|
|
const widthModeSelect = screen.getByLabelText("필드 너비") as HTMLSelectElement;
|
|
const widthModeClass = widthModeSelect.getAttribute("class") ?? "";
|
|
expect(widthModeClass).toContain("bg-white");
|
|
expect(widthModeClass).toContain("text-slate-900");
|
|
expect(widthModeClass).toContain("border-slate-300");
|
|
expect(widthModeClass).toContain("dark:bg-slate-900");
|
|
expect(widthModeClass).toContain("dark:text-slate-100");
|
|
expect(widthModeClass).toContain("dark:border-slate-700");
|
|
|
|
const optionLabelInput = screen.getByPlaceholderText("라벨") as HTMLInputElement;
|
|
const optionLabelClass = optionLabelInput.getAttribute("class") ?? "";
|
|
expect(optionLabelClass).toContain("bg-white");
|
|
expect(optionLabelClass).toContain("text-slate-900");
|
|
expect(optionLabelClass).toContain("border-slate-300");
|
|
expect(optionLabelClass).toContain("dark:bg-slate-900");
|
|
expect(optionLabelClass).toContain("dark:text-slate-100");
|
|
expect(optionLabelClass).toContain("dark:border-slate-700");
|
|
});
|
|
|
|
it("FormSelect 옵션 행은 라벨/값 인풋과 삭제 버튼을 위한 3열 그리드 레이아웃 클래스를 사용해야 한다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
const block = makeBlock<FormSelectBlockProps>("f-select-option-layout", "formSelect", {
|
|
...baseProps,
|
|
} as any);
|
|
|
|
render(
|
|
<FormSelectPropertiesPanel
|
|
block={block}
|
|
selectedBlockId="f-select-option-layout"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const optionLabelInput = screen.getByPlaceholderText("라벨") as HTMLInputElement;
|
|
const rowDiv = optionLabelInput.closest("div") as HTMLDivElement | null;
|
|
expect(rowDiv).not.toBeNull();
|
|
|
|
const rowClass = rowDiv!.getAttribute("class") ?? "";
|
|
expect(rowClass).toContain("grid");
|
|
expect(rowClass).toContain("grid-cols-[minmax(0,1fr)_minmax(0,1fr)_auto]");
|
|
});
|
|
});
|
|
|
|
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<FormCheckboxBlockProps>("f-check-1", "formCheckbox", {
|
|
...baseProps,
|
|
textColorCustom: "#ffffff",
|
|
});
|
|
|
|
render(
|
|
<FormCheckboxPropertiesPanel
|
|
block={block}
|
|
selectedBlockId="f-check-1"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const hexInput = screen.getByLabelText("체크박스 텍스트 색상 HEX");
|
|
fireEvent.change(hexInput, { target: { value: "#112233" } });
|
|
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-check-1",
|
|
expect.objectContaining({ textColorCustom: "#112233" }),
|
|
);
|
|
});
|
|
|
|
it("폼 체크박스 텍스트/줄간격/자간 px 컨트롤이 fontSizeCustom/lineHeightCustom/letterSpacingCustom 으로 저장되어야 한다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
const block = makeBlock<FormCheckboxBlockProps>("f-check-typo-1", "formCheckbox", {
|
|
...baseProps,
|
|
fontSizeCustom: "14px",
|
|
lineHeightCustom: "20px",
|
|
letterSpacingCustom: "0px",
|
|
} as any);
|
|
|
|
render(
|
|
<FormCheckboxPropertiesPanel
|
|
block={block}
|
|
selectedBlockId="f-check-typo-1"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const fontSizeInput = screen.getByLabelText("체크박스 텍스트 크기 (px) 커스텀 (px)");
|
|
fireEvent.change(fontSizeInput, { target: { value: "24" } });
|
|
|
|
const lineHeightInput = screen.getByLabelText("체크박스 줄간격 (px) 커스텀 (px)");
|
|
fireEvent.change(lineHeightInput, { target: { value: "30" } });
|
|
|
|
const letterSpacingInput = screen.getByLabelText("체크박스 자간 (px) 커스텀 (px)");
|
|
fireEvent.change(letterSpacingInput, { target: { value: "1.5" } });
|
|
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-check-typo-1",
|
|
expect.objectContaining({ fontSizeCustom: "24px" }),
|
|
);
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-check-typo-1",
|
|
expect.objectContaining({ lineHeightCustom: "30px" }),
|
|
);
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-check-typo-1",
|
|
expect.objectContaining({ letterSpacingCustom: "1.5px" }),
|
|
);
|
|
});
|
|
|
|
it("폼 체크박스 너비/패딩 컨트롤이 widthPx/paddingX/paddingY 로 저장되어야 한다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
const block = makeBlock<FormCheckboxBlockProps>("f-check-layout-1", "formCheckbox", {
|
|
...baseProps,
|
|
widthMode: "fixed",
|
|
widthPx: 240,
|
|
paddingX: 12,
|
|
paddingY: 10,
|
|
} as any);
|
|
|
|
render(
|
|
<FormCheckboxPropertiesPanel
|
|
block={block}
|
|
selectedBlockId="f-check-layout-1"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const widthPxInput = screen.getByLabelText("필드 고정 너비 커스텀 (px)");
|
|
fireEvent.change(widthPxInput, { target: { value: "320" } });
|
|
|
|
const paddingXInput = screen.getByLabelText("체크박스 가로 패딩 (px) 커스텀 (px)");
|
|
fireEvent.change(paddingXInput, { target: { value: "16" } });
|
|
|
|
const paddingYInput = screen.getByLabelText("체크박스 세로 패딩 (px) 커스텀 (px)");
|
|
fireEvent.change(paddingYInput, { target: { value: "12" } });
|
|
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-check-layout-1",
|
|
expect.objectContaining({ widthPx: 320 }),
|
|
);
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-check-layout-1",
|
|
expect.objectContaining({ paddingX: 16 }),
|
|
);
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-check-layout-1",
|
|
expect.objectContaining({ paddingY: 12 }),
|
|
);
|
|
});
|
|
|
|
it("FormCheckbox 레이아웃 셀렉트는 그룹 라벨 표시 방식이 visible 일 때만 보여야 한다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
const visibleBlock = makeBlock<FormCheckboxBlockProps>("f-check-layout-visible", "formCheckbox", {
|
|
...baseProps,
|
|
groupLabelDisplay: "visible",
|
|
labelLayout: "inline",
|
|
} as any);
|
|
|
|
render(
|
|
<FormCheckboxPropertiesPanel
|
|
block={visibleBlock}
|
|
selectedBlockId="f-check-layout-visible"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const layoutSelect = screen.getByLabelText("레이아웃");
|
|
expect(layoutSelect).toBeTruthy();
|
|
|
|
cleanup();
|
|
|
|
const hiddenBlock = makeBlock<FormCheckboxBlockProps>("f-check-layout-hidden", "formCheckbox", {
|
|
...baseProps,
|
|
groupLabelDisplay: "hidden",
|
|
labelLayout: "inline",
|
|
} as any);
|
|
|
|
render(
|
|
<FormCheckboxPropertiesPanel
|
|
block={hiddenBlock}
|
|
selectedBlockId="f-check-layout-hidden"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
expect(screen.queryByLabelText("레이아웃")).toBeNull();
|
|
});
|
|
|
|
it("그룹 타이틀 타입 셀렉트 변경 시 updateBlock 이 groupLabelMode 로 호출되어야 한다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
const block = makeBlock<FormCheckboxBlockProps>("f-check-group-mode", "formCheckbox", {
|
|
...baseProps,
|
|
groupLabelMode: "text",
|
|
} as any);
|
|
|
|
render(
|
|
<FormCheckboxPropertiesPanel
|
|
block={block}
|
|
selectedBlockId="f-check-group-mode"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const select = screen.getByLabelText("그룹 타이틀 타입");
|
|
fireEvent.change(select, { target: { value: "image" } });
|
|
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-check-group-mode",
|
|
expect.objectContaining({ groupLabelMode: "image" }),
|
|
);
|
|
});
|
|
|
|
it("그룹 타이틀 표시 방식 셀렉트 변경 시 updateBlock 이 groupLabelDisplay 로 호출되어야 한다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
const block = makeBlock<FormCheckboxBlockProps>("f-check-group-display", "formCheckbox", {
|
|
...baseProps,
|
|
groupLabelDisplay: "visible",
|
|
} as any);
|
|
|
|
render(
|
|
<FormCheckboxPropertiesPanel
|
|
block={block}
|
|
selectedBlockId="f-check-group-display"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const select = screen.getByLabelText("그룹 타이틀 표시 방식");
|
|
fireEvent.change(select, { target: { value: "hidden" } });
|
|
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-check-group-display",
|
|
expect.objectContaining({ groupLabelDisplay: "hidden" }),
|
|
);
|
|
});
|
|
|
|
it("레이아웃 셀렉트 변경 시 updateBlock 이 labelLayout 으로 호출되어야 한다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
const block = makeBlock<FormCheckboxBlockProps>("f-check-label-layout", "formCheckbox", {
|
|
...baseProps,
|
|
groupLabelDisplay: "visible",
|
|
labelLayout: "stacked",
|
|
} as any);
|
|
|
|
render(
|
|
<FormCheckboxPropertiesPanel
|
|
block={block}
|
|
selectedBlockId="f-check-label-layout"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const select = screen.getByLabelText("레이아웃");
|
|
fireEvent.change(select, { target: { value: "inline" } });
|
|
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-check-label-layout",
|
|
expect.objectContaining({ labelLayout: "inline" }),
|
|
);
|
|
});
|
|
|
|
it("라벨/필드 간격 (px) 컨트롤 변경 시 updateBlock 이 labelGapPx 로 호출되어야 한다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
const block = makeBlock<FormCheckboxBlockProps>("f-check-label-gap", "formCheckbox", {
|
|
...baseProps,
|
|
groupLabelDisplay: "visible",
|
|
labelLayout: "inline",
|
|
labelGapPx: 8,
|
|
} as any);
|
|
|
|
render(
|
|
<FormCheckboxPropertiesPanel
|
|
block={block}
|
|
selectedBlockId="f-check-label-gap"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const gapInput = screen.getByLabelText("라벨/필드 간격 (px) 커스텀 (px)");
|
|
fireEvent.change(gapInput, { target: { value: "16" } });
|
|
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-check-label-gap",
|
|
expect.objectContaining({ labelGapPx: 16 }),
|
|
);
|
|
});
|
|
|
|
it("그룹 타이틀 인풋 변경 시 updateBlock 이 groupLabel 로 호출되어야 한다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
const block = makeBlock<FormCheckboxBlockProps>("f-check-group-label", "formCheckbox", {
|
|
...baseProps,
|
|
groupLabel: "옵션들",
|
|
} as any);
|
|
|
|
render(
|
|
<FormCheckboxPropertiesPanel
|
|
block={block}
|
|
selectedBlockId="f-check-group-label"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const input = screen.getByLabelText("그룹 타이틀");
|
|
fireEvent.change(input, { target: { value: "새 옵션들" } });
|
|
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-check-group-label",
|
|
expect.objectContaining({ groupLabel: "새 옵션들" }),
|
|
);
|
|
});
|
|
|
|
it("체크박스 옵션 간격 (px) 컨트롤 변경 시 updateBlock 이 optionGapPx 로 호출되어야 한다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
const block = makeBlock<FormCheckboxBlockProps>("f-check-option-gap", "formCheckbox", {
|
|
...baseProps,
|
|
optionGapPx: 4,
|
|
} as any);
|
|
|
|
render(
|
|
<FormCheckboxPropertiesPanel
|
|
block={block}
|
|
selectedBlockId="f-check-option-gap"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const gapInput = screen.getByLabelText("체크박스 옵션 간격 (px) 커스텀 (px)");
|
|
fireEvent.change(gapInput, { target: { value: "10" } });
|
|
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-check-option-gap",
|
|
expect.objectContaining({ optionGapPx: 10 }),
|
|
);
|
|
});
|
|
|
|
it("체크박스 옵션 레이아웃 셀렉트 변경 시 updateBlock 이 optionLayout 으로 호출되어야 한다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
const block = makeBlock<FormCheckboxBlockProps>("f-check-option-layout", "formCheckbox", {
|
|
...baseProps,
|
|
optionLayout: "stacked",
|
|
} as any);
|
|
|
|
render(
|
|
<FormCheckboxPropertiesPanel
|
|
block={block}
|
|
selectedBlockId="f-check-option-layout"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const select = screen.getByLabelText("옵션 레이아웃");
|
|
fireEvent.change(select, { target: { value: "inline" } });
|
|
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-check-option-layout",
|
|
expect.objectContaining({ optionLayout: "inline" }),
|
|
);
|
|
});
|
|
|
|
it("체크박스 필드 모서리 둥글기 컨트롤 변경 시 updateBlock 이 borderRadius 로 호출되어야 한다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
const block = makeBlock<FormCheckboxBlockProps>("f-check-radius", "formCheckbox", {
|
|
...baseProps,
|
|
borderRadius: "md",
|
|
} as any);
|
|
|
|
render(
|
|
<FormCheckboxPropertiesPanel
|
|
block={block}
|
|
selectedBlockId="f-check-radius"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const radiusInput = screen.getByLabelText("필드 모서리 둥글기 커스텀");
|
|
fireEvent.change(radiusInput, { target: { value: "8" } });
|
|
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-check-radius",
|
|
expect.objectContaining({ borderRadius: "full" }),
|
|
);
|
|
});
|
|
|
|
it("FormCheckbox 필드 너비 셀렉트는 라이트/다크 듀얼 테마 크롬을 사용해야 한다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
const block = makeBlock<FormCheckboxBlockProps>("f-check-theme-width", "formCheckbox", {
|
|
...baseProps,
|
|
} as any);
|
|
|
|
render(
|
|
<FormCheckboxPropertiesPanel
|
|
block={block}
|
|
selectedBlockId="f-check-theme-width"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const select = screen.getByLabelText("필드 너비") as HTMLSelectElement;
|
|
const className = select.getAttribute("class") ?? "";
|
|
|
|
expect(className).toContain("bg-white");
|
|
expect(className).toContain("text-slate-900");
|
|
expect(className).toContain("border-slate-300");
|
|
expect(className).toContain("dark:bg-slate-900");
|
|
expect(className).toContain("dark:text-slate-100");
|
|
expect(className).toContain("dark:border-slate-700");
|
|
});
|
|
|
|
it("폼 라디오 텍스트/줄간격/자간 px 컨트롤이 fontSizeCustom/lineHeightCustom/letterSpacingCustom 으로 저장되어야 한다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
const block = makeBlock<FormRadioBlockProps>("f-radio-typo-1", "formRadio", {
|
|
...baseProps,
|
|
fontSizeCustom: "14px",
|
|
lineHeightCustom: "20px",
|
|
letterSpacingCustom: "0px",
|
|
} as any);
|
|
|
|
render(
|
|
<FormRadioPropertiesPanel
|
|
block={block}
|
|
selectedBlockId="f-radio-typo-1"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const fontSizeInput = screen.getByLabelText("라디오 텍스트 크기 (px) 커스텀 (px)");
|
|
fireEvent.change(fontSizeInput, { target: { value: "24" } });
|
|
|
|
const lineHeightInput = screen.getByLabelText("라디오 줄간격 (px) 커스텀 (px)");
|
|
fireEvent.change(lineHeightInput, { target: { value: "30" } });
|
|
|
|
const letterSpacingInput = screen.getByLabelText("라디오 자간 (px) 커스텀 (px)");
|
|
fireEvent.change(letterSpacingInput, { target: { value: "1.5" } });
|
|
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-radio-typo-1",
|
|
expect.objectContaining({ fontSizeCustom: "24px" }),
|
|
);
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-radio-typo-1",
|
|
expect.objectContaining({ lineHeightCustom: "30px" }),
|
|
);
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-radio-typo-1",
|
|
expect.objectContaining({ letterSpacingCustom: "1.5px" }),
|
|
);
|
|
});
|
|
|
|
it("폼 라디오 너비/패딩/옵션 간격 컨트롤이 widthPx/paddingX/paddingY/optionGapPx 로 저장되어야 한다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
const block = makeBlock<FormRadioBlockProps>("f-radio-layout-1", "formRadio", {
|
|
...baseProps,
|
|
widthMode: "fixed",
|
|
widthPx: 240,
|
|
paddingX: 8,
|
|
paddingY: 4,
|
|
optionGapPx: 8,
|
|
} as any);
|
|
|
|
render(
|
|
<FormRadioPropertiesPanel
|
|
block={block}
|
|
selectedBlockId="f-radio-layout-1"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const widthPxInput = screen.getByLabelText("필드 고정 너비 커스텀 (px)");
|
|
fireEvent.change(widthPxInput, { target: { value: "320" } });
|
|
|
|
const paddingXInput = screen.getByLabelText("라디오 가로 패딩 (px) 커스텀 (px)");
|
|
fireEvent.change(paddingXInput, { target: { value: "12" } });
|
|
|
|
const paddingYInput = screen.getByLabelText("라디오 세로 패딩 (px) 커스텀 (px)");
|
|
fireEvent.change(paddingYInput, { target: { value: "6" } });
|
|
|
|
const optionGapInput = screen.getByLabelText("라디오 옵션 간격 (px) 커스텀 (px)");
|
|
fireEvent.change(optionGapInput, { target: { value: "10" } });
|
|
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-radio-layout-1",
|
|
expect.objectContaining({ widthPx: 320 }),
|
|
);
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-radio-layout-1",
|
|
expect.objectContaining({ paddingX: 12 }),
|
|
);
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-radio-layout-1",
|
|
expect.objectContaining({ paddingY: 6 }),
|
|
);
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-radio-layout-1",
|
|
expect.objectContaining({ optionGapPx: 10 }),
|
|
);
|
|
});
|
|
|
|
it("FormRadio 그룹 타이틀 레이아웃 셀렉트는 그룹 타이틀 표시 방식이 visible 일 때만 보여야 한다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
const visibleBlock = makeBlock<FormRadioBlockProps>("f-radio-layout-visible", "formRadio", {
|
|
...baseProps,
|
|
groupLabelDisplay: "visible",
|
|
labelLayout: "inline",
|
|
} as any);
|
|
|
|
render(
|
|
<FormRadioPropertiesPanel
|
|
block={visibleBlock}
|
|
selectedBlockId="f-radio-layout-visible"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const layoutSelect = screen.getByLabelText("그룹 타이틀 레이아웃");
|
|
expect(layoutSelect).toBeTruthy();
|
|
|
|
cleanup();
|
|
|
|
const hiddenBlock = makeBlock<FormRadioBlockProps>("f-radio-layout-hidden", "formRadio", {
|
|
...baseProps,
|
|
groupLabelDisplay: "hidden",
|
|
labelLayout: "inline",
|
|
} as any);
|
|
|
|
render(
|
|
<FormRadioPropertiesPanel
|
|
block={hiddenBlock}
|
|
selectedBlockId="f-radio-layout-hidden"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
expect(screen.queryByLabelText("그룹 타이틀 레이아웃")).toBeNull();
|
|
});
|
|
|
|
it("그룹 타이틀 타입 셀렉트 변경 시 updateBlock 이 groupLabelMode 로 호출되어야 한다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
const block = makeBlock<FormRadioBlockProps>("f-radio-group-mode", "formRadio", {
|
|
...baseProps,
|
|
groupLabelMode: "text",
|
|
} as any);
|
|
|
|
render(
|
|
<FormRadioPropertiesPanel
|
|
block={block}
|
|
selectedBlockId="f-radio-group-mode"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const select = screen.getByLabelText("그룹 타이틀 타입");
|
|
fireEvent.change(select, { target: { value: "image" } });
|
|
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-radio-group-mode",
|
|
expect.objectContaining({ groupLabelMode: "image" }),
|
|
);
|
|
});
|
|
|
|
it("그룹 타이틀 표시 방식 셀렉트 변경 시 updateBlock 이 groupLabelDisplay 로 호출되어야 한다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
const block = makeBlock<FormRadioBlockProps>("f-radio-group-display", "formRadio", {
|
|
...baseProps,
|
|
groupLabelDisplay: "visible",
|
|
} as any);
|
|
|
|
render(
|
|
<FormRadioPropertiesPanel
|
|
block={block}
|
|
selectedBlockId="f-radio-group-display"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const select = screen.getByLabelText("그룹 타이틀 표시 방식");
|
|
fireEvent.change(select, { target: { value: "hidden" } });
|
|
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-radio-group-display",
|
|
expect.objectContaining({ groupLabelDisplay: "hidden" }),
|
|
);
|
|
});
|
|
|
|
it("그룹 타이틀 레이아웃 셀렉트 변경 시 updateBlock 이 labelLayout 으로 호출되어야 한다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
const block = makeBlock<FormRadioBlockProps>("f-radio-label-layout", "formRadio", {
|
|
...baseProps,
|
|
groupLabelDisplay: "visible",
|
|
labelLayout: "stacked",
|
|
} as any);
|
|
|
|
render(
|
|
<FormRadioPropertiesPanel
|
|
block={block}
|
|
selectedBlockId="f-radio-label-layout"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const select = screen.getByLabelText("그룹 타이틀 레이아웃");
|
|
fireEvent.change(select, { target: { value: "inline" } });
|
|
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-radio-label-layout",
|
|
expect.objectContaining({ labelLayout: "inline" }),
|
|
);
|
|
});
|
|
|
|
it("라벨/필드 간격 (px) 컨트롤 변경 시 updateBlock 이 labelGapPx 로 호출되어야 한다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
const block = makeBlock<FormRadioBlockProps>("f-radio-label-gap", "formRadio", {
|
|
...baseProps,
|
|
groupLabelDisplay: "visible",
|
|
labelLayout: "inline",
|
|
labelGapPx: 8,
|
|
} as any);
|
|
|
|
render(
|
|
<FormRadioPropertiesPanel
|
|
block={block}
|
|
selectedBlockId="f-radio-label-gap"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const gapInput = screen.getByLabelText("라벨/필드 간격 (px) 커스텀 (px)");
|
|
fireEvent.change(gapInput, { target: { value: "16" } });
|
|
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-radio-label-gap",
|
|
expect.objectContaining({ labelGapPx: 16 }),
|
|
);
|
|
});
|
|
|
|
it("그룹 타이틀 인풋 변경 시 updateBlock 이 groupLabel 로 호출되어야 한다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
const block = makeBlock<FormRadioBlockProps>("f-radio-group-label", "formRadio", {
|
|
...baseProps,
|
|
groupLabel: "플랜",
|
|
} as any);
|
|
|
|
render(
|
|
<FormRadioPropertiesPanel
|
|
block={block}
|
|
selectedBlockId="f-radio-group-label"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const input = screen.getByLabelText("그룹 타이틀");
|
|
fireEvent.change(input, { target: { value: "새 플랜" } });
|
|
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-radio-group-label",
|
|
expect.objectContaining({ groupLabel: "새 플랜" }),
|
|
);
|
|
});
|
|
|
|
it("그룹 타이틀 이미지 소스 셀렉트 변경 시 updateBlock 이 groupLabelImageSource 로 호출되어야 한다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
const block = makeBlock<FormRadioBlockProps>("f-radio-group-image-source", "formRadio", {
|
|
...baseProps,
|
|
groupLabelMode: "image",
|
|
groupLabelImageSource: "url",
|
|
} as any);
|
|
|
|
render(
|
|
<FormRadioPropertiesPanel
|
|
block={block}
|
|
selectedBlockId="f-radio-group-image-source"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const select = screen.getByLabelText("그룹 타이틀 이미지 소스");
|
|
fireEvent.change(select, { target: { value: "upload" } });
|
|
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-radio-group-image-source",
|
|
expect.objectContaining({ groupLabelImageSource: "upload" }),
|
|
);
|
|
});
|
|
|
|
it("그룹 타이틀 이미지 URL 인풋 변경 시 updateBlock 이 groupLabelImageUrl 로 호출되어야 한다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
const block = makeBlock<FormRadioBlockProps>("f-radio-group-image-url", "formRadio", {
|
|
...baseProps,
|
|
groupLabelMode: "image",
|
|
groupLabelImageSource: "url",
|
|
groupLabelImageUrl: "https://example.com/old.png",
|
|
} as any);
|
|
|
|
render(
|
|
<FormRadioPropertiesPanel
|
|
block={block}
|
|
selectedBlockId="f-radio-group-image-url"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const input = screen.getByLabelText("그룹 타이틀 이미지 URL");
|
|
fireEvent.change(input, { target: { value: "https://example.com/new.png" } });
|
|
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-radio-group-image-url",
|
|
expect.objectContaining({ groupLabelImageUrl: "https://example.com/new.png" }),
|
|
);
|
|
});
|
|
|
|
it("옵션 이미지 소스 셀렉트 변경 시 updateBlock 이 optionImageSource 로 호출되어야 한다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
const block = makeBlock<FormRadioBlockProps>("f-radio-option-image-source", "formRadio", {
|
|
...baseProps,
|
|
optionImageSource: "url",
|
|
} as any);
|
|
|
|
render(
|
|
<FormRadioPropertiesPanel
|
|
block={block}
|
|
selectedBlockId="f-radio-option-image-source"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const select = screen.getByLabelText("옵션 이미지 소스");
|
|
fireEvent.change(select, { target: { value: "upload" } });
|
|
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-radio-option-image-source",
|
|
expect.objectContaining({ optionImageSource: "upload" }),
|
|
);
|
|
});
|
|
|
|
it("라디오 배경 색상 HEX 인풋 변경 시 updateBlock 이 fillColorCustom 으로 호출되어야 한다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
const block = makeBlock<FormRadioBlockProps>("f-radio-fill-color", "formRadio", {
|
|
...baseProps,
|
|
fillColorCustom: "#000000",
|
|
} as any);
|
|
|
|
render(
|
|
<FormRadioPropertiesPanel
|
|
block={block}
|
|
selectedBlockId="f-radio-fill-color"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const hexInput = screen.getByLabelText("라디오 배경 색상 HEX");
|
|
fireEvent.change(hexInput, { target: { value: "#445566" } });
|
|
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-radio-fill-color",
|
|
expect.objectContaining({ fillColorCustom: "#445566" }),
|
|
);
|
|
});
|
|
|
|
it("라디오 테두리 색상 HEX 인풋 변경 시 updateBlock 이 strokeColorCustom 으로 호출되어야 한다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
const block = makeBlock<FormRadioBlockProps>("f-radio-stroke-color", "formRadio", {
|
|
...baseProps,
|
|
strokeColorCustom: "#000000",
|
|
} as any);
|
|
|
|
render(
|
|
<FormRadioPropertiesPanel
|
|
block={block}
|
|
selectedBlockId="f-radio-stroke-color"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const hexInput = screen.getByLabelText("라디오 테두리 색상 HEX");
|
|
fireEvent.change(hexInput, { target: { value: "#778899" } });
|
|
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-radio-stroke-color",
|
|
expect.objectContaining({ strokeColorCustom: "#778899" }),
|
|
);
|
|
});
|
|
|
|
it("라디오 필드 모서리 둥글기 컨트롤 변경 시 updateBlock 이 borderRadius 로 호출되어야 한다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
const block = makeBlock<FormRadioBlockProps>("f-radio-radius", "formRadio", {
|
|
...baseProps,
|
|
borderRadius: "md",
|
|
} as any);
|
|
|
|
render(
|
|
<FormRadioPropertiesPanel
|
|
block={block}
|
|
selectedBlockId="f-radio-radius"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const radiusInput = screen.getByLabelText("필드 모서리 둥글기 커스텀");
|
|
fireEvent.change(radiusInput, { target: { value: "8" } });
|
|
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"f-radio-radius",
|
|
expect.objectContaining({ borderRadius: "full" }),
|
|
);
|
|
});
|
|
|
|
it("FormRadio 기본 인풋/셀렉트/옵션 편집 컨트롤은 라이트/다크 듀얼 테마 크롬을 사용해야 한다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
const block = makeBlock<FormRadioBlockProps>("f-radio-theme", "formRadio", {
|
|
...baseProps,
|
|
} as any);
|
|
|
|
render(
|
|
<FormRadioPropertiesPanel
|
|
block={block}
|
|
selectedBlockId="f-radio-theme"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const groupTitleInput = screen.getByLabelText("그룹 타이틀") as HTMLInputElement;
|
|
const groupTitleClass = groupTitleInput.getAttribute("class") ?? "";
|
|
expect(groupTitleClass).toContain("bg-white");
|
|
expect(groupTitleClass).toContain("text-slate-900");
|
|
expect(groupTitleClass).toContain("border-slate-300");
|
|
expect(groupTitleClass).toContain("dark:bg-slate-900");
|
|
expect(groupTitleClass).toContain("dark:text-slate-100");
|
|
expect(groupTitleClass).toContain("dark:border-slate-700");
|
|
|
|
const groupDisplaySelect = screen.getByLabelText("그룹 타이틀 표시 방식") as HTMLSelectElement;
|
|
const groupDisplayClass = groupDisplaySelect.getAttribute("class") ?? "";
|
|
expect(groupDisplayClass).toContain("bg-white");
|
|
expect(groupDisplayClass).toContain("text-slate-900");
|
|
expect(groupDisplayClass).toContain("border-slate-300");
|
|
expect(groupDisplayClass).toContain("dark:bg-slate-900");
|
|
expect(groupDisplayClass).toContain("dark:text-slate-100");
|
|
expect(groupDisplayClass).toContain("dark:border-slate-700");
|
|
|
|
const optionLayoutSelect = screen.getByLabelText("옵션 레이아웃") as HTMLSelectElement;
|
|
const optionLayoutClass = optionLayoutSelect.getAttribute("class") ?? "";
|
|
expect(optionLayoutClass).toContain("bg-white");
|
|
expect(optionLayoutClass).toContain("text-slate-900");
|
|
expect(optionLayoutClass).toContain("border-slate-300");
|
|
expect(optionLayoutClass).toContain("dark:bg-slate-900");
|
|
expect(optionLayoutClass).toContain("dark:text-slate-100");
|
|
expect(optionLayoutClass).toContain("dark:border-slate-700");
|
|
|
|
const formFieldNameInput = screen.getByLabelText("전송 키") as HTMLInputElement;
|
|
const formFieldNameClass = formFieldNameInput.getAttribute("class") ?? "";
|
|
expect(formFieldNameClass).toContain("bg-white");
|
|
expect(formFieldNameClass).toContain("text-slate-900");
|
|
expect(formFieldNameClass).toContain("border-slate-300");
|
|
expect(formFieldNameClass).toContain("dark:bg-slate-900");
|
|
expect(formFieldNameClass).toContain("dark:text-slate-100");
|
|
expect(formFieldNameClass).toContain("dark:border-slate-700");
|
|
|
|
const optionLabelInput = screen.getByPlaceholderText("라벨") as HTMLInputElement;
|
|
const optionLabelClass = optionLabelInput.getAttribute("class") ?? "";
|
|
expect(optionLabelClass).toContain("bg-white");
|
|
expect(optionLabelClass).toContain("text-slate-900");
|
|
expect(optionLabelClass).toContain("border-slate-300");
|
|
expect(optionLabelClass).toContain("dark:bg-slate-900");
|
|
expect(optionLabelClass).toContain("dark:text-slate-100");
|
|
expect(optionLabelClass).toContain("dark:border-slate-700");
|
|
});
|
|
});
|