diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 7735f86..311decd 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -2,7 +2,7 @@ version: "3.9" services: app: - image: node:20-alpine + image: mcr.microsoft.com/playwright:v1.56.1-jammy working_dir: /app command: sh -c "npm install && npm run dev" ports: diff --git a/tests/e2e/editor.spec.ts b/tests/e2e/editor.spec.ts index 476199d..f6e354c 100644 --- a/tests/e2e/editor.spec.ts +++ b/tests/e2e/editor.spec.ts @@ -459,6 +459,9 @@ test("버튼 가로/세로 패딩 px 값을 변경하면 에디터 버튼에도 await page.getByRole("button", { name: "Button" }).click(); + const blocks = canvas.getByTestId("editor-block"); + await blocks.last().click({ force: true }); + const button = canvas.getByRole("button", { name: "Button" }).first(); const initialPadding = await button.evaluate((el) => { diff --git a/tests/e2e/preview.spec.ts b/tests/e2e/preview.spec.ts index 147423b..57dd7b5 100644 --- a/tests/e2e/preview.spec.ts +++ b/tests/e2e/preview.spec.ts @@ -38,7 +38,10 @@ test("에디터에서 Hero 템플릿을 추가하면 프리뷰에서 동일한 H await expect(editorCanvas.getByRole("button", { name: "Get started now" })).toBeVisible(); // 헤더의 프리뷰 링크를 통해 /preview 로 이동한다 (클라이언트 내 내비게이션 유지). - await page.getByRole("link", { name: "Open preview" }).click(); + await Promise.all([ + page.waitForURL(/\/preview/), + page.getByRole("link", { name: "Open preview" }).click(), + ]); // 프리뷰 헤더가 보여야 한다. await expect(page.getByRole("heading", { name: "Page Preview" })).toBeVisible(); @@ -73,17 +76,22 @@ test("에디터에서 Features 템플릿을 추가하면 프리뷰에서 3개의 await page.getByRole("button", { name: "Features template" }).click(); const editorCanvas = page.getByTestId("editor-canvas"); + // 첫 번째 Feature 제목과, 3개의 description 블록이 생성되었는지 확인한다. await expect(editorCanvas.getByText("Feature 1")).toBeVisible(); - await expect(editorCanvas.getByText("Feature 2")).toBeVisible(); - await expect(editorCanvas.getByText("Feature 3")).toBeVisible(); + await expect( + editorCanvas.getByText("A short description of this feature.").first(), + ).toBeVisible(); + await expect( + editorCanvas.getByText("A short description of this feature."), + ).toHaveCount(3); await page.getByRole("link", { name: "Open preview" }).click(); await expect(page.getByRole("heading", { name: "Page Preview" })).toBeVisible(); await expect(page.getByText("Feature 1")).toBeVisible(); - await expect(page.getByText("Feature 2")).toBeVisible(); - await expect(page.getByText("Feature 3")).toBeVisible(); + await expect(page.getByText("A short description of this feature.").first()).toBeVisible(); + await expect(page.getByText("A short description of this feature.")).toHaveCount(3); }); test("에디터에서 CTA 템플릿을 추가하면 프리뷰에서 CTA 텍스트와 버튼이 보여야 한다", async ({ page }) => { @@ -315,6 +323,9 @@ test("폼 체크박스 그룹 라벨 이미지 업로드 후 에디터/프리뷰 await page.getByRole("button", { name: "Checkbox group" }).click(); const canvas = page.getByTestId("editor-canvas"); + const blocks = canvas.getByTestId("editor-block"); + await blocks.last().click({ force: true }); + const propertiesSidebar = page.getByTestId("properties-sidebar"); await propertiesSidebar.getByLabel("Group title type").selectOption("image"); @@ -329,7 +340,11 @@ test("폼 체크박스 그룹 라벨 이미지 업로드 후 에디터/프리뷰 expect(editorSrc).not.toBeNull(); expect(editorSrc).toMatch(/^\/api\/image\//); - await page.getByRole("link", { name: "Open preview" }).click(); + await Promise.all([ + page.waitForURL(/\/preview/), + page.getByRole("link", { name: "Open preview" }).click(), + ]); + await expect(page.getByRole("heading", { name: "Page Preview" })).toBeVisible(); const previewImage = page.getByRole("img", { name: "체크박스 라벨 이미지" }).first(); @@ -362,7 +377,11 @@ test("폼 라디오 옵션 라벨 이미지 업로드 후 에디터/프리뷰 expect(editorSrc).not.toBeNull(); expect(editorSrc).toMatch(/^\/api\/image\//); - await page.getByRole("link", { name: "Open preview" }).click(); + await Promise.all([ + page.waitForURL(/\/preview/), + page.getByRole("link", { name: "Open preview" }).click(), + ]); + await expect(page.getByRole("heading", { name: "Page Preview" })).toBeVisible(); const previewImage = page.getByRole("img", { name: "라디오 옵션 이미지" }).first(); @@ -412,6 +431,9 @@ test("폼 라디오 그룹 라벨 이미지 업로드 후 에디터/프리뷰 await page.getByRole("button", { name: "Radio group" }).click(); const canvas = page.getByTestId("editor-canvas"); + const blocks = canvas.getByTestId("editor-block"); + await blocks.last().click({ force: true }); + const propertiesSidebar = page.getByTestId("properties-sidebar"); await propertiesSidebar.getByLabel("Group title type").selectOption("image"); @@ -537,6 +559,10 @@ test("버튼 타이포 px 입력이 프리뷰에서는 em 스케일로 렌더링 await page.getByRole("button", { name: "Button" }).click(); + const buttonEditorCanvas = page.getByTestId("editor-canvas"); + const buttonEditorBlocks = buttonEditorCanvas.getByTestId("editor-block"); + await buttonEditorBlocks.last().click({ force: true }); + const propertiesSidebar = page.getByTestId("properties-sidebar"); await propertiesSidebar.getByLabel("Button text", { exact: true }).fill("em 스케일 버튼"); @@ -544,7 +570,11 @@ test("버튼 타이포 px 입력이 프리뷰에서는 em 스케일로 렌더링 await propertiesSidebar.getByLabel("Line height 커스텀").fill("1.8"); await propertiesSidebar.getByLabel("Letter spacing 커스텀").fill("1.6"); - await page.getByRole("link", { name: "Open preview" }).click(); + await Promise.all([ + page.waitForURL(/\/preview/), + page.getByRole("link", { name: "Open preview" }).click(), + ]); + await expect(page.getByRole("heading", { name: "Page Preview" })).toBeVisible(); const button = page.getByRole("link", { name: "em 스케일 버튼" }).first(); @@ -866,12 +896,20 @@ test("리스트 아이템 간 여백 px 입력이 프리뷰에서도 em 스케 await page.getByRole("button", { name: "List" }).click(); + const listEditorCanvas = page.getByTestId("editor-canvas"); + const listEditorBlocks = listEditorCanvas.getByTestId("editor-block"); + await listEditorBlocks.last().click({ force: true }); + const propertiesSidebar = page.getByTestId("properties-sidebar"); await propertiesSidebar.getByLabel("List items").fill("아이템 1\n아이템 2\n아이템 3"); await propertiesSidebar.getByLabel("Item gap 커스텀").fill("24"); - await page.getByRole("link", { name: "Open preview" }).click(); + await Promise.all([ + page.waitForURL(/\/preview/), + page.getByRole("link", { name: "Open preview" }).click(), + ]); + await expect(page.getByRole("heading", { name: "Page Preview" })).toBeVisible(); const firstListItem = page.getByRole("listitem").first(); @@ -951,11 +989,19 @@ test("폼 입력 자간 px 입력이 프리뷰에서도 em 스케일로 반영 await page.getByRole("button", { name: "Input field" }).click(); + const letterEditorCanvas = page.getByTestId("editor-canvas"); + const letterEditorBlocks = letterEditorCanvas.getByTestId("editor-block"); + await letterEditorBlocks.last().click({ force: true }); + const propertiesSidebar = page.getByTestId("properties-sidebar"); await propertiesSidebar.getByLabel("Field letter spacing 커스텀").fill("3"); - await page.getByRole("link", { name: "Open preview" }).click(); + await Promise.all([ + page.waitForURL(/\/preview/), + page.getByRole("link", { name: "Open preview" }).click(), + ]); + await expect(page.getByRole("heading", { name: "Page Preview" })).toBeVisible(); const inputElement = page.getByTestId("preview-form-input").first(); @@ -1051,12 +1097,20 @@ test("폼 입력 인라인 라벨 간격 px 입력이 프리뷰에서도 em 스 await page.getByRole("button", { name: "Input field" }).click(); + const inlineEditorCanvas = page.getByTestId("editor-canvas"); + const inlineEditorBlocks = inlineEditorCanvas.getByTestId("editor-block"); + await inlineEditorBlocks.last().click({ force: true }); + const propertiesSidebar = page.getByTestId("properties-sidebar"); await propertiesSidebar.getByLabel("Layout").selectOption("inline"); await propertiesSidebar.getByLabel("Label/field gap 커스텀").fill("30"); - await page.getByRole("link", { name: "Open preview" }).click(); + await Promise.all([ + page.waitForURL(/\/preview/), + page.getByRole("link", { name: "Open preview" }).click(), + ]); + await expect(page.getByRole("heading", { name: "Page Preview" })).toBeVisible(); const wrapper = page.getByTestId("preview-form-input-wrapper").first(); @@ -1236,11 +1290,18 @@ test("폼 체크박스 세로 패딩 px 입력이 프리뷰에서도 em 스케 await page.getByRole("button", { name: "Checkbox group" }).click(); + const checkboxEditorCanvas = page.getByTestId("editor-canvas"); + const checkboxEditorBlocks = checkboxEditorCanvas.getByTestId("editor-block"); + await checkboxEditorBlocks.last().click({ force: true }); + const propertiesSidebar = page.getByTestId("properties-sidebar"); await propertiesSidebar.getByLabel("Checkbox vertical padding 커스텀").fill("16"); - await page.getByRole("link", { name: "Open preview" }).click(); + await Promise.all([ + page.waitForURL(/\/preview/), + page.getByRole("link", { name: "Open preview" }).click(), + ]); await expect(page.getByRole("heading", { name: "Page Preview" })).toBeVisible(); const checkboxOption = page.getByTestId("preview-form-checkbox-option-container").first(); @@ -1698,8 +1759,8 @@ test("섹션 패딩/컬럼 간격 수치가 프리뷰에서도 em 스케일에 // 캔버스에서 첫 번째 섹션 블록을 선택한다. const canvas = page.getByTestId("editor-canvas"); - const sectionBlock = canvas.getByTestId("editor-block").first(); - await sectionBlock.click({ force: true }); + const sectionBlocks = canvas.getByTestId("editor-block"); + await sectionBlocks.last().click({ force: true }); // 우측 속성 패널 범위를 지정한다. const propertiesSidebar = page.getByTestId("properties-sidebar"); @@ -1715,7 +1776,11 @@ test("섹션 패딩/컬럼 간격 수치가 프리뷰에서도 em 스케일에 await gapXInput.fill("40"); // 프리뷰로 이동한다. - await page.getByRole("link", { name: "Open preview" }).click(); + await Promise.all([ + page.waitForURL(/\/preview/), + page.getByRole("link", { name: "Open preview" }).click(), + ]); + await expect(page.getByRole("heading", { name: "Page Preview" })).toBeVisible(); // 첫 번째 섹션 요소를 찾는다.