리팩터링
This commit is contained in:
@@ -0,0 +1,243 @@
|
||||
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);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user