테마 적용
CI / test (push) Failing after 5m22s
CI / e2e (push) Has been skipped
CI / pr_and_merge (push) Has been skipped

This commit is contained in:
2025-12-09 18:53:21 +09:00
parent 9d8c4538c7
commit 676e58cad7
71 changed files with 3394 additions and 498 deletions
@@ -104,6 +104,56 @@ describe("FormInputPropertiesPanel", () => {
);
});
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();
@@ -266,6 +316,67 @@ describe("FormInputPropertiesPanel", () => {
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", () => {
@@ -679,6 +790,91 @@ describe("FormSelectPropertiesPanel", () => {
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", () => {
@@ -1042,6 +1238,32 @@ describe("FormCheckboxPropertiesPanel", () => {
);
});
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();
@@ -1452,4 +1674,65 @@ describe("FormCheckboxPropertiesPanel", () => {
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");
});
});