Files
page-builder/tests/unit/EditorMultiSelect.spec.tsx
jaybe 0cf67b5619
CI / test (push) Failing after 7m55s
CI / pr_and_merge (push) Has been skipped
리스트/에디터 스타일 정리 및 버그 수정
2025-11-30 16:52:27 +09:00

154 lines
4.2 KiB
TypeScript

import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
import { render, screen, cleanup, fireEvent } from "@testing-library/react";
import type { Block, ProjectConfig } from "@/features/editor/state/editorStore";
import EditorPage from "@/app/editor/page";
let mockState: any;
beforeEach(() => {
window.localStorage.clear();
const baseProjectConfig: ProjectConfig = {
title: "멀티 선택 테스트",
slug: "multi-select",
canvasPreset: "full",
canvasWidthPx: 1024,
canvasBgColorHex: "#0f172a",
bodyBgColorHex: "#020617",
} as ProjectConfig;
mockState = {
blocks: [] as Block[],
projectConfig: baseProjectConfig,
selectedBlockId: null as string | null,
selectedListItemId: null as string | null,
undo: vi.fn(),
redo: vi.fn(),
removeBlock: vi.fn(),
duplicateBlock: vi.fn(),
selectBlock: vi.fn(),
selectListItem: vi.fn(),
replaceBlocks: vi.fn(),
reorderBlocks: vi.fn(),
moveBlock: vi.fn(),
addTextBlock: vi.fn(),
addButtonBlock: vi.fn(),
addImageBlock: vi.fn(),
addDividerBlock: vi.fn(),
addListBlock: vi.fn(),
addSectionBlock: vi.fn(),
addFormBlock: vi.fn(),
addFormInputBlock: vi.fn(),
addFormSelectBlock: vi.fn(),
addFormCheckboxBlock: vi.fn(),
addFormRadioBlock: vi.fn(),
addHeroTemplateSection: vi.fn(),
addFeaturesTemplateSection: vi.fn(),
addCtaTemplateSection: vi.fn(),
addFaqTemplateSection: vi.fn(),
addPricingTemplateSection: vi.fn(),
addTestimonialsTemplateSection: vi.fn(),
addBlogTemplateSection: vi.fn(),
addTeamTemplateSection: vi.fn(),
addFooterTemplateSection: vi.fn(),
updateBlock: vi.fn(),
};
});
afterEach(() => {
cleanup();
});
vi.mock("@/features/editor/state/editorStore", () => {
const useEditorStore = (selector: (state: any) => any) => selector(mockState);
(useEditorStore as any).getState = () => mockState;
return {
__esModule: true,
useEditorStore,
};
});
vi.mock("next/link", () => {
return {
__esModule: true,
default: ({ href, children }: any) => <a href={href}>{children}</a>,
};
});
function setupThreeBlocks() {
const blocks: Block[] = [
{
id: "blk_1",
type: "text",
props: {
text: "블록 1",
align: "left",
size: "base",
},
} as any,
{
id: "blk_2",
type: "text",
props: {
text: "블록 2",
align: "left",
size: "base",
},
} as any,
{
id: "blk_3",
type: "text",
props: {
text: "블록 3",
align: "left",
size: "base",
},
} as any,
];
mockState.blocks = blocks;
mockState.selectedBlockId = null;
}
describe("EditorPage - 멀티 선택", () => {
it("Cmd/Ctrl+클릭으로 여러 블록을 동시에 선택할 수 있어야 한다", () => {
setupThreeBlocks();
render(<EditorPage />);
const blocksEls = screen.getAllByTestId("editor-block") as HTMLElement[];
// 첫 번째와 두 번째 블록을 Cmd/Ctrl+클릭
fireEvent.click(blocksEls[0], { metaKey: true });
fireEvent.click(blocksEls[1], { metaKey: true });
const selectedEls = blocksEls.filter((el) => el.dataset.selected === "true");
expect(selectedEls.length).toBe(2);
const selectedIds = selectedEls.map((el) => el.getAttribute("data-block-id") ?? "");
expect(new Set(selectedIds)).toEqual(new Set(["blk_1", "blk_2"]));
});
it("멀티 선택 상태에서 Backspace 로 선택된 모든 블록이 삭제되어야 한다", () => {
setupThreeBlocks();
render(<EditorPage />);
const blocksEls = screen.getAllByTestId("editor-block") as HTMLElement[];
// 블록 1, 2 를 멀티 선택
fireEvent.click(blocksEls[0], { metaKey: true });
fireEvent.click(blocksEls[1], { metaKey: true });
// Backspace 로 삭제
const event = new KeyboardEvent("keydown", { key: "Backspace" });
window.dispatchEvent(event);
// replaceBlocks 가 한 번 호출되고, 남은 블록은 blk_3 만 있어야 한다.
expect(mockState.replaceBlocks).toHaveBeenCalledTimes(1);
const arg = mockState.replaceBlocks.mock.calls[0][0] as Block[];
expect(arg.length).toBe(1);
expect(arg[0].id).toBe("blk_3");
});
});