Files
page-builder/tests/e2e/project-status-public-page.spec.ts
T
jaybe 7491dacba2
CI / test (push) Successful in 5m38s
CI / e2e (push) Has been skipped
CI / pr_and_merge (push) Successful in 1m12s
리팩터링
2025-12-16 17:49:25 +09:00

244 lines
7.3 KiB
TypeScript

import { test, expect } from "@playwright/test";
async function signupAndGetAccessToken(
request: import("@playwright/test").APIRequestContext,
email: string,
password: string,
): Promise<string> {
const signupRes = await request.post("/api/auth/signup", {
headers: { "Content-Type": "application/json" },
data: { email, password },
});
const signupStatus = signupRes.status();
if (signupStatus !== 201) {
// 디버깅용: CI 등에서 500 이 떨어질 때 응답 바디를 함께 로그로 출력해 원인을 파악할 수 있게 한다.
console.log("[project-status-public-page] signup error status=", signupStatus);
try {
const bodyText = await signupRes.text();
console.log("[project-status-public-page] signup error body=", bodyText);
} catch (e) {
console.log("[project-status-public-page] signup error: body read failed", e);
}
}
expect(signupStatus).toBe(201);
const setCookieHeader = signupRes.headers()["set-cookie"];
expect(setCookieHeader).toBeTruthy();
const cookieHeaderString = Array.isArray(setCookieHeader)
? setCookieHeader.join("; ")
: (setCookieHeader as string);
const match = cookieHeaderString.match(/pb_access=([^;]+)/);
expect(match).not.toBeNull();
const accessToken = match![1];
return accessToken;
}
async function createProject(
request: import("@playwright/test").APIRequestContext,
accessToken: string,
params: { slug: string; title: string; contentJson?: any },
) {
const { slug, title, contentJson = [] } = params;
const res = await request.post("/api/projects", {
headers: {
"Content-Type": "application/json",
Cookie: `pb_access=${accessToken}`,
},
data: {
title,
slug,
contentJson,
},
});
const status = res.status();
if (status !== 201) {
console.log("[project-status-public-page] createProject error status=", status);
try {
const bodyText = await res.text();
console.log("[project-status-public-page] createProject error body=", bodyText);
} catch (e) {
console.log("[project-status-public-page] createProject error: body read failed", e);
}
}
expect(status).toBe(201);
}
async function patchProjectStatus(
request: import("@playwright/test").APIRequestContext,
accessToken: string,
slug: string,
status: "DRAFT" | "PUBLISHED" | "ARCHIVED",
) {
const res = await request.patch(`/api/projects/${slug}`, {
headers: {
"Content-Type": "application/json",
Cookie: `pb_access=${accessToken}`,
},
data: { status },
});
const code = res.status();
if (code !== 200) {
console.log("[project-status-public-page] patchProjectStatus error status=", code);
try {
const bodyText = await res.text();
console.log("[project-status-public-page] patchProjectStatus error body=", bodyText);
} catch (e) {
console.log("[project-status-public-page] patchProjectStatus error: body read failed", e);
}
}
expect(code).toBe(200);
}
// 프로젝트 status 에 따른 퍼블릭 페이지(/p/[slug]) 기본 동작을 검증하는 E2E.
// - DRAFT: 404
// - PUBLISHED: 200
test.describe("Public project status behaviour", () => {
test("DRAFT 프로젝트는 퍼블릭 페이지에서 404 를 반환해야 한다", async ({ page, request }) => {
const now = Date.now();
const email = `status-draft-${now}@example.com`;
const password = "status-draft-password";
const slug = `status-draft-${now}`;
const accessToken = await signupAndGetAccessToken(request, email, password);
await createProject(request, accessToken, {
slug,
title: "Status DRAFT project",
contentJson: [],
});
const response = await page.goto(`/p/${slug}`);
expect(response?.status()).toBe(404);
});
test("PUBLISHED 프로젝트는 퍼블릭 페이지에서 200 으로 열려야 한다", async ({ page, request }) => {
const now = Date.now();
const email = `status-published-${now}@example.com`;
const password = "status-published-password";
const slug = `status-published-${now}`;
const accessToken = await signupAndGetAccessToken(request, email, password);
await createProject(request, accessToken, {
slug,
title: "Status PUBLISHED project",
contentJson: [],
});
await patchProjectStatus(request, accessToken, slug, "PUBLISHED");
const response = await page.goto(`/p/${slug}`);
expect(response?.status()).toBe(200);
});
test("ARCHIVED 프로젝트는 퍼블릭 페이지에서 폼은 보이지만 제출이 차단되어야 한다", async ({ page, request }) => {
const now = Date.now();
const email = `status-archived-${now}@example.com`;
const password = "status-archived-password";
const slug = `status-archived-${now}`;
const accessToken = await signupAndGetAccessToken(request, email, password);
const inputId = "field_name";
const buttonId = "submit_btn";
const formId = "form_controller_1";
const disabledMessage = "Submission is disabled for this project.";
const contentJson = [
{
id: inputId,
type: "formInput",
props: {
formFieldName: "name",
label: "Name",
inputType: "text",
labelDisplay: "visible",
align: "left",
widthMode: "full",
paddingX: 0,
paddingY: 0,
borderRadius: "md",
},
sectionId: null,
columnId: null,
},
{
id: buttonId,
type: "button",
props: {
label: "Submit",
href: "#",
align: "left",
size: "md",
variant: "solid",
colorPalette: "primary",
borderRadius: "md",
},
sectionId: null,
columnId: null,
},
{
id: formId,
type: "form",
props: {
kind: "contact",
submitTarget: "internal",
successMessage: "Submitted successfully.",
errorMessage: disabledMessage,
payloadFormat: "form",
fieldIds: [inputId],
requiredFieldIds: [inputId],
submitButtonId: buttonId,
formWidthMode: "auto",
},
sectionId: null,
columnId: null,
},
];
await createProject(request, accessToken, {
slug,
title: "Status ARCHIVED project",
contentJson,
});
await patchProjectStatus(request, accessToken, slug, "ARCHIVED");
const submitRequests: string[] = [];
page.on("request", (req) => {
const url = req.url();
if (url.includes("/api/forms/submit")) {
submitRequests.push(url);
}
});
const response = await page.goto(`/p/${slug}`);
expect(response?.status()).toBe(200);
const input = page.locator("input.pb-input").first();
await input.waitFor();
// 퍼블릭 페이지 클라이언트 스크립트가 폼 submit 이벤트를 후킹해 data-pb-initialized 속성을 설정할 때까지 기다린다.
// 이 폼은 hidden 일 수 있으므로, DOM 에만 붙어 있으면 되도록 state 는 attached 로 제한한다.
await page.waitForSelector('form[data-pb-initialized="1"]', { state: "attached" });
await input.fill("Archived user");
await page.getByRole("button", { name: "Submit" }).click();
await expect(page.getByText(disabledMessage)).toBeVisible();
expect(submitRequests.length).toBe(0);
});
});