244 lines
7.3 KiB
TypeScript
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);
|
|
});
|
|
});
|