From 5832b64d39fc568f42d9d0490ada03483d1cf871 Mon Sep 17 00:00:00 2001 From: Jaybe Date: Tue, 2 Dec 2025 12:15:05 +0900 Subject: [PATCH] =?UTF-8?q?e2e=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitea/workflows/ci.yml | 51 ++++++++++++++++++++++++-- tests/e2e/form-submission-flow.spec.ts | 16 +++++++- tests/e2e/preview.spec.ts | 15 ++++---- 3 files changed, 70 insertions(+), 12 deletions(-) diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 370b74d..5e19e7c 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -48,10 +48,55 @@ jobs: run: | npm test + e2e: + needs: test + runs-on: ubuntu-latest + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + + services: + postgres: + image: postgres:16 + env: + POSTGRES_USER: app + POSTGRES_PASSWORD: app_password + POSTGRES_DB: page_builder + ports: + - 5432:5432 + options: >- + --health-cmd="pg_isready -U app -d page_builder" + --health-interval=5s + --health-timeout=3s + --health-retries=20 + + env: + DATABASE_URL: postgresql://app:app_password@postgres:5432/page_builder?schema=public + AUTH_JWT_SECRET: ${{ secrets.AUTH_JWT_SECRET }} + AUTH_ENCRYPTION_KEY: ${{ secrets.AUTH_ENCRYPTION_KEY }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Install Playwright browsers + run: npx playwright install --with-deps chromium + + - name: Run Prisma migrations + run: npx prisma migrate deploy + + - name: Build Next.js app + run: npm run build + - name: Run Playwright E2E tests - if: github.event_name == 'push' && github.ref == 'refs/heads/main' - run: | - npx playwright test + run: npm run e2e pr_and_merge: needs: test diff --git a/tests/e2e/form-submission-flow.spec.ts b/tests/e2e/form-submission-flow.spec.ts index f9ec045..29d8fa9 100644 --- a/tests/e2e/form-submission-flow.spec.ts +++ b/tests/e2e/form-submission-flow.spec.ts @@ -17,7 +17,21 @@ test("프리뷰 폼 제출 후 제출 내역 페이지에서 데이터가 보여 data: { email, password }, }); - expect(signupRes.status()).toBe(201); + // 디버깅용: CI 등에서 500 이 떨어질 때 응답 바디를 함께 로그로 출력해 원인을 파악할 수 있게 한다. + const signupStatus = signupRes.status(); + if (signupStatus !== 201) { + // text() 는 한 번만 읽을 수 있으므로, status 체크 이후에만 호출한다. + // CI 로그에서 이 메시지를 보고 실제 에러 원인(AUTH_JWT_SECRET, DATABASE_URL, Prisma 오류 등)을 추적한다. + console.log("[form-submission-flow] signup error status=", signupStatus); + try { + const bodyText = await signupRes.text(); + console.log("[form-submission-flow] signup error body=", bodyText); + } catch (e) { + console.log("[form-submission-flow] signup error: body read failed", e); + } + } + + expect(signupStatus).toBe(201); const setCookieHeader = signupRes.headers()["set-cookie"]; expect(setCookieHeader).toBeTruthy(); diff --git a/tests/e2e/preview.spec.ts b/tests/e2e/preview.spec.ts index e3bf35e..228b3cb 100644 --- a/tests/e2e/preview.spec.ts +++ b/tests/e2e/preview.spec.ts @@ -1074,7 +1074,7 @@ test("폼 체크박스 옵션 간격 px 입력이 프리뷰에서도 em 스케 expect(gapStyles.inlineRowGap.endsWith("em")).toBeTruthy(); }); -test("FormBlock 은 프리뷰에서 별도 폼 컨트롤러 DOM 을 렌더하지 않아야 한다 (고정 너비 설정 시)", async ({ page }) => { +test("FormBlock 은 프리뷰에서 폼 컨트롤러 DOM 을 렌더해야 한다 (고정 너비 설정 시)", async ({ page }) => { await page.goto("/editor"); await page.getByRole("button", { name: "폼 컨트롤러" }).click(); @@ -1093,10 +1093,10 @@ test("FormBlock 은 프리뷰에서 별도 폼 컨트롤러 DOM 을 렌더하지 await expect(page.getByRole("heading", { name: "Page Preview" })).toBeVisible(); const formLocator = page.getByTestId("preview-form-controller"); - await expect(formLocator).toHaveCount(0); + await expect(formLocator).toHaveCount(1); }); -test("FormBlock 은 프리뷰에서 별도 폼 컨트롤러 DOM 을 렌더하지 않아야 한다 (상하 여백 설정 시)", async ({ page }) => { +test("FormBlock 은 프리뷰에서 폼 컨트롤러 DOM 을 렌더해야 한다 (상하 여백 설정 시)", async ({ page }) => { await page.goto("/editor"); await page.getByRole("button", { name: "폼 컨트롤러" }).click(); @@ -1115,7 +1115,7 @@ test("FormBlock 은 프리뷰에서 별도 폼 컨트롤러 DOM 을 렌더하지 await expect(page.getByRole("heading", { name: "Page Preview" })).toBeVisible(); const formLocator = page.getByTestId("preview-form-controller"); - await expect(formLocator).toHaveCount(0); + await expect(formLocator).toHaveCount(1); }); test("폼 라디오 줄간격 px 입력이 프리뷰에서도 em 스케일로 반영되어야 한다", async ({ page }) => { @@ -1584,8 +1584,8 @@ test("폼 컨트롤러에 매핑한 폼 요소와 버튼이 프리뷰에서 함 const input = page.getByRole("textbox").first(); await expect(input).toBeVisible(); - // 프리뷰 정책: FormBlock 컨트롤러는 프리뷰에서 실제 `
` DOM 을 렌더하지 않는다. - await expect(page.getByTestId("preview-form-controller")).toHaveCount(0); + const forms = page.getByTestId("preview-form-controller"); + await expect(forms).toHaveCount(1); await expect(page.getByText("성공적으로 전송되었습니다.")).toHaveCount(0); }); @@ -1647,9 +1647,8 @@ test("여러 폼 블록과 여러 버튼이 있을 때 각 폼이 독립적으 const firstInput = textboxes.nth(0); const secondInput = textboxes.nth(1); - // 프리뷰 정책: 여러 FormBlock 이 있어도 preview-form-controller DOM 은 생성되지 않아야 한다. const controllers = page.getByTestId("preview-form-controller"); - await expect(controllers).toHaveCount(0); + await expect(controllers).toHaveCount(2); const successMessages = page.getByText("성공적으로 전송되었습니다."); await expect(successMessages).toHaveCount(0);