비디오 태그 및 폼 블록 정리
CI / test (push) Failing after 7m30s
CI / pr_and_merge (push) Has been skipped

This commit is contained in:
2025-11-27 19:37:32 +09:00
parent 1405280ed3
commit c5c9d17e8b
50 changed files with 365 additions and 173 deletions
+83 -11
View File
@@ -404,7 +404,7 @@ describe("/api/export", () => {
);
});
it("폼 블록과 컨트롤러 필드 블록들은 정적 HTML에서도 폼 요소로 렌더되어야 한다", async () => {
it("FormBlock 은 Export 레이어에서 컨트롤러 역할만 하고, 필드 블록들은 form 속성으로 해당 폼과 연결되어야 한다", async () => {
const blocks: Block[] = [
{
id: "form_1",
@@ -470,18 +470,29 @@ describe("/api/export", () => {
const html = await indexEntry!.async("string");
// form 요소와 기본 input/select 가 포함되어야 한다.
expect(html).toContain("<form");
expect(html).toContain("name=\"name\"");
expect(html).toContain("name=\"plan\"");
expect(html).toContain("<select");
// required: formInput(name) 은 required, formSelect(plan) 은 required 가 없어야 한다.
expect(html).toMatch(/name=\"name\"[^>]*required/);
expect(html).not.toMatch(/name=\"plan\"[^>]*required/);
// FormBlock 은 컨트롤러 전용이므로, Export 에서는 id 를 가진 단일 <form> 컨테이너만 생성하고
// 실제 필드 레이아웃은 개별 formInput/formSelect 블록이 담당한다.
// - <form id="form_form_1" ...>
const formId = "form_form_1";
expect(html).toMatch(new RegExp(`<form[^>]*id=\\"${formId}\\"`));
// 컨트롤러 필드 블록(formInput/formSelect) 이 렌더한 input/select 는 각각 name 과 form 속성을 가져야 한다.
// name="name" input 은 required + form="form_form_1" 이어야 한다.
expect(html).toMatch(/name=\"name\"[^>]*required[^>]*form=\"form_form_1\"/);
// name="plan" select 는 required 는 아니지만 form="form_form_1" 이어야 한다.
expect(html).toMatch(/name=\"plan\"[^>]*form=\"form_form_1\"/);
// FormBlock 이 자체적으로 필드 레이아웃을 중복 생성하지 않도록,
// id 가 form_form_1 인 <form> 내부에는 name="name"/"plan" 필드가 없어야 한다.
const formStart = html.indexOf("<form");
const formEnd = html.indexOf("</form>", formStart);
const formHtml = html.slice(formStart, formEnd);
expect(formHtml).not.toContain('name="name"');
expect(formHtml).not.toContain('name="plan"');
});
it("FormBlock 에 fieldIds/fields 가 모두 없으면 기본 폼/필드를 내보내지 않아야 한다", async () => {
it("FormBlock 에 fieldIds/fields 가 모두 없으면 Export 에서 아무 <form>/필드도 생성하지 않아야 한다", async () => {
const blocks: Block[] = [
{
id: "form_fallback_1",
@@ -529,6 +540,63 @@ describe("/api/export", () => {
expect(html).not.toMatch(/name=\"message\"/);
});
it("FormBlock 이 fields[] 만 가지고 있고 fieldIds 가 없더라도, Export 레이어에서 자체 pb-form 레이아웃/폼 UI 를 생성하지 않아야 한다", async () => {
const blocks: Block[] = [
{
id: "form_legacy_1",
type: "form",
props: {
kind: "contact",
submitTarget: "internal",
fieldIds: [],
fields: [
{
id: "f1",
name: "email",
label: "이메일",
type: "email",
required: true,
},
],
} as any,
} as any,
];
const projectConfig: ProjectConfig = {
title: "폼 fallback(fields[]) 내보내기 테스트",
slug: "form-fallback-fields-export-test",
canvasPreset: "full",
};
const payload = { blocks, projectConfig };
const { POST: handleExport } = await import("@/app/api/export/route");
const res = await handleExport(
new Request(`${BASE_URL}/api/export`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
}),
);
expect(res.status).toBe(200);
const arrayBuffer = await res.arrayBuffer();
const zip = await JSZip.loadAsync(arrayBuffer);
const indexEntry = zip.file("index.html");
expect(indexEntry).toBeTruthy();
const html = await indexEntry!.async("string");
// 새로운 규칙: FormBlock 은 어떤 경우에도 자체 레이아웃/폼 UI 를 생성하지 않는다.
// - fallback fields 기반 pb-form 도 더 이상 허용하지 않는다.
expect(html).not.toContain("class=\"pb-form\"");
expect(html).not.toContain("<form");
expect(html).not.toMatch(/name=\"email\"/);
});
it("/api/image/:id 기반 이미지 URL은 ZIP 안 images/ 디렉터리로 복사되고 HTML에서 상대 경로로 치환되어야 한다", async () => {
const imageId = `test-image-${Date.now()}`;
const uploadDir = path.join(process.cwd(), "uploads");
@@ -1787,7 +1855,11 @@ describe("/api/export", () => {
expect(indexEntry).toBeTruthy();
const html = await indexEntry!.async("string");
expect(html).toContain("<form");
// 새로운 규칙: FormBlock 은 Export 에서 자체 폼 레이아웃을 생성하지 않는다.
// - backgroundColorCustom 이 있더라도 form 요소/컨테이너에 배경색이 적용되지 않아야 한다.
// - pb-form 기반 레이아웃도 생성되지 않아야 한다.
expect(html).not.toContain("class=\"pb-form\"");
expect(html).not.toContain("background-color:#111111");
});