Files
page-builder/tests/unit/EditorAutosave.spec.tsx
T
jaybe 17bfac62ed
CI / test (push) Failing after 7m39s
CI / pr_and_merge (push) Has been skipped
프로젝트 저장 및 목록
2025-11-29 22:05:25 +09:00

143 lines
4.2 KiB
TypeScript

import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
import { render, cleanup } from "@testing-library/react";
import EditorPage from "@/app/editor/page";
import type { Block, ProjectConfig } from "@/features/editor/state/editorStore";
// EditorPage 자동저장 TDD
// - blocks + projectConfig 를 localStorage 의 pb:autosave:<slug> 키로 자동 저장해야 한다.
// - localStorage 에 autosave 스냅샷이 있으면 초기 렌더에서 replaceBlocks + updateProjectConfig 로 복원해야 한다.
let mockState: any;
beforeEach(() => {
const baseProjectConfig: ProjectConfig = {
title: "자동저장 테스트 페이지",
slug: "autosave-test",
canvasPreset: "full",
} as ProjectConfig;
mockState = {
blocks: [
{
id: "blk_1",
type: "text",
props: {
text: "자동저장 테스트 블록",
align: "left",
size: "base",
},
},
] as Block[],
projectConfig: baseProjectConfig,
selectedBlockId: null as string | null,
selectedListItemId: null as string | null,
// EditorPage 가 참조하는 액션들: 기본적으로 no-op mock 으로 채운다.
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(),
updateProjectConfig: vi.fn(),
};
});
afterEach(() => {
cleanup();
vi.restoreAllMocks();
});
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>,
};
});
describe("EditorPage - 자동저장", () => {
it("blocks 와 projectConfig 를 localStorage 의 pb:autosave:<slug> 키로 자동 저장해야 한다", () => {
const storageProto = Object.getPrototypeOf(window.localStorage);
const setItemSpy = vi.spyOn(storageProto, "setItem");
render(<EditorPage />);
expect(setItemSpy).toHaveBeenCalled();
const [key, value] = setItemSpy.mock.calls[0] as [string, string];
expect(key).toBe("pb:autosave:autosave-test");
const parsed = JSON.parse(value);
expect(Array.isArray(parsed.blocks)).toBe(true);
expect(parsed.blocks[0].id).toBe("blk_1");
expect(parsed.projectConfig.slug).toBe("autosave-test");
});
it("localStorage 에 autosave 스냅샷이 있으면 초기 렌더에서 replaceBlocks 와 updateProjectConfig 로 복원해야 한다", () => {
const savedBlocks: Block[] = [
{
id: "saved_1",
type: "text",
props: {
text: "저장된 블록",
align: "left",
size: "base",
},
} as any,
];
const savedConfig: ProjectConfig = {
title: "저장된 프로젝트",
slug: "autosave-test",
canvasPreset: "full",
} as ProjectConfig;
const payload = {
blocks: savedBlocks,
projectConfig: savedConfig,
};
window.localStorage.setItem("pb:autosave:autosave-test", JSON.stringify(payload));
render(<EditorPage />);
expect(mockState.replaceBlocks).toHaveBeenCalledTimes(1);
expect(mockState.replaceBlocks).toHaveBeenCalledWith(savedBlocks);
expect(mockState.updateProjectConfig).toHaveBeenCalledTimes(1);
expect(mockState.updateProjectConfig).toHaveBeenCalledWith(savedConfig);
});
});