Files
page-builder/tests/unit/ImagePropertiesPanel.spec.tsx
T
jaybe 676e58cad7
CI / test (push) Failing after 5m22s
CI / e2e (push) Has been skipped
CI / pr_and_merge (push) Has been skipped
테마 적용
2025-12-09 18:53:21 +09:00

235 lines
7.7 KiB
TypeScript

import { describe, it, expect, vi, afterEach } from "vitest";
import { render, screen, fireEvent, cleanup } from "@testing-library/react";
import { ImagePropertiesPanel } from "@/app/editor/panels/ImagePropertiesPanel";
import type { ImageBlockProps } from "@/features/editor/state/editorStore";
// ImagePropertiesPanel 컨트롤 TDD
// - 카드 배경색(backgroundColorCustom) HEX 인풋 변경 시 updateBlock 이 올바르게 호출되는지
describe("ImagePropertiesPanel", () => {
const baseProps: ImageBlockProps = {
src: "/images/example.png",
alt: "예시 이미지",
align: "center",
widthMode: "auto",
borderRadius: "md",
};
afterEach(() => {
cleanup();
});
it("카드 배경색 HEX 인풋 변경 시 updateBlock 이 backgroundColorCustom 으로 호출되어야 한다", () => {
const updateBlock = vi.fn();
render(
<ImagePropertiesPanel
imageProps={{ ...baseProps, backgroundColorCustom: "#111111" }}
selectedBlockId="image-1"
updateBlock={updateBlock}
/>,
);
const hexInput = screen.getByLabelText("이미지 카드 배경색 HEX");
fireEvent.change(hexInput, { target: { value: "#112233" } });
expect(updateBlock).toHaveBeenCalledWith(
"image-1",
expect.objectContaining({ backgroundColorCustom: "#112233" }),
);
});
it("이미지 소스 셀렉트에서 url 선택 시 sourceType 이 externalUrl 이 되고 assetId 가 null 로 설정되어야 한다", () => {
const updateBlock = vi.fn();
render(
<ImagePropertiesPanel
imageProps={{
...baseProps,
src: "/api/image/asset-1",
sourceType: "asset",
assetId: "asset-1",
} as any}
selectedBlockId="image-source-1"
updateBlock={updateBlock}
/>,
);
const select = screen.getByLabelText("이미지 소스");
fireEvent.change(select, { target: { value: "url" } });
expect(updateBlock).toHaveBeenCalledWith(
"image-source-1",
expect.objectContaining({ sourceType: "externalUrl", assetId: null }),
);
});
it("이미지 URL 인풋 변경 시 src / sourceType / assetId 가 externalUrl/null 로 업데이트되어야 한다", () => {
const updateBlock = vi.fn();
render(
<ImagePropertiesPanel
imageProps={{
...baseProps,
src: "https://example.com/before.png",
sourceType: "externalUrl",
assetId: "old-asset",
} as any}
selectedBlockId="image-url-1"
updateBlock={updateBlock}
/>,
);
const urlInput = screen.getByLabelText("이미지 URL");
fireEvent.change(urlInput, { target: { value: "https://example.com/after.png" } });
expect(updateBlock).toHaveBeenCalledWith(
"image-url-1",
expect.objectContaining({
src: "https://example.com/after.png",
sourceType: "externalUrl",
assetId: null,
}),
);
});
it("대체 텍스트 인풋 변경 시 updateBlock 이 alt 로 호출되어야 한다", () => {
const updateBlock = vi.fn();
render(
<ImagePropertiesPanel
imageProps={baseProps}
selectedBlockId="image-alt-1"
updateBlock={updateBlock}
/>,
);
const altInput = screen.getByLabelText("대체 텍스트");
fireEvent.change(altInput, { target: { value: "새 대체 텍스트" } });
expect(updateBlock).toHaveBeenCalledWith(
"image-alt-1",
expect.objectContaining({ alt: "새 대체 텍스트" }),
);
});
it("이미지 정렬 셀렉트 변경 시 updateBlock 이 align 으로 호출되어야 한다", () => {
const updateBlock = vi.fn();
render(
<ImagePropertiesPanel
imageProps={{ ...baseProps, align: "center" }}
selectedBlockId="image-align-1"
updateBlock={updateBlock}
/>,
);
const select = screen.getByLabelText("이미지 정렬");
fireEvent.change(select, { target: { value: "left" } });
expect(updateBlock).toHaveBeenCalledWith(
"image-align-1",
expect.objectContaining({ align: "left" }),
);
});
it("너비 모드 셀렉트 변경 시 updateBlock 이 widthMode 로 호출되어야 한다", () => {
const updateBlock = vi.fn();
render(
<ImagePropertiesPanel
imageProps={{ ...baseProps, widthMode: "auto" }}
selectedBlockId="image-width-mode-1"
updateBlock={updateBlock}
/>,
);
const select = screen.getByLabelText("이미지 너비 모드");
fireEvent.change(select, { target: { value: "fixed" } });
expect(updateBlock).toHaveBeenCalledWith(
"image-width-mode-1",
expect.objectContaining({ widthMode: "fixed" }),
);
});
it("너비 모드가 fixed 일 때 고정 너비 슬라이더 변경 시 updateBlock 이 widthPx 로 호출되어야 한다", () => {
const updateBlock = vi.fn();
render(
<ImagePropertiesPanel
imageProps={{ ...baseProps, widthMode: "fixed", widthPx: 320 }}
selectedBlockId="image-width-fixed-1"
updateBlock={updateBlock}
/>,
);
const slider = screen.getByLabelText("고정 너비 (px) 슬라이더");
fireEvent.change(slider, { target: { value: "480" } });
expect(updateBlock).toHaveBeenCalledWith(
"image-width-fixed-1",
expect.objectContaining({ widthPx: 480 }),
);
});
it("모서리 둥글기 슬라이더 변경 시 updateBlock 이 borderRadius 와 borderRadiusPx 를 함께 업데이트해야 한다", () => {
const updateBlock = vi.fn();
render(
<ImagePropertiesPanel
imageProps={{ ...baseProps, borderRadius: "md", borderRadiusPx: 60 }}
selectedBlockId="image-radius-1"
updateBlock={updateBlock}
/>,
);
const slider = screen.getByLabelText("모서리 둥글기 슬라이더");
fireEvent.change(slider, { target: { value: "24" } });
expect(updateBlock).toHaveBeenCalledWith(
"image-radius-1",
expect.objectContaining({ borderRadius: "sm", borderRadiusPx: 24 }),
);
});
it("ImagePropertiesPanel 주요 인풋/셀렉트 컨트롤은 라이트/다크 듀얼 테마 크롬을 사용해야 한다", () => {
const updateBlock = vi.fn();
render(
<ImagePropertiesPanel
imageProps={baseProps as any}
selectedBlockId="image-theme-1"
updateBlock={updateBlock}
/>,
);
const sourceSelect = screen.getByLabelText("이미지 소스") as HTMLSelectElement;
const sourceClass = sourceSelect.getAttribute("class") ?? "";
expect(sourceClass).toContain("bg-white");
expect(sourceClass).toContain("text-slate-900");
expect(sourceClass).toContain("border-slate-300");
expect(sourceClass).toContain("dark:bg-slate-900");
expect(sourceClass).toContain("dark:text-slate-100");
expect(sourceClass).toContain("dark:border-slate-700");
const urlInput = screen.getByLabelText("이미지 URL") as HTMLInputElement;
const urlClass = urlInput.getAttribute("class") ?? "";
expect(urlClass).toContain("bg-white");
expect(urlClass).toContain("text-slate-900");
expect(urlClass).toContain("border-slate-300");
expect(urlClass).toContain("dark:bg-slate-900");
expect(urlClass).toContain("dark:text-slate-100");
expect(urlClass).toContain("dark:border-slate-700");
const alignSelect = screen.getByLabelText("이미지 정렬") as HTMLSelectElement;
const alignClass = alignSelect.getAttribute("class") ?? "";
expect(alignClass).toContain("bg-white");
expect(alignClass).toContain("text-slate-900");
expect(alignClass).toContain("border-slate-300");
expect(alignClass).toContain("dark:bg-slate-900");
expect(alignClass).toContain("dark:text-slate-100");
expect(alignClass).toContain("dark:border-slate-700");
});
});