CI: feature/forms-submissions #24

Merged
jaybe merged 1 commits from feature/forms-submissions into main 2025-12-02 03:21:28 +00:00
3 changed files with 70 additions and 12 deletions
Showing only changes of commit 5832b64d39 - Show all commits
+48 -3
View File
@@ -48,10 +48,55 @@ jobs:
run: | run: |
npm test 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 - name: Run Playwright E2E tests
if: github.event_name == 'push' && github.ref == 'refs/heads/main' run: npm run e2e
run: |
npx playwright test
pr_and_merge: pr_and_merge:
needs: test needs: test
+15 -1
View File
@@ -17,7 +17,21 @@ test("프리뷰 폼 제출 후 제출 내역 페이지에서 데이터가 보여
data: { email, password }, 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"]; const setCookieHeader = signupRes.headers()["set-cookie"];
expect(setCookieHeader).toBeTruthy(); expect(setCookieHeader).toBeTruthy();
+7 -8
View File
@@ -1074,7 +1074,7 @@ test("폼 체크박스 옵션 간격 px 입력이 프리뷰에서도 em 스케
expect(gapStyles.inlineRowGap.endsWith("em")).toBeTruthy(); expect(gapStyles.inlineRowGap.endsWith("em")).toBeTruthy();
}); });
test("FormBlock 은 프리뷰에서 별도 폼 컨트롤러 DOM 을 렌더하지 않아야 한다 (고정 너비 설정 시)", async ({ page }) => { test("FormBlock 은 프리뷰에서 폼 컨트롤러 DOM 을 렌더야 한다 (고정 너비 설정 시)", async ({ page }) => {
await page.goto("/editor"); await page.goto("/editor");
await page.getByRole("button", { name: "폼 컨트롤러" }).click(); await page.getByRole("button", { name: "폼 컨트롤러" }).click();
@@ -1093,10 +1093,10 @@ test("FormBlock 은 프리뷰에서 별도 폼 컨트롤러 DOM 을 렌더하지
await expect(page.getByRole("heading", { name: "Page Preview" })).toBeVisible(); await expect(page.getByRole("heading", { name: "Page Preview" })).toBeVisible();
const formLocator = page.getByTestId("preview-form-controller"); 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.goto("/editor");
await page.getByRole("button", { name: "폼 컨트롤러" }).click(); await page.getByRole("button", { name: "폼 컨트롤러" }).click();
@@ -1115,7 +1115,7 @@ test("FormBlock 은 프리뷰에서 별도 폼 컨트롤러 DOM 을 렌더하지
await expect(page.getByRole("heading", { name: "Page Preview" })).toBeVisible(); await expect(page.getByRole("heading", { name: "Page Preview" })).toBeVisible();
const formLocator = page.getByTestId("preview-form-controller"); const formLocator = page.getByTestId("preview-form-controller");
await expect(formLocator).toHaveCount(0); await expect(formLocator).toHaveCount(1);
}); });
test("폼 라디오 줄간격 px 입력이 프리뷰에서도 em 스케일로 반영되어야 한다", async ({ page }) => { test("폼 라디오 줄간격 px 입력이 프리뷰에서도 em 스케일로 반영되어야 한다", async ({ page }) => {
@@ -1584,8 +1584,8 @@ test("폼 컨트롤러에 매핑한 폼 요소와 버튼이 프리뷰에서 함
const input = page.getByRole("textbox").first(); const input = page.getByRole("textbox").first();
await expect(input).toBeVisible(); await expect(input).toBeVisible();
// 프리뷰 정책: FormBlock 컨트롤러는 프리뷰에서 실제 `<form>` DOM 을 렌더하지 않는다. const forms = page.getByTestId("preview-form-controller");
await expect(page.getByTestId("preview-form-controller")).toHaveCount(0); await expect(forms).toHaveCount(1);
await expect(page.getByText("성공적으로 전송되었습니다.")).toHaveCount(0); await expect(page.getByText("성공적으로 전송되었습니다.")).toHaveCount(0);
}); });
@@ -1647,9 +1647,8 @@ test("여러 폼 블록과 여러 버튼이 있을 때 각 폼이 독립적으
const firstInput = textboxes.nth(0); const firstInput = textboxes.nth(0);
const secondInput = textboxes.nth(1); const secondInput = textboxes.nth(1);
// 프리뷰 정책: 여러 FormBlock 이 있어도 preview-form-controller DOM 은 생성되지 않아야 한다.
const controllers = page.getByTestId("preview-form-controller"); const controllers = page.getByTestId("preview-form-controller");
await expect(controllers).toHaveCount(0); await expect(controllers).toHaveCount(2);
const successMessages = page.getByText("성공적으로 전송되었습니다."); const successMessages = page.getByText("성공적으로 전송되었습니다.");
await expect(successMessages).toHaveCount(0); await expect(successMessages).toHaveCount(0);