367 lines
12 KiB
TypeScript
367 lines
12 KiB
TypeScript
import { describe, it, expect, vi, afterEach } from "vitest";
|
|
import { render, screen, fireEvent, cleanup } from "@testing-library/react";
|
|
import type { SectionBlockProps } from "@/features/editor/state/editorStore";
|
|
import { SectionPropertiesPanel } from "@/app/editor/panels/SectionPropertiesPanel";
|
|
|
|
// 섹션 패널의 숫자 슬라이더/프리셋 제어가 정상적으로 동작하는지 최소 한 번은 검증한다.
|
|
describe("SectionPropertiesPanel", () => {
|
|
const baseProps: SectionBlockProps = {
|
|
background: "default",
|
|
paddingY: "md",
|
|
columns: [],
|
|
maxWidthMode: "normal",
|
|
gapX: "md",
|
|
};
|
|
|
|
afterEach(() => {
|
|
cleanup();
|
|
});
|
|
|
|
it("섹션 배경 색상 HEX 인풋 변경 시 updateBlock 이 backgroundColorCustom 으로 호출되어야 한다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
render(
|
|
<SectionPropertiesPanel
|
|
sectionProps={{
|
|
...baseProps,
|
|
backgroundColorCustom: "#111111",
|
|
}}
|
|
selectedBlockId="section-bg-1"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const hexInput = screen.getByLabelText("섹션 배경 색상 HEX 입력");
|
|
fireEvent.change(hexInput, { target: { value: "#112233" } });
|
|
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"section-bg-1",
|
|
expect.objectContaining({ backgroundColorCustom: "#112233" }),
|
|
);
|
|
});
|
|
|
|
it("가로 위치 프리셋 드롭다운 변경 시 backgroundImagePositionXPercent 가 0/50/100 등으로 설정된다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
render(
|
|
<SectionPropertiesPanel
|
|
sectionProps={{
|
|
...baseProps,
|
|
backgroundImagePositionMode: "custom",
|
|
backgroundImagePositionXPercent: 50,
|
|
backgroundImageSrc: "https://example.com/bg.png",
|
|
backgroundImageSourceType: "externalUrl",
|
|
}}
|
|
selectedBlockId="section-xy-x"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const presetSelect = screen.getByLabelText("배경 이미지 가로 위치 프리셋");
|
|
fireEvent.change(presetSelect, { target: { value: "left" } });
|
|
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"section-xy-x",
|
|
expect.objectContaining({ backgroundImagePositionXPercent: 0 }),
|
|
);
|
|
});
|
|
|
|
it("세로 위치 프리셋 드롭다운 변경 시 backgroundImagePositionYPercent 가 0/50/100 등으로 설정된다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
render(
|
|
<SectionPropertiesPanel
|
|
sectionProps={{
|
|
...baseProps,
|
|
backgroundImagePositionMode: "custom",
|
|
backgroundImagePositionYPercent: 50,
|
|
backgroundImageSrc: "https://example.com/bg.png",
|
|
backgroundImageSourceType: "externalUrl",
|
|
}}
|
|
selectedBlockId="section-xy-y"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const presetSelect = screen.getByLabelText("배경 이미지 세로 위치 프리셋");
|
|
fireEvent.change(presetSelect, { target: { value: "top" } });
|
|
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"section-xy-y",
|
|
expect.objectContaining({ backgroundImagePositionYPercent: 0 }),
|
|
);
|
|
});
|
|
|
|
it("위치 모드를 custom 으로 두면 X/Y 퍼센트 슬라이더 변경 시 backgroundImagePositionXPercent/YPercent 가 업데이트된다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
render(
|
|
<SectionPropertiesPanel
|
|
sectionProps={{
|
|
...baseProps,
|
|
backgroundImagePositionMode: "custom",
|
|
backgroundImagePositionXPercent: 50,
|
|
backgroundImagePositionYPercent: 50,
|
|
backgroundImageSrc: "https://example.com/bg.png",
|
|
backgroundImageSourceType: "externalUrl",
|
|
}}
|
|
selectedBlockId="section-xy"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const xSlider = screen.getByLabelText("배경 이미지 가로 위치 슬라이더");
|
|
fireEvent.change(xSlider, { target: { value: "30" } });
|
|
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"section-xy",
|
|
expect.objectContaining({
|
|
backgroundImagePositionMode: "custom",
|
|
backgroundImagePositionXPercent: 30,
|
|
}),
|
|
);
|
|
|
|
const ySlider = screen.getByLabelText("배경 이미지 세로 위치 슬라이더");
|
|
fireEvent.change(ySlider, { target: { value: "70" } });
|
|
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"section-xy",
|
|
expect.objectContaining({
|
|
backgroundImagePositionMode: "custom",
|
|
backgroundImagePositionYPercent: 70,
|
|
}),
|
|
);
|
|
});
|
|
|
|
it("세로 패딩 프리셋/슬라이더 변경 시 updateBlock 이 paddingYPx 로 호출된다", () => {
|
|
const updateBlock = vi.fn();
|
|
render(
|
|
<SectionPropertiesPanel
|
|
sectionProps={{ ...baseProps, paddingYPx: 48 }}
|
|
selectedBlockId="section-1"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const slider = screen.getByLabelText("세로 패딩 슬라이더");
|
|
fireEvent.change(slider, { target: { value: "40" } });
|
|
|
|
expect(updateBlock).toHaveBeenCalledWith("section-1", expect.objectContaining({ paddingYPx: 40 }));
|
|
});
|
|
|
|
it("배경 이미지 URL 인풋 변경 시 updateBlock 이 backgroundImageSrc/SourceType/AssetId 로 호출된다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
render(
|
|
<SectionPropertiesPanel
|
|
sectionProps={{ ...baseProps, backgroundImageSrc: "", backgroundImageSourceType: "externalUrl" }}
|
|
selectedBlockId="section-2"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const urlInput = screen.getByLabelText("배경 이미지 URL");
|
|
fireEvent.change(urlInput, { target: { value: "https://example.com/bg.png" } });
|
|
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"section-2",
|
|
expect.objectContaining({
|
|
backgroundImageSrc: "https://example.com/bg.png",
|
|
backgroundImageSourceType: "externalUrl",
|
|
backgroundImageAssetId: null,
|
|
}),
|
|
);
|
|
});
|
|
|
|
it("배경 이미지 크기 셀렉트 변경 시 updateBlock 이 backgroundImageSize 로 호출된다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
render(
|
|
<SectionPropertiesPanel
|
|
sectionProps={{
|
|
...baseProps,
|
|
backgroundImageSize: "cover",
|
|
backgroundImageSrc: "https://example.com/bg.png",
|
|
backgroundImageSourceType: "externalUrl",
|
|
}}
|
|
selectedBlockId="section-3"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const select = screen.getByLabelText("배경 이미지 크기");
|
|
fireEvent.change(select, { target: { value: "contain" } });
|
|
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"section-3",
|
|
expect.objectContaining({ backgroundImageSize: "contain" }),
|
|
);
|
|
});
|
|
|
|
it("배경 이미지 위치 셀렉트 변경 시 updateBlock 이 backgroundImagePosition 으로 호출된다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
render(
|
|
<SectionPropertiesPanel
|
|
sectionProps={{
|
|
...baseProps,
|
|
backgroundImagePosition: "center",
|
|
backgroundImageSrc: "https://example.com/bg.png",
|
|
backgroundImageSourceType: "externalUrl",
|
|
}}
|
|
selectedBlockId="section-4"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const select = screen.getByLabelText("배경 이미지 위치");
|
|
fireEvent.change(select, { target: { value: "top" } });
|
|
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"section-4",
|
|
expect.objectContaining({ backgroundImagePosition: "top" }),
|
|
);
|
|
});
|
|
|
|
it("기본 상태에서는 배경 이미지 소스가 '없음' 이고, '없음' 선택 시 관련 필드가 초기화된다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
render(
|
|
<SectionPropertiesPanel
|
|
sectionProps={{ ...baseProps }}
|
|
selectedBlockId="section-0"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const sourceSelect = screen.getByLabelText("배경 이미지 소스") as HTMLSelectElement;
|
|
expect(sourceSelect.value).toBe("none");
|
|
|
|
fireEvent.change(sourceSelect, { target: { value: "none" } });
|
|
|
|
expect(updateBlock).toHaveBeenCalledWith("section-0", {
|
|
backgroundImageSrc: undefined,
|
|
backgroundImageSourceType: undefined,
|
|
backgroundImageAssetId: null,
|
|
} as any);
|
|
|
|
// "없음" 상태에서는 크기/위치/반복 컨트롤이 렌더되지 않아야 한다.
|
|
expect(screen.queryByLabelText("배경 이미지 크기")).toBeNull();
|
|
expect(screen.queryByLabelText("배경 이미지 위치")).toBeNull();
|
|
expect(screen.queryByLabelText("배경 이미지 반복")).toBeNull();
|
|
});
|
|
|
|
it("배경 이미지 반복 셀렉트 변경 시 updateBlock 이 backgroundImageRepeat 로 호출된다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
render(
|
|
<SectionPropertiesPanel
|
|
sectionProps={{
|
|
...baseProps,
|
|
backgroundImageRepeat: "no-repeat",
|
|
backgroundImageSrc: "https://example.com/bg.png",
|
|
backgroundImageSourceType: "externalUrl",
|
|
}}
|
|
selectedBlockId="section-6"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const repeatSelect = screen.getByLabelText("배경 이미지 반복");
|
|
fireEvent.change(repeatSelect, { target: { value: "repeat-x" } });
|
|
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"section-6",
|
|
expect.objectContaining({ backgroundImageRepeat: "repeat-x" }),
|
|
);
|
|
});
|
|
|
|
it("배경 이미지 소스 셀렉트 변경 시 updateBlock 이 backgroundImageSourceType/backgroundImageAssetId 로 호출된다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
render(
|
|
<SectionPropertiesPanel
|
|
sectionProps={{
|
|
...baseProps,
|
|
backgroundImageSrc: "/api/image/asset-1",
|
|
backgroundImageSourceType: "asset",
|
|
backgroundImageAssetId: "asset-1",
|
|
}}
|
|
selectedBlockId="section-5"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const sourceSelect = screen.getByLabelText("배경 이미지 소스") as HTMLSelectElement;
|
|
expect(sourceSelect.value).toBe("upload");
|
|
|
|
fireEvent.change(sourceSelect, { target: { value: "url" } });
|
|
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"section-5",
|
|
expect.objectContaining({
|
|
backgroundImageSourceType: "externalUrl",
|
|
backgroundImageAssetId: null,
|
|
}),
|
|
);
|
|
});
|
|
|
|
it("배경 비디오 URL 인풋 변경 시 updateBlock 이 backgroundVideoSrc/backgroundVideoSourceType/backgroundVideoAssetId 로 호출된다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
render(
|
|
<SectionPropertiesPanel
|
|
sectionProps={{
|
|
...baseProps,
|
|
backgroundVideoSrc: "",
|
|
backgroundVideoSourceType: "externalUrl",
|
|
}}
|
|
selectedBlockId="section-video-1"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const urlInput = screen.getByLabelText("배경 비디오 URL");
|
|
fireEvent.change(urlInput, { target: { value: "https://example.com/bg-video.mp4" } });
|
|
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"section-video-1",
|
|
expect.objectContaining({
|
|
backgroundVideoSrc: "https://example.com/bg-video.mp4",
|
|
backgroundVideoSourceType: "externalUrl",
|
|
backgroundVideoAssetId: null,
|
|
}),
|
|
);
|
|
});
|
|
|
|
it("배경 비디오 소스 셀렉트 변경 시 updateBlock 이 backgroundVideoSourceType/backgroundVideoAssetId 로 호출된다", () => {
|
|
const updateBlock = vi.fn();
|
|
|
|
render(
|
|
<SectionPropertiesPanel
|
|
sectionProps={{
|
|
...baseProps,
|
|
backgroundVideoSrc: "/api/video/asset-video-1",
|
|
backgroundVideoSourceType: "asset",
|
|
backgroundVideoAssetId: "asset-video-1",
|
|
}}
|
|
selectedBlockId="section-video-2"
|
|
updateBlock={updateBlock}
|
|
/>,
|
|
);
|
|
|
|
const sourceSelect = screen.getByLabelText("배경 비디오 소스") as HTMLSelectElement;
|
|
expect(sourceSelect.value).toBe("upload");
|
|
|
|
fireEvent.change(sourceSelect, { target: { value: "url" } });
|
|
|
|
expect(updateBlock).toHaveBeenCalledWith(
|
|
"section-video-2",
|
|
expect.objectContaining({
|
|
backgroundVideoSourceType: "externalUrl",
|
|
backgroundVideoAssetId: null,
|
|
}),
|
|
);
|
|
});
|
|
});
|