232 lines
10 KiB
TypeScript
232 lines
10 KiB
TypeScript
import { test, expect } from "@playwright/test";
|
|
|
|
// 최초에는 실패하는 테스트: /editor 페이지와 헤더 텍스트가 아직 없음
|
|
|
|
test("/editor 페이지가 존재하고 Page Editor 헤더를 보여줘야 한다", async ({ page }) => {
|
|
await page.goto("/editor");
|
|
|
|
await expect(page.getByRole("heading", { name: "Page Editor" })).toBeVisible();
|
|
});
|
|
|
|
test("텍스트 블록 추가 버튼을 누르면 캔버스에 '새 텍스트' 블록이 보여야 한다", async ({ page }) => {
|
|
await page.goto("/editor");
|
|
|
|
await page.getByRole("button", { name: "텍스트 블록 추가" }).click();
|
|
|
|
const canvas = page.getByTestId("editor-canvas");
|
|
await expect(canvas.getByText("새 텍스트")).toBeVisible();
|
|
});
|
|
|
|
test("텍스트 블록을 더블클릭해서 내용을 수정할 수 있어야 한다", async ({ page }) => {
|
|
await page.goto("/editor");
|
|
|
|
// 텍스트 블록을 하나 추가한다.
|
|
await page.getByRole("button", { name: "텍스트 블록 추가" }).click();
|
|
|
|
// 기존 텍스트 블록을 더블클릭해서 편집 모드로 전환한다.
|
|
const canvas = page.getByTestId("editor-canvas");
|
|
const block = canvas.getByText("새 텍스트");
|
|
await block.dblclick();
|
|
|
|
// 편집 모드에서 텍스트를 변경한다.
|
|
const editor = page.getByRole("textbox", { name: "텍스트 블록 내용 편집" });
|
|
await editor.fill("수정된 텍스트");
|
|
await editor.press("Enter");
|
|
|
|
// 변경된 텍스트가 캔버스에 반영되어야 한다.
|
|
const canvasAfterEdit = page.getByTestId("editor-canvas");
|
|
await expect(canvasAfterEdit.getByText("수정된 텍스트")).toBeVisible();
|
|
});
|
|
|
|
test("속성 패널에서 선택된 텍스트 블록 내용을 수정하면 캔버스에 반영되어야 한다", async ({ page }) => {
|
|
await page.goto("/editor");
|
|
|
|
// 텍스트 블록을 추가한다.
|
|
await page.getByRole("button", { name: "텍스트 블록 추가" }).click();
|
|
|
|
// 캔버스 블록을 클릭해서 선택한다.
|
|
const canvas = page.getByTestId("editor-canvas");
|
|
await canvas.getByText("새 텍스트").click();
|
|
|
|
// 우측 속성 패널에서 텍스트를 수정한다.
|
|
const sidebarEditor = page.getByRole("textbox", { name: "선택한 텍스트 블록 내용" });
|
|
await sidebarEditor.fill("사이드바에서 수정된 텍스트");
|
|
|
|
// 캔버스에 수정된 텍스트가 보여야 한다.
|
|
const canvasAfterSidebarEdit = page.getByTestId("editor-canvas");
|
|
await expect(canvasAfterSidebarEdit.getByText("사이드바에서 수정된 텍스트")).toBeVisible();
|
|
});
|
|
|
|
test("여러 텍스트 블록 중 선택을 전환하면 속성 패널 내용과 선택 하이라이트가 바뀌어야 한다", async ({ page }) => {
|
|
await page.goto("/editor");
|
|
|
|
// 텍스트 블록 두 개를 추가한다.
|
|
await page.getByRole("button", { name: "텍스트 블록 추가" }).click();
|
|
await page.getByRole("button", { name: "텍스트 블록 추가" }).click();
|
|
|
|
const canvas = page.getByTestId("editor-canvas");
|
|
const blocks = canvas.getByTestId("editor-block");
|
|
const firstBlock = blocks.nth(0);
|
|
const secondBlock = blocks.nth(1);
|
|
|
|
// 첫 번째 블록을 선택하고, 속성 패널에서 텍스트를 변경한다.
|
|
await firstBlock.click();
|
|
const sidebarEditor = page.getByRole("textbox", { name: "선택한 텍스트 블록 내용" });
|
|
await sidebarEditor.fill("첫 번째 블록");
|
|
|
|
// 두 번째 블록을 선택하고, 다시 속성 패널에서 텍스트를 변경한다.
|
|
await secondBlock.click();
|
|
await sidebarEditor.fill("두 번째 블록");
|
|
|
|
// 캔버스에는 두 블록의 텍스트가 모두 보여야 한다.
|
|
await expect(canvas.getByText("첫 번째 블록")).toBeVisible();
|
|
await expect(canvas.getByText("두 번째 블록")).toBeVisible();
|
|
|
|
// 현재 선택된 블록은 두 번째 블록이어야 한다.
|
|
await expect(firstBlock).toHaveAttribute("data-selected", "false");
|
|
await expect(secondBlock).toHaveAttribute("data-selected", "true");
|
|
|
|
// 속성 패널 textarea에도 두 번째 블록 텍스트가 표시되어야 한다.
|
|
await expect(sidebarEditor).toHaveValue("두 번째 블록");
|
|
});
|
|
|
|
test("텍스트 블록의 정렬과 글자 크기를 속성 패널에서 변경하면 캔버스 스타일이 바뀌어야 한다", async ({ page }) => {
|
|
await page.goto("/editor");
|
|
|
|
// 텍스트 블록을 하나 추가하고 선택한다.
|
|
await page.getByRole("button", { name: "텍스트 블록 추가" }).click();
|
|
const canvas = page.getByTestId("editor-canvas");
|
|
const block = canvas.getByTestId("editor-block").nth(0);
|
|
await block.click();
|
|
|
|
// 속성 패널에서 정렬을 가운데로 변경한다.
|
|
const alignSelect = page.getByRole("combobox", { name: "정렬" });
|
|
await alignSelect.selectOption("center");
|
|
|
|
// 속성 패널에서 글자 크기를 크게로 변경한다.
|
|
const sizeSelect = page.getByRole("combobox", { name: "글자 크기" });
|
|
await sizeSelect.selectOption("lg");
|
|
|
|
// 캔버스 블록에 text-center와 text-lg 클래스가 적용되어야 한다.
|
|
await expect(block).toHaveClass(/text-center/);
|
|
await expect(block).toHaveClass(/text-lg/);
|
|
});
|
|
|
|
test("에디터 상태를 JSON으로 내보내고 다시 불러오면 동일한 블록 상태가 복원되어야 한다", async ({ page }) => {
|
|
await page.goto("/editor");
|
|
|
|
const canvas = page.getByTestId("editor-canvas");
|
|
|
|
// 블록 두 개를 추가하고 각각 다른 내용/스타일을 설정한다.
|
|
await page.getByRole("button", { name: "텍스트 블록 추가" }).click();
|
|
await page.getByRole("button", { name: "텍스트 블록 추가" }).click();
|
|
|
|
const blocks = canvas.getByTestId("editor-block");
|
|
const firstBlock = blocks.nth(0);
|
|
const secondBlock = blocks.nth(1);
|
|
|
|
// 첫 번째 블록: 텍스트/정렬/크기 설정
|
|
await firstBlock.click();
|
|
const sidebarEditor = page.getByRole("textbox", { name: "선택한 텍스트 블록 내용" });
|
|
await sidebarEditor.fill("첫 번째 JSON 블록");
|
|
const alignSelect = page.getByRole("combobox", { name: "정렬" });
|
|
await alignSelect.selectOption("left");
|
|
const sizeSelect = page.getByRole("combobox", { name: "글자 크기" });
|
|
await sizeSelect.selectOption("sm");
|
|
|
|
// 두 번째 블록: 텍스트/정렬/크기 설정
|
|
await secondBlock.click();
|
|
await sidebarEditor.fill("두 번째 JSON 블록");
|
|
await alignSelect.selectOption("right");
|
|
await sizeSelect.selectOption("lg");
|
|
|
|
// 현재 상태를 JSON으로 내보낸다.
|
|
await page.getByRole("button", { name: "JSON 내보내기" }).click();
|
|
const exportArea = page.getByRole("textbox", { name: "에디터 상태 JSON" });
|
|
const exportedJson = await exportArea.inputValue();
|
|
|
|
// 캔버스를 초기화한다.
|
|
await page.getByRole("button", { name: "캔버스 초기화" }).click();
|
|
await expect(canvas.getByTestId("editor-block")).toHaveCount(0);
|
|
|
|
// JSON을 다시 입력하고 불러온다.
|
|
const importArea = page.getByRole("textbox", { name: "JSON 불러오기" });
|
|
await importArea.fill(exportedJson);
|
|
await page.getByRole("button", { name: "JSON 적용" }).click();
|
|
|
|
// 복원된 캔버스에 두 블록이 모두 존재해야 한다.
|
|
const restoredBlocks = canvas.getByTestId("editor-block");
|
|
await expect(restoredBlocks).toHaveCount(2);
|
|
|
|
// 각 블록의 텍스트와 스타일이 원상 복구되었는지 확인한다.
|
|
const restoredFirst = restoredBlocks.nth(0);
|
|
const restoredSecond = restoredBlocks.nth(1);
|
|
|
|
await expect(restoredFirst).toContainText("첫 번째 JSON 블록");
|
|
await expect(restoredFirst).toHaveClass(/text-left/);
|
|
await expect(restoredFirst).toHaveClass(/text-sm/);
|
|
|
|
await expect(restoredSecond).toContainText("두 번째 JSON 블록");
|
|
await expect(restoredSecond).toHaveClass(/text-right/);
|
|
await expect(restoredSecond).toHaveClass(/text-lg/);
|
|
});
|
|
|
|
test("각 캔버스 블록에 드래그 핸들 UI가 표시되고 선택 상태가 aria-selected로 노출되어야 한다", async ({ page }) => {
|
|
await page.goto("/editor");
|
|
|
|
const canvas = page.getByTestId("editor-canvas");
|
|
|
|
// 블록 두 개를 추가한다.
|
|
await page.getByRole("button", { name: "텍스트 블록 추가" }).click();
|
|
await page.getByRole("button", { name: "텍스트 블록 추가" }).click();
|
|
|
|
const blocks = canvas.getByTestId("editor-block");
|
|
const firstBlock = blocks.nth(0);
|
|
const secondBlock = blocks.nth(1);
|
|
|
|
// 각 블록 안에 드래그 핸들이 존재해야 한다.
|
|
await expect(firstBlock.getByRole("button", { name: "블록 드래그 핸들" })).toBeVisible();
|
|
await expect(secondBlock.getByRole("button", { name: "블록 드래그 핸들" })).toBeVisible();
|
|
|
|
// 초기에는 마지막으로 추가된 두 번째 블록이 선택되어 있어야 한다.
|
|
await expect(firstBlock).toHaveAttribute("aria-selected", "false");
|
|
await expect(secondBlock).toHaveAttribute("aria-selected", "true");
|
|
|
|
// 첫 번째 블록을 클릭하면 선택 상태가 전환되어야 한다.
|
|
await firstBlock.click();
|
|
await expect(firstBlock).toHaveAttribute("aria-selected", "true");
|
|
await expect(secondBlock).toHaveAttribute("aria-selected", "false");
|
|
});
|
|
|
|
test("블록 드래그앤드롭으로 순서를 변경할 수 있어야 한다", async ({ page }) => {
|
|
await page.goto("/editor");
|
|
|
|
const canvas = page.getByTestId("editor-canvas");
|
|
|
|
// 블록 두 개를 추가하고 서로 다른 텍스트로 구분한다.
|
|
await page.getByRole("button", { name: "텍스트 블록 추가" }).click();
|
|
await page.getByRole("button", { name: "텍스트 블록 추가" }).click();
|
|
|
|
const blocks = canvas.getByTestId("editor-block");
|
|
const firstBlock = blocks.nth(0);
|
|
const secondBlock = blocks.nth(1);
|
|
|
|
// 텍스트를 A/B로 설정해서 순서를 식별한다.
|
|
await firstBlock.click();
|
|
const sidebarEditor = page.getByRole("textbox", { name: "선택한 텍스트 블록 내용" });
|
|
await sidebarEditor.fill("블록 A");
|
|
|
|
await secondBlock.click();
|
|
await sidebarEditor.fill("블록 B");
|
|
|
|
// 두 번째 블록의 드래그 핸들을 첫 번째 블록 위치로 드래그앤드롭 한다.
|
|
const secondHandle = secondBlock.getByRole("button", { name: "블록 드래그 핸들" });
|
|
const firstHandle = firstBlock.getByRole("button", { name: "블록 드래그 핸들" });
|
|
await secondHandle.dragTo(firstHandle);
|
|
|
|
// 드래그 후에는 순서가 [블록 B, 블록 A]가 되어야 한다.
|
|
const reorderedBlocks = canvas.getByTestId("editor-block");
|
|
await expect(reorderedBlocks.nth(0)).toContainText("블록 B");
|
|
await expect(reorderedBlocks.nth(1)).toContainText("블록 A");
|
|
});
|