339 lines
15 KiB
TypeScript
339 lines
15 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");
|
|
});
|
|
|
|
test("버튼 블록을 추가하고 라벨과 링크를 수정할 수 있어야 한다", async ({ page }) => {
|
|
await page.goto("/editor");
|
|
|
|
const canvas = page.getByTestId("editor-canvas");
|
|
|
|
// 버튼 블록을 추가한다.
|
|
await page.getByRole("button", { name: "버튼 블록 추가" }).click();
|
|
|
|
// 캔버스에 기본 버튼이 렌더되어야 한다.
|
|
const button = canvas.getByRole("button", { name: "버튼" });
|
|
await expect(button).toBeVisible();
|
|
|
|
// 속성 패널에서 버튼 텍스트와 링크를 수정한다.
|
|
const labelInput = page.getByRole("textbox", { name: "버튼 텍스트" });
|
|
const hrefInput = page.getByRole("textbox", { name: "버튼 링크" });
|
|
|
|
await labelInput.fill("자세히 보기");
|
|
await hrefInput.fill("https://example.com");
|
|
|
|
// 캔버스의 버튼 텍스트가 변경되어야 한다.
|
|
const updatedButton = canvas.getByRole("button", { name: "자세히 보기" });
|
|
await expect(updatedButton).toBeVisible();
|
|
});
|
|
|
|
test("2컬럼 섹션에서 블록을 다른 컬럼 영역으로 드래그하면 컬럼이 변경되어야 한다", async ({ page }) => {
|
|
await page.goto("/editor");
|
|
|
|
const canvas = page.getByTestId("editor-canvas");
|
|
|
|
// 섹션을 하나 추가하고 레이아웃을 2컬럼으로 변경한다.
|
|
await page.getByRole("button", { name: "섹션 블록 추가" }).click();
|
|
|
|
// 섹션을 선택한다 (캔버스 첫 블록).
|
|
const sectionBlock = canvas.getByTestId("editor-block").nth(0);
|
|
await sectionBlock.click();
|
|
|
|
const layoutSelect = page.getByRole("combobox", { name: "섹션 레이아웃" });
|
|
await layoutSelect.selectOption("2col");
|
|
|
|
// 왼쪽 컬럼에 텍스트 블록을 하나 추가한다.
|
|
await page.getByRole("button", { name: "텍스트 블록 추가" }).click();
|
|
|
|
const leftColumn = canvas.locator('[data-section-id][data-column-id]').nth(0);
|
|
const rightColumn = canvas.locator('[data-section-id][data-column-id]').nth(1);
|
|
|
|
// 왼쪽 컬럼 안에 텍스트 블록이 존재하는지 확인한다.
|
|
const leftBlocks = leftColumn.getByTestId("editor-block");
|
|
await expect(leftBlocks).toHaveCount(1);
|
|
|
|
// 왼쪽 컬럼의 첫 블록을 오른쪽 컬럼 droppable 영역으로 드래그한다.
|
|
const leftBlock = leftBlocks.nth(0);
|
|
const handle = leftBlock.getByRole("button", { name: "블록 드래그 핸들" });
|
|
await handle.dragTo(rightColumn);
|
|
|
|
// 드래그 이후, 오른쪽 컬럼 안에 블록이 존재해야 하고 왼쪽 컬럼은 비어 있어야 한다.
|
|
const rightBlocks = rightColumn.getByTestId("editor-block");
|
|
await expect(rightBlocks).toHaveCount(1);
|
|
|
|
// 왼쪽 컬럼에는 더 이상 블록이 없어야 한다.
|
|
await expect(leftColumn.getByTestId("editor-block")).toHaveCount(0);
|
|
});
|
|
|
|
test("Hero 템플릿 버튼을 클릭하면 섹션과 기본 텍스트/버튼 블록이 캔버스에 생성되어야 한다", async ({ page }) => {
|
|
await page.goto("/editor");
|
|
|
|
const canvas = page.getByTestId("editor-canvas");
|
|
|
|
// Hero 템플릿을 추가한다.
|
|
await page.getByRole("button", { name: "Hero 템플릿 추가" }).click();
|
|
|
|
// 섹션/블록이 하나 이상 존재해야 한다.
|
|
const blocks = canvas.getByTestId("editor-block");
|
|
await expect(blocks.first()).toBeVisible();
|
|
|
|
// Hero 헤드라인 텍스트가 포함된 블록이 보여야 한다.
|
|
await expect(canvas.getByText("Hero 제목을 여기에 입력하세요")).toBeVisible();
|
|
|
|
// CTA 버튼도 렌더되어 있어야 한다.
|
|
const ctaButton = canvas.getByRole("button", { name: "지금 시작하기" });
|
|
await expect(ctaButton).toBeVisible();
|
|
});
|
|
|
|
test("Features 템플릿 버튼을 클릭하면 3개의 Feature 항목(제목/설명)이 생성되어야 한다", async ({ page }) => {
|
|
await page.goto("/editor");
|
|
|
|
const canvas = page.getByTestId("editor-canvas");
|
|
|
|
await page.getByRole("button", { name: "Features 템플릿 추가" }).click();
|
|
|
|
// Feature 제목들이 렌더되어야 한다.
|
|
await expect(canvas.getByText("Feature 1 제목")).toBeVisible();
|
|
await expect(canvas.getByText("Feature 2 제목")).toBeVisible();
|
|
await expect(canvas.getByText("Feature 3 제목")).toBeVisible();
|
|
});
|
|
|
|
test("CTA 템플릿 버튼을 클릭하면 CTA 텍스트와 버튼이 생성되어야 한다", async ({ page }) => {
|
|
await page.goto("/editor");
|
|
|
|
const canvas = page.getByTestId("editor-canvas");
|
|
|
|
await page.getByRole("button", { name: "CTA 템플릿 추가" }).click();
|
|
|
|
await expect(canvas.getByText("지금 바로 행동을 유도하는 CTA 텍스트를 입력하세요.")).toBeVisible();
|
|
const ctaButton = canvas.getByRole("button", { name: "CTA 버튼" });
|
|
await expect(ctaButton).toBeVisible();
|
|
});
|