비디오 태그 및 폼 블록 정리
This commit is contained in:
+83
-11
@@ -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");
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user