리팩터링
CI / test (push) Successful in 5m38s
CI / e2e (push) Has been skipped
CI / pr_and_merge (push) Successful in 1m12s

This commit is contained in:
2025-12-16 17:49:25 +09:00
parent 87cdc1868b
commit 7491dacba2
19 changed files with 1857 additions and 99 deletions
+61 -9
View File
@@ -166,8 +166,13 @@ describe("EditorPage - 프로젝트 저장/불러오기", () => {
const parsed = JSON.parse(raw as string);
expect(parsed.title).toBe("저장 테스트 프로젝트");
expect(Array.isArray(parsed.contentJson)).toBe(true);
expect(parsed.contentJson[0].id).toBe("blk_1");
// contentJson 은 이제 { blocks, projectConfig } 스냅샷 객체로 저장된다.
expect(parsed.contentJson).toBeTruthy();
expect(Array.isArray(parsed.contentJson.blocks)).toBe(true);
expect(parsed.contentJson.blocks[0].id).toBe("blk_1");
expect(parsed.contentJson.projectConfig).toBeTruthy();
expect(parsed.contentJson.projectConfig.title).toBe("저장 테스트 프로젝트");
expect(parsed.contentJson.projectConfig.slug).toBe("save-test-slug");
const projectCall = fetchMock.mock.calls.find(
([url, options]: any[]) => url === "/api/projects" && options?.method === "POST",
@@ -181,8 +186,13 @@ describe("EditorPage - 프로젝트 저장/불러오기", () => {
const body = JSON.parse(options.body);
expect(body.slug).toBe("save-test-slug");
expect(body.title).toBe("저장 테스트 프로젝트");
expect(Array.isArray(body.contentJson)).toBe(true);
expect(body.contentJson[0].id).toBe("blk_1");
// 서버로 전송되는 contentJson 도 { blocks, projectConfig } 스냅샷이어야 한다.
expect(body.contentJson).toBeTruthy();
expect(Array.isArray(body.contentJson.blocks)).toBe(true);
expect(body.contentJson.blocks[0].id).toBe("blk_1");
expect(body.contentJson.projectConfig).toBeTruthy();
expect(body.contentJson.projectConfig.title).toBe("저장 테스트 프로젝트");
expect(body.contentJson.projectConfig.slug).toBe("save-test-slug");
const message = await screen.findByText("Project saved: save-test-slug");
expect(message).toBeTruthy();
@@ -257,7 +267,15 @@ describe("EditorPage - 프로젝트 저장/불러오기", () => {
localKey,
JSON.stringify({
title: "로컬 프로젝트",
contentJson: savedBlocks,
// 새 스펙: contentJson 은 blocks 와 projectConfig 를 함께 가지는 스냅샷 객체다.
contentJson: {
blocks: savedBlocks,
projectConfig: {
title: "로컬 프로젝트",
slug,
canvasPreset: "full",
} as ProjectConfig,
},
}),
);
@@ -319,7 +337,19 @@ describe("EditorPage - 프로젝트 저장/불러오기", () => {
const fetchMock = vi.fn().mockResolvedValue({
ok: true,
status: 200,
json: async () => ({ slug, title: "서버 프로젝트", contentJson: serverBlocks }),
// 서버에서도 contentJson 은 { blocks, projectConfig } 형태로 반환된다고 가정한다.
json: async () => ({
slug,
title: "서버 프로젝트",
contentJson: {
blocks: serverBlocks,
projectConfig: {
title: "서버 프로젝트",
slug,
canvasPreset: "full",
} as ProjectConfig,
},
}),
} as any);
vi.stubGlobal("fetch", fetchMock);
@@ -366,7 +396,7 @@ describe("EditorPage - 프로젝트 저장/불러오기", () => {
expect(message).toBeTruthy();
});
it("서버에서 복잡한 프로젝트 contentJson 을 불러오면 모든 블록 속성과 스타일이 그대로 복원되어야 한다", async () => {
it("서버에서 복잡한 프로젝트 contentJson 스냅샷을 불러오면 모든 블록 속성과 스타일이 그대로 복원되어야 한다", async () => {
const slug = "roundtrip-slug";
const complexBlocks: Block[] = [
@@ -454,7 +484,18 @@ describe("EditorPage - 프로젝트 저장/불러오기", () => {
if (url === `/api/projects/${slug}` && method === "GET") {
return Promise.resolve(
new Response(
JSON.stringify({ slug, title: "복잡한 프로젝트", contentJson: complexBlocks }),
JSON.stringify({
slug,
title: "복잡한 프로젝트",
contentJson: {
blocks: complexBlocks,
projectConfig: {
title: "복잡한 프로젝트",
slug,
canvasPreset: "full",
} as ProjectConfig,
},
}),
{
status: 200,
headers: { "Content-Type": "application/json" },
@@ -576,7 +617,18 @@ describe("EditorPage - 프로젝트 저장/불러오기", () => {
if (url === `/api/projects/${slug}` && method === "GET") {
return Promise.resolve(
new Response(
JSON.stringify({ slug, title: "쿼리 로드 프로젝트", contentJson: serverBlocks }),
JSON.stringify({
slug,
title: "쿼리 로드 프로젝트",
contentJson: {
blocks: serverBlocks,
projectConfig: {
title: "쿼리 로드 프로젝트",
slug,
canvasPreset: "full",
} as ProjectConfig,
},
}),
{
status: 200,
headers: { "Content-Type": "application/json" },