i18n 적용
CI / test (push) Failing after 11m12s
CI / e2e (push) Has been cancelled
CI / pr_and_merge (push) Has been cancelled

This commit is contained in:
2025-12-10 15:56:51 +09:00
parent 73e9bc6a1c
commit f71207aeb5
127 changed files with 7346 additions and 2079 deletions
+8 -8
View File
@@ -16,7 +16,7 @@ test("비로그인 사용자가 /projects 에 접근하면 /login 으로 리다
await page.goto("/projects");
// 최종적으로 로그인 페이지의 헤더가 보여야 한다.
await expect(page.getByRole("heading", { name: "로그인" })).toBeVisible();
await expect(page.getByRole("heading", { name: "Log in" })).toBeVisible();
});
test("비로그인 사용자가 /editor 에 접근하면 /login 으로 리다이렉트되어야 한다", async ({ page }) => {
@@ -31,7 +31,7 @@ test("비로그인 사용자가 /editor 에 접근하면 /login 으로 리다이
await page.goto("/editor");
await expect(page.getByRole("heading", { name: "로그인" })).toBeVisible();
await expect(page.getByRole("heading", { name: "Log in" })).toBeVisible();
});
test("비로그인 사용자가 /preview 에 접근하면 /login 으로 리다이렉트되어야 한다", async ({ page }) => {
@@ -45,7 +45,7 @@ test("비로그인 사용자가 /preview 에 접근하면 /login 으로 리다
await page.goto("/preview");
await expect(page.getByRole("heading", { name: "로그인" })).toBeVisible();
await expect(page.getByRole("heading", { name: "Log in" })).toBeVisible();
});
test("회원가입 후에는 /dashboard, /editor, /preview 에 정상 접근할 수 있어야 한다", async ({ page }) => {
@@ -94,14 +94,14 @@ test("회원가입 후에는 /dashboard, /editor, /preview 에 정상 접근할
// 1) 회원가입
await page.goto("/signup");
await page.getByLabel("이메일").fill(email);
await page.getByLabel("비밀번호", { exact: true }).fill(password);
await page.getByLabel("비밀번호 확인").fill(password);
await page.getByRole("button", { name: "회원가입" }).click();
await page.getByLabel("Email").fill(email);
await page.getByLabel("Password", { exact: true }).fill(password);
await page.getByLabel("Confirm password").fill(password);
await page.getByRole("button", { name: "Sign up" }).click();
// /dashboard 로 이동했는지, 헤더가 보이는지 확인
await expect(page).toHaveURL(/\/dashboard/);
await expect(page.getByRole("heading", { name: "대시보드" })).toBeVisible();
await expect(page.getByRole("heading", { name: "Dashboard" })).toBeVisible();
// 2) /editor 접근 확인 – 로그인 페이지로 리다이렉트되면 안 된다.
await page.goto("/editor");
+8 -8
View File
@@ -16,7 +16,7 @@ test("비로그인 사용자가 /dashboard 에 접근하면 /login 으로 리다
await page.goto("/dashboard");
await expect(page.getByRole("heading", { name: "로그인" })).toBeVisible();
await expect(page.getByRole("heading", { name: "Log in" })).toBeVisible();
});
test("로그인 사용자가 /dashboard 에 접근하면 요약 지표와 프로젝트 카드 리스트, 일별 그래프를 볼 수 있어야 한다", async ({ page }) => {
@@ -61,19 +61,19 @@ test("로그인 사용자가 /dashboard 에 접근하면 요약 지표와 프로
await page.goto("/dashboard");
// 대시보드 헤더가 보여야 한다.
await expect(page.getByRole("heading", { name: "대시보드" })).toBeVisible();
await expect(page.getByRole("heading", { name: "Dashboard" })).toBeVisible();
// 상단 헤더 내비게이션이 보여야 한다.
const dashboardLink = page.getByRole("link", { name: "대시보드" });
const dashboardLink = page.getByRole("link", { name: "Dashboard" });
await expect(dashboardLink).toBeVisible();
const projectsLink = page.getByRole("link", { name: "프로젝트 목록" });
const projectsLink = page.getByRole("link", { name: "Projects" });
await expect(projectsLink).toBeVisible();
const submissionsLink = page.getByRole("link", { name: "전체 제출 내역" });
const submissionsLink = page.getByRole("link", { name: "All submissions" });
await expect(submissionsLink).toBeVisible();
const menuButton = page.getByRole("button", { name: "메뉴" });
const menuButton = page.getByRole("button", { name: "Menu" });
await expect(menuButton).toBeVisible();
// 상단 요약 위젯의 지표들이 기대 값으로 렌더되어야 한다.
@@ -112,7 +112,7 @@ test("로그인 사용자가 /dashboard 에 접근하면 요약 지표와 프로
// 메뉴 버튼을 클릭하면 로그아웃 항목이 보여야 한다.
await menuButton.click();
await expect(page.getByRole("button", { name: "로그아웃" })).toBeVisible();
await expect(page.getByRole("button", { name: "Log out" })).toBeVisible();
});
test("로그인 사용자가 / 에 접속하면 대시보드로 진입해야 한다", async ({ page }) => {
@@ -136,5 +136,5 @@ test("로그인 사용자가 / 에 접속하면 대시보드로 진입해야 한
await page.goto("/");
await expect(page).toHaveURL(/\/dashboard/);
await expect(page.getByRole("heading", { name: "대시보드" })).toBeVisible();
await expect(page.getByRole("heading", { name: "Dashboard" })).toBeVisible();
});
+34 -30
View File
@@ -23,7 +23,7 @@ test("/editor 페이지가 존재하고 Page Editor 헤더를 보여줘야 한
test("텍스트 버튼을 누르면 캔버스에 '새 텍스트' 블록이 보여야 한다", async ({ page }) => {
await page.goto("/editor");
await page.getByRole("button", { name: "텍스트" }).first().click();
await page.getByRole("button", { name: "Text" }).first().click();
const canvas = page.getByTestId("editor-canvas");
await expect(canvas.getByText("새 텍스트")).toBeVisible();
@@ -255,22 +255,22 @@ test("에디터 상태를 JSON으로 내보내고 다시 불러오면 동일한
await sizeSelect.selectOption("lg");
// JSON 내보내기/불러오기 모달을 연다.
await page.getByRole("button", { name: "메뉴 ▼" }).click();
await page.getByRole("button", { name: "JSON 내보내기/불러오기" }).click();
await page.getByRole("button", { name: "Menu" }).click();
await page.getByRole("button", { name: "JSON export / import" }).click();
// 현재 상태를 JSON으로 내보낸다.
await page.getByRole("button", { name: "JSON 내보내기" }).click();
const exportArea = page.getByRole("textbox", { name: "에디터 상태 JSON" });
await page.getByRole("button", { name: "Export JSON" }).click();
const exportArea = page.getByRole("textbox", { name: "Editor state JSON" });
const exportedJson = await exportArea.inputValue();
// 캔버스를 초기화한다.
await page.getByRole("button", { name: "캔버스 초기화" }).click();
await page.getByRole("button", { name: "Clear canvas" }).click();
await expect(canvas.getByTestId("editor-block")).toHaveCount(0);
// JSON을 다시 입력하고 불러온다.
const importArea = page.getByRole("textbox", { name: "JSON에서 불러오기" });
const importArea = page.getByRole("textbox", { name: "Import from JSON" });
await importArea.fill(exportedJson);
await page.getByRole("button", { name: "JSON 적용하기" }).click();
await page.getByRole("button", { name: "Apply JSON" }).click();
// 복원된 캔버스에 두 블록이 모두 존재해야 한다.
const restoredBlocks = canvas.getByTestId("editor-block");
@@ -510,9 +510,9 @@ test("에디터 메뉴에서 페이지 파일로 내보내기 (ZIP)를 클릭하
await page.getByRole("button", { name: "텍스트" }).first().click();
// 헤더 메뉴를 연다.
await page.getByRole("button", { name: "메뉴 ▼" }).click();
await page.getByRole("button", { name: "Menu" }).click();
const exportButton = page.getByRole("button", { name: "페이지 파일로 내보내기 (ZIP)" });
const exportButton = page.getByRole("button", { name: "Export as page files (ZIP)" });
await expect(exportButton).toBeVisible();
const [download] = await Promise.all([
@@ -573,10 +573,10 @@ test("Hero 템플릿 버튼을 클릭하면 섹션과 기본 텍스트/버튼
await expect(blocks.first()).toBeVisible();
// Hero 헤드라인 텍스트가 포함된 블록이 보여야 한다.
await expect(canvas.getByText("Hero 제목을 여기에 입력하세요")).toBeVisible();
await expect(canvas.getByText("Your hero headline goes here")).toBeVisible();
// CTA 버튼도 렌더되어 있어야 한다.
const ctaButton = canvas.getByRole("button", { name: "지금 시작하기" });
const ctaButton = canvas.getByRole("button", { name: "Get started now" });
await expect(ctaButton).toBeVisible();
});
@@ -588,9 +588,9 @@ test("Features 템플릿 버튼을 클릭하면 3개의 Feature 항목(제목/
await page.getByRole("button", { name: "기능 템플릿" }).click();
// Feature 제목들이 렌더되어야 한다.
await expect(canvas.getByText("Feature 1 제목")).toBeVisible();
await expect(canvas.getByText("Feature 2 제목")).toBeVisible();
await expect(canvas.getByText("Feature 3 제목")).toBeVisible();
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 }) => {
@@ -600,8 +600,8 @@ test("CTA 템플릿 버튼을 클릭하면 CTA 텍스트와 버튼이 포함된
await page.getByRole("button", { name: "CTA 템플릿" }).click();
await expect(canvas.getByText("지금 바로 행동을 유도하는 CTA 텍스트를 입력하세요.")).toBeVisible();
const ctaButton = canvas.getByRole("button", { name: "CTA 버튼" });
await expect(canvas.getByText("Write a compelling CTA message here.")).toBeVisible();
const ctaButton = canvas.getByRole("button", { name: "Call to action" });
await expect(ctaButton).toBeVisible();
});
@@ -613,10 +613,14 @@ test("FAQ 템플릿 버튼을 클릭하면 질문/답변 텍스트가 세 쌍
await page.getByRole("button", { name: "FAQ 템플릿" }).click();
// 기본 FAQ 질문/답변 텍스트들이 렌더되어야 한다.
await expect(canvas.getByText("서비스 이용료는 얼마인가요?")).toBeVisible();
await expect(canvas.getByText("기본 기능은 무료로 제공되며, 프리미엄 기능은 월 구독료가 발생합니다.")).toBeVisible();
await expect(canvas.getByText("환불 정책은 어떻게 되나요?")).toBeVisible();
await expect(canvas.getByText("팀원 초대 기능이 있나요?")).toBeVisible();
await expect(canvas.getByText("How much does the service cost?")).toBeVisible();
await expect(
canvas.getByText(
"The core features are free, and premium features are available with a monthly subscription.",
),
).toBeVisible();
await expect(canvas.getByText("What is your refund policy?")).toBeVisible();
await expect(canvas.getByText("Can I invite teammates?")).toBeVisible();
});
test("Pricing 템플릿 버튼을 클릭하면 3개의 요금제 카드가 생성되어야 한다", async ({ page }) => {
@@ -627,11 +631,11 @@ test("Pricing 템플릿 버튼을 클릭하면 3개의 요금제 카드가 생
await page.getByRole("button", { name: "상품 템플릿" }).click();
await expect(canvas.getByText("Basic")).toBeVisible();
await expect(canvas.getByText("₩9,900/월")).toBeVisible();
await expect(canvas.getByText("$9/month")).toBeVisible();
await expect(canvas.getByText("Pro")).toBeVisible();
await expect(canvas.getByText("29,900/월")).toBeVisible();
await expect(canvas.getByText("$29/month")).toBeVisible();
await expect(canvas.getByText("Enterprise")).toBeVisible();
await expect(canvas.getByText("문의")).toBeVisible();
await expect(canvas.getByText("Contact us")).toBeVisible();
});
test("Testimonials 템플릿 버튼을 클릭하면 3개의 후기 카드가 생성되어야 한다", async ({ page }) => {
@@ -743,8 +747,8 @@ test("헤더의 실행 취소/다시 실행 버튼으로 블록 추가를 Undo/R
await page.getByRole("button", { name: "텍스트" }).first().click();
await expect(canvas.getByTestId("editor-block")).toHaveCount(1);
const undoButton = page.getByRole("button", { name: "실행 취소" });
const redoButton = page.getByRole("button", { name: "다시 실행" });
const undoButton = page.getByRole("button", { name: "Undo" });
const redoButton = page.getByRole("button", { name: "Redo" });
await undoButton.click();
await expect(canvas.getByTestId("editor-block")).toHaveCount(0);
@@ -909,10 +913,10 @@ test("리스트 아이템을 선택하면 패널에서 아이템 위/아래/들
await firstItem.click({ force: true });
// 리스트 아이템 행에 위치한 조작 버튼들을 사용할 수 있어야 한다.
const moveUpButton = firstItem.getByRole("button", { name: "" });
const moveDownButton = firstItem.getByRole("button", { name: "아래" });
const indentButton = firstItem.getByRole("button", { name: "들여쓰기" });
const outdentButton = firstItem.getByRole("button", { name: "내어쓰기" });
const moveUpButton = firstItem.getByRole("button", { name: "Move item up" });
const moveDownButton = firstItem.getByRole("button", { name: "Move item down" });
const indentButton = firstItem.getByRole("button", { name: "Indent item" });
const outdentButton = firstItem.getByRole("button", { name: "Outdent item" });
await expect(moveUpButton).toBeVisible();
await expect(moveDownButton).toBeVisible();
+22 -18
View File
@@ -34,8 +34,8 @@ test("에디터에서 Hero 템플릿을 추가하면 프리뷰에서 동일한 H
// 헤드라인과 CTA 버튼이 에디터에서 생성되었는지 한 번 확인한다.
const editorCanvas = page.getByTestId("editor-canvas");
await expect(editorCanvas.getByText("Hero 제목을 여기에 입력하세요")).toBeVisible();
await expect(editorCanvas.getByRole("button", { name: "지금 시작하기" })).toBeVisible();
await expect(editorCanvas.getByText("Your hero headline goes here")).toBeVisible();
await expect(editorCanvas.getByRole("button", { name: "Get started now" })).toBeVisible();
// 헤더의 프리뷰 열기 링크를 통해 /preview 로 이동한다 (클라이언트 내 내비게이션 유지).
await page.getByRole("link", { name: "프리뷰 열기" }).click();
@@ -44,9 +44,9 @@ test("에디터에서 Hero 템플릿을 추가하면 프리뷰에서 동일한 H
await expect(page.getByRole("heading", { name: "Page Preview" })).toBeVisible();
// 프리뷰에서도 Hero 헤드라인과 CTA 버튼이 그대로 보여야 한다.
await expect(page.getByText("Hero 제목을 여기에 입력하세요")).toBeVisible();
await expect(page.getByText("Your hero headline goes here")).toBeVisible();
// 프리뷰에서는 CTA를 실제 링크(<a>)로 렌더링하므로 텍스트 기준으로만 검증한다.
await expect(page.getByText("지금 시작하기")).toBeVisible();
await expect(page.getByText("Get started now")).toBeVisible();
// 프리뷰 화면에는 에디터용 "텍스트" 버튼이 없어야 한다.
await expect(page.getByRole("button", { name: "텍스트" })).toHaveCount(0);
@@ -73,17 +73,17 @@ test("에디터에서 Features 템플릿을 추가하면 프리뷰에서 3개의
await page.getByRole("button", { name: "기능 템플릿" }).click();
const editorCanvas = page.getByTestId("editor-canvas");
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("Feature 1")).toBeVisible();
await expect(editorCanvas.getByText("Feature 2")).toBeVisible();
await expect(editorCanvas.getByText("Feature 3")).toBeVisible();
await page.getByRole("link", { name: "프리뷰 열기" }).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("Feature 1")).toBeVisible();
await expect(page.getByText("Feature 2")).toBeVisible();
await expect(page.getByText("Feature 3")).toBeVisible();
});
test("에디터에서 CTA 템플릿을 추가하면 프리뷰에서 CTA 텍스트와 버튼이 보여야 한다", async ({ page }) => {
@@ -92,15 +92,15 @@ test("에디터에서 CTA 템플릿을 추가하면 프리뷰에서 CTA 텍스
await page.getByRole("button", { name: "CTA 템플릿" }).click();
const editorCanvas = page.getByTestId("editor-canvas");
await expect(editorCanvas.getByText("지금 바로 행동을 유도하는 CTA 텍스트를 입력하세요.")).toBeVisible();
await expect(editorCanvas.getByRole("button", { name: "CTA 버튼" })).toBeVisible();
await expect(editorCanvas.getByText("Write a compelling CTA message here.")).toBeVisible();
await expect(editorCanvas.getByRole("button", { name: "Call to action" })).toBeVisible();
await page.getByRole("link", { name: "프리뷰 열기" }).click();
await expect(page.getByRole("heading", { name: "Page Preview" })).toBeVisible();
await expect(page.getByText("지금 바로 행동을 유도하는 CTA 텍스트를 입력하세요.")).toBeVisible();
await expect(page.getByText("CTA 버튼")).toBeVisible();
await expect(page.getByText("Write a compelling CTA message here.")).toBeVisible();
await expect(page.getByText("Call to action")).toBeVisible();
});
test("모바일 뷰에서도 프리뷰 페이지가 깨지지 않고 주요 템플릿 컨텐츠를 보여줘야 한다", async ({ page }) => {
@@ -122,17 +122,21 @@ test("모바일 뷰에서도 프리뷰 페이지가 깨지지 않고 주요 템
await expect(page.getByRole("heading", { name: "Page Preview" })).toBeVisible();
// Hero 헤드라인
await expect(page.getByText("Hero 제목을 여기에 입력하세요")).toBeVisible();
await expect(page.getByText("Your hero headline goes here")).toBeVisible();
// Features 제목 일부
await expect(page.getByText("Feature 1 제목")).toBeVisible();
await expect(page.getByText("Feature 1")).toBeVisible();
// Pricing 플랜 이름/가격 일부
await expect(page.getByText("Basic")).toBeVisible();
await expect(page.getByText("₩9,900/월")).toBeVisible();
await expect(page.getByText("$9/month")).toBeVisible();
// Testimonials 본문 일부
await expect(page.getByText("이 서비스 덕분에 랜딩 페이지 제작 시간이 크게 줄었습니다.")).toBeVisible();
await expect(
page.getByText(
"This builder dramatically reduced the time we spend creating landing pages.",
),
).toBeVisible();
});
test("텍스트 블록의 정렬/폰트 크기/색상이 프리뷰에도 반영되어야 한다", async ({ page }) => {
+9 -9
View File
@@ -209,7 +209,7 @@ test("프로젝트 목록에서 '폼 제출 내역' 링크를 클릭하면 해
await expect(page.getByText("test-project-a")).toBeVisible();
// 액션 영역의 "폼 제출 내역" 링크를 클릭하면 제출 내역 페이지로 이동해야 한다.
await page.getByRole("link", { name: "폼 제출 내역" }).click();
await page.getByRole("link", { name: "Form submissions" }).click();
await expect(page).toHaveURL(/\/projects\/test-project-a\/submissions/);
@@ -284,10 +284,10 @@ test("프로젝트 목록 헤더에서 대시보드로 이동할 수 있어야
await expect(page.getByText("테스트 프로젝트 A")).toBeVisible();
await page.getByRole("link", { name: "대시보드" }).click();
await page.getByRole("link", { name: "Dashboard" }).click();
await expect(page).toHaveURL(/\/dashboard/);
await expect(page.getByRole("heading", { name: "대시보드" })).toBeVisible();
await expect(page.getByRole("heading", { name: "Dashboard" })).toBeVisible();
});
test("전체 제출 내역 페이지에서 공통 GNB와 메뉴가 보여야 한다", async ({ page }) => {
@@ -309,20 +309,20 @@ test("전체 제출 내역 페이지에서 공통 GNB와 메뉴가 보여야 한
await page.goto("/projects/submissions");
await expect(page.getByRole("heading", { name: "전체 폼 제출 내역" })).toBeVisible();
await expect(page.getByRole("heading", { name: "All form submissions" })).toBeVisible();
const dashboardLink = page.getByRole("link", { name: "대시보드" });
const dashboardLink = page.getByRole("link", { name: "Dashboard" });
await expect(dashboardLink).toBeVisible();
const projectsLink = page.getByRole("link", { name: "프로젝트 목록" });
const projectsLink = page.getByRole("link", { name: "Projects" });
await expect(projectsLink).toBeVisible();
const submissionsLink = page.getByRole("link", { name: "전체 제출 내역" });
const submissionsLink = page.getByRole("link", { name: "All submissions" });
await expect(submissionsLink).toBeVisible();
const menuButton = page.getByRole("button", { name: "메뉴" });
const menuButton = page.getByRole("button", { name: "Menu" });
await expect(menuButton).toBeVisible();
await menuButton.click();
await expect(page.getByRole("button", { name: "로그아웃" })).toBeVisible();
await expect(page.getByRole("button", { name: "Log out" })).toBeVisible();
});