617 lines
22 KiB
TypeScript
617 lines
22 KiB
TypeScript
import { describe, it, expect } from "vitest";
|
|
import { expectNoPxInStyleObject, expectNoPxInStyleString } from "./styleInvariants";
|
|
import {
|
|
computeFormInputExportTokens,
|
|
computeFormSelectExportTokens,
|
|
computeFormCheckboxExportTokens,
|
|
computeFormRadioExportTokens,
|
|
computeFormInputEditorTokens,
|
|
computeFormSelectEditorTokens,
|
|
computeFormOptionGroupEditorTokens,
|
|
computeFormInputPublicTokens,
|
|
computeFormSelectPublicTokens,
|
|
computeFormCheckboxPublicTokens,
|
|
computeFormRadioPublicTokens,
|
|
computeFormControllerPublicTokens,
|
|
computeFormBlockExportTokens,
|
|
} from "@/features/editor/utils/formHelpers";
|
|
import type {
|
|
Block,
|
|
FormBlockProps,
|
|
FormInputBlockProps,
|
|
FormSelectBlockProps,
|
|
FormCheckboxBlockProps,
|
|
FormRadioBlockProps,
|
|
} from "@/features/editor/state/editorStore";
|
|
|
|
const baseInput: FormInputBlockProps = {
|
|
label: "이름",
|
|
formFieldName: "name",
|
|
};
|
|
|
|
const baseSelect: FormSelectBlockProps = {
|
|
label: "플랜",
|
|
formFieldName: "plan",
|
|
options: [],
|
|
};
|
|
|
|
const baseCheckbox: FormCheckboxBlockProps = {
|
|
groupLabel: "옵션",
|
|
formFieldName: "options",
|
|
options: [],
|
|
};
|
|
|
|
const baseRadio: FormRadioBlockProps = {
|
|
groupLabel: "선택",
|
|
formFieldName: "choice",
|
|
options: [],
|
|
};
|
|
|
|
describe("formHelpers.computeFormInputExportTokens", () => {
|
|
it("텍스트 색상/배경/테두리/패딩/너비/둥글기 스타일을 계산해야 한다", () => {
|
|
const tokens = computeFormInputExportTokens({
|
|
...baseInput,
|
|
textColorCustom: " #ff0000 ",
|
|
fillColorCustom: " #111827 ",
|
|
strokeColorCustom: " #4b5563 ",
|
|
paddingX: 8,
|
|
paddingY: 4,
|
|
widthMode: "fixed",
|
|
widthPx: 300,
|
|
borderRadius: "lg",
|
|
});
|
|
|
|
expect(tokens.labelStyleAttr).toBe(' style="color:#ff0000"');
|
|
expect(tokens.inputStyleAttr).toContain("color:#ff0000");
|
|
expect(tokens.inputStyleAttr).toContain("background-color:#111827");
|
|
expect(tokens.inputStyleAttr).toContain("border-color:#4b5563");
|
|
expect(tokens.inputStyleAttr).toContain("padding-left:0.5em");
|
|
expect(tokens.inputStyleAttr).toContain("padding-right:0.5em");
|
|
expect(tokens.inputStyleAttr).toContain("padding-top:0.25em");
|
|
expect(tokens.inputStyleAttr).toContain("padding-bottom:0.25em");
|
|
expect(tokens.inputStyleAttr).toContain("width:18.75em");
|
|
expect(tokens.inputStyleAttr).toContain("border-radius:6px");
|
|
|
|
// Export 인풋 스타일에는 border-radius 등의 예외를 제외하고 px 단위가 포함되지 않아야 한다.
|
|
expectNoPxInStyleString(tokens.inputStyleAttr, {
|
|
allowPatterns: [/border-radius:\d+px/, /border-width:\d+px/],
|
|
});
|
|
});
|
|
|
|
it("스타일 값이 없으면 style 속성이 비어 있어야 한다", () => {
|
|
const tokens = computeFormInputExportTokens(baseInput);
|
|
expect(tokens.labelStyleAttr).toBe("");
|
|
expect(tokens.inputStyleAttr).toBe(' style="border-radius:4px"');
|
|
});
|
|
});
|
|
|
|
describe("formHelpers.computeFormSelectExportTokens", () => {
|
|
it("텍스트 색상/배경/테두리/패딩/너비/둥글기 스타일을 계산해야 한다", () => {
|
|
const tokens = computeFormSelectExportTokens({
|
|
...baseSelect,
|
|
textColorCustom: " #00ff00 ",
|
|
fillColorCustom: " #020617 ",
|
|
strokeColorCustom: " #64748b ",
|
|
paddingX: 12,
|
|
paddingY: 6,
|
|
widthMode: "fixed",
|
|
widthPx: 240,
|
|
borderRadius: "full",
|
|
});
|
|
|
|
expect(tokens.labelStyleAttr).toBe(' style="color:#00ff00"');
|
|
expect(tokens.selectStyleAttr).toContain("color:#00ff00");
|
|
expect(tokens.selectStyleAttr).toContain("background-color:#020617");
|
|
expect(tokens.selectStyleAttr).toContain("border-color:#64748b");
|
|
expect(tokens.selectStyleAttr).toContain("padding-left:0.75em");
|
|
expect(tokens.selectStyleAttr).toContain("padding-right:0.75em");
|
|
expect(tokens.selectStyleAttr).toContain("padding-top:0.375em");
|
|
expect(tokens.selectStyleAttr).toContain("padding-bottom:0.375em");
|
|
expect(tokens.selectStyleAttr).toContain("width:15em");
|
|
expect(tokens.selectStyleAttr).toContain("border-radius:9999px");
|
|
|
|
// Export 셀렉트 스타일에는 border-radius 등의 예외를 제외하고 px 단위가 포함되지 않아야 한다.
|
|
expectNoPxInStyleString(tokens.selectStyleAttr, {
|
|
allowPatterns: [/border-radius:\d+px/, /border-width:\d+px/],
|
|
});
|
|
});
|
|
|
|
it("스타일 값이 없으면 style 속성이 비어 있어야 한다", () => {
|
|
const tokens = computeFormSelectExportTokens(baseSelect);
|
|
expect(tokens.labelStyleAttr).toBe("");
|
|
expect(tokens.selectStyleAttr).toBe(' style="border-radius:4px"');
|
|
});
|
|
});
|
|
|
|
describe("formHelpers.computeFormCheckboxExportTokens", () => {
|
|
it("텍스트 색상/배경/테두리/패딩/둥글기 스타일을 계산해야 한다", () => {
|
|
const tokens = computeFormCheckboxExportTokens({
|
|
...baseCheckbox,
|
|
textColorCustom: " #38bdf8 ",
|
|
fillColorCustom: " #0f172a ",
|
|
strokeColorCustom: " #e5e7eb ",
|
|
paddingX: 10,
|
|
paddingY: 5,
|
|
borderRadius: "sm",
|
|
});
|
|
|
|
expect(tokens.labelStyleAttr).toBe(' style="color:#38bdf8"');
|
|
expect(tokens.optionStyleAttr).toContain("color:#38bdf8");
|
|
expect(tokens.optionStyleAttr).toContain("background-color:#0f172a");
|
|
expect(tokens.optionStyleAttr).toContain("border-color:#e5e7eb");
|
|
expect(tokens.optionStyleAttr).toContain("border-width:1px");
|
|
expect(tokens.optionStyleAttr).toContain("border-style:solid");
|
|
expect(tokens.optionStyleAttr).toContain("padding-left:0.625em");
|
|
expect(tokens.optionStyleAttr).toContain("padding-right:0.625em");
|
|
expect(tokens.optionStyleAttr).toContain("padding-top:0.3125em");
|
|
expect(tokens.optionStyleAttr).toContain("padding-bottom:0.3125em");
|
|
expect(tokens.optionStyleAttr).toContain("border-radius:2px");
|
|
|
|
// Export 체크박스 옵션 스타일에는 border/border-radius 를 제외하고 px 단위가 포함되지 않아야 한다.
|
|
expectNoPxInStyleString(tokens.optionStyleAttr, {
|
|
allowPatterns: [/border-radius:\d+px/, /border-width:\d+px/],
|
|
});
|
|
});
|
|
|
|
it("스타일 값이 없으면 style 속성이 비어 있어야 한다", () => {
|
|
const tokens = computeFormCheckboxExportTokens(baseCheckbox);
|
|
expect(tokens.labelStyleAttr).toBe("");
|
|
expect(tokens.optionStyleAttr).toBe(' style="border-radius:4px"');
|
|
});
|
|
});
|
|
|
|
describe("formHelpers.computeFormRadioExportTokens", () => {
|
|
it("체크박스와 동일한 규칙으로 옵션 스타일을 계산해야 한다", () => {
|
|
const tokens = computeFormRadioExportTokens({
|
|
...baseRadio,
|
|
textColorCustom: " #fbbf24 ",
|
|
fillColorCustom: " #111827 ",
|
|
strokeColorCustom: " #facc15 ",
|
|
paddingX: 4,
|
|
paddingY: 2,
|
|
borderRadius: "md",
|
|
});
|
|
|
|
expect(tokens.labelStyleAttr).toBe(' style="color:#fbbf24"');
|
|
expect(tokens.optionStyleAttr).toContain("color:#fbbf24");
|
|
expect(tokens.optionStyleAttr).toContain("background-color:#111827");
|
|
expect(tokens.optionStyleAttr).toContain("border-color:#facc15");
|
|
expect(tokens.optionStyleAttr).toContain("border-width:1px");
|
|
expect(tokens.optionStyleAttr).toContain("border-style:solid");
|
|
expect(tokens.optionStyleAttr).toContain("padding-left:0.25em");
|
|
expect(tokens.optionStyleAttr).toContain("padding-right:0.25em");
|
|
expect(tokens.optionStyleAttr).toContain("padding-top:0.125em");
|
|
expect(tokens.optionStyleAttr).toContain("padding-bottom:0.125em");
|
|
expect(tokens.optionStyleAttr).toContain("border-radius:4px");
|
|
|
|
// Export 라디오 옵션 스타일에는 border/border-radius 를 제외하고 px 단위가 포함되지 않아야 한다.
|
|
expectNoPxInStyleString(tokens.optionStyleAttr, {
|
|
allowPatterns: [/border-radius:\d+px/, /border-width:\d+px/],
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("formHelpers - editor tokens", () => {
|
|
it("computeFormInputEditorTokens: 에디터 인풋 필드 스타일을 계산해야 한다", () => {
|
|
const tokens = computeFormInputEditorTokens({
|
|
...baseInput,
|
|
textColorCustom: "#ff0000",
|
|
fillColorCustom: "#123456",
|
|
strokeColorCustom: "#654321",
|
|
widthMode: "fixed",
|
|
widthPx: 240,
|
|
borderRadius: "full",
|
|
align: "center",
|
|
labelLayout: "stacked",
|
|
} as any);
|
|
|
|
expect(tokens.fieldStyle.color).toBe("#ff0000");
|
|
expect(tokens.fieldStyle.backgroundColor).toBe("#123456");
|
|
expect(tokens.fieldStyle.borderColor).toBe("#654321");
|
|
expect(tokens.fieldStyle.width).toBe("240px");
|
|
expect(tokens.fieldStyle.borderRadius).toBe(9999);
|
|
});
|
|
|
|
it("computeFormSelectEditorTokens: 에디터 셀렉트 필드 스타일을 계산해야 한다", () => {
|
|
const tokens = computeFormSelectEditorTokens({
|
|
...baseSelect,
|
|
textColorCustom: "#ff0000",
|
|
fillColorCustom: "#123456",
|
|
strokeColorCustom: "#654321",
|
|
widthMode: "fixed",
|
|
widthPx: 260,
|
|
borderRadius: "lg",
|
|
paddingX: 12,
|
|
paddingY: 6,
|
|
fontSizeCustom: "14px",
|
|
lineHeightCustom: "20px",
|
|
letterSpacingCustom: "1px",
|
|
} as any);
|
|
|
|
expect(tokens.fieldStyle.color).toBe("#ff0000");
|
|
expect(tokens.fieldStyle.backgroundColor).toBe("#123456");
|
|
expect(tokens.fieldStyle.borderColor).toBe("#654321");
|
|
expect(tokens.fieldStyle.width).toBe("260px");
|
|
expect(tokens.fieldStyle.borderRadius).toBe(9999);
|
|
expect(tokens.fieldStyle.paddingInline).toBe("12px");
|
|
expect(tokens.fieldStyle.paddingBlock).toBe("6px");
|
|
expect(tokens.fieldStyle.fontSize).toBe("14px");
|
|
expect(tokens.fieldStyle.lineHeight).toBe("20px");
|
|
expect(tokens.fieldStyle.letterSpacing).toBe("1px");
|
|
});
|
|
|
|
it("computeFormOptionGroupEditorTokens: 에디터 라디오/체크박스 그룹 필드 스타일을 계산해야 한다", () => {
|
|
const tokens = computeFormOptionGroupEditorTokens({
|
|
...baseRadio,
|
|
textColorCustom: "#ff0000",
|
|
fillColorCustom: "#123456",
|
|
strokeColorCustom: "#654321",
|
|
widthMode: "fixed",
|
|
widthPx: 280,
|
|
borderRadius: "lg",
|
|
paddingX: 8,
|
|
paddingY: 4,
|
|
fontSizeCustom: "15px",
|
|
lineHeightCustom: "22px",
|
|
letterSpacingCustom: "0.5px",
|
|
} as any);
|
|
|
|
expect(tokens.fieldStyle.color).toBe("#ff0000");
|
|
expect(tokens.fieldStyle.backgroundColor).toBe("#123456");
|
|
expect(tokens.fieldStyle.borderColor).toBe("#654321");
|
|
expect(tokens.fieldStyle.width).toBe("280px");
|
|
expect(tokens.fieldStyle.borderRadius).toBe(9999);
|
|
expect(tokens.fieldStyle.paddingInline).toBe("8px");
|
|
expect(tokens.fieldStyle.paddingBlock).toBe("4px");
|
|
expect(tokens.fieldStyle.fontSize).toBe("15px");
|
|
expect(tokens.fieldStyle.lineHeight).toBe("22px");
|
|
expect(tokens.fieldStyle.letterSpacing).toBe("0.5px");
|
|
});
|
|
});
|
|
|
|
describe("formHelpers - public tokens", () => {
|
|
it("computeFormInputPublicTokens: 퍼블릭 인풋 필드 스타일을 계산해야 한다", () => {
|
|
const tokens = computeFormInputPublicTokens({
|
|
...baseInput,
|
|
align: "center",
|
|
widthMode: "fixed",
|
|
widthPx: 320,
|
|
paddingX: 16,
|
|
paddingY: 8,
|
|
textColorCustom: "#112233",
|
|
fillColorCustom: "#123456",
|
|
strokeColorCustom: "#ff0000",
|
|
borderRadius: "lg",
|
|
} as any);
|
|
|
|
expect(tokens.wrapperLayoutClass).toBe("flex flex-col gap-1");
|
|
expect(tokens.widthClass).toBe("");
|
|
expect(tokens.inputStyle.textAlign).toBe("center");
|
|
expect(tokens.inputStyle.width).toBe("20em");
|
|
expect(tokens.inputStyle.paddingInline).toBe("1em");
|
|
expect(tokens.inputStyle.paddingBlock).toBe("0.5em");
|
|
expect(tokens.inputStyle.color).toBe("#112233");
|
|
expect(tokens.inputStyle.backgroundColor).toBe("#123456");
|
|
expect(tokens.inputStyle.borderColor).toBe("#ff0000");
|
|
expect(tokens.inputStyle.borderRadius).toBe("6px");
|
|
|
|
// 퍼블릭 인풋 토큰 스타일에는 borderRadius/borderWidth 를 제외하고 px 단위가 없어야 한다.
|
|
expectNoPxInStyleObject(tokens.wrapperStyle);
|
|
expectNoPxInStyleObject(tokens.inputStyle, { allowKeys: ["borderRadius", "borderWidth"] });
|
|
});
|
|
|
|
it("computeFormSelectPublicTokens: 퍼블릭 셀렉트 필드 스타일을 계산해야 한다", () => {
|
|
const tokens = computeFormSelectPublicTokens({
|
|
...baseSelect,
|
|
widthMode: "fixed",
|
|
widthPx: 240,
|
|
paddingX: 16,
|
|
paddingY: 8,
|
|
textColorCustom: "#00ff00",
|
|
fillColorCustom: "#123456",
|
|
strokeColorCustom: "#ff0000",
|
|
borderRadius: "lg",
|
|
} as any);
|
|
|
|
expect(tokens.widthClass).toBe("");
|
|
expect(tokens.wrapperStyle.width).toBe("15em");
|
|
expect(tokens.selectStyle.paddingInline).toBe("1em");
|
|
expect(tokens.selectStyle.paddingBlock).toBe("0.5em");
|
|
expect(tokens.selectStyle.color).toBe("#00ff00");
|
|
expect(tokens.selectStyle.backgroundColor).toBe("#123456");
|
|
expect(tokens.selectStyle.borderColor).toBe("#ff0000");
|
|
expect(tokens.selectStyle.borderRadius).toBe("6px");
|
|
|
|
// 퍼블릭 셀렉트 토큰 스타일에는 borderRadius/borderWidth 를 제외하고 px 단위가 없어야 한다.
|
|
expectNoPxInStyleObject(tokens.wrapperStyle);
|
|
expectNoPxInStyleObject(tokens.selectStyle, { allowKeys: ["borderRadius", "borderWidth"] });
|
|
});
|
|
|
|
it("computeFormCheckboxPublicTokens: 퍼블릭 체크박스 그룹 스타일을 계산해야 한다", () => {
|
|
const tokens = computeFormCheckboxPublicTokens({
|
|
...baseCheckbox,
|
|
widthMode: "fixed",
|
|
widthPx: 320,
|
|
paddingX: 8,
|
|
paddingY: 4,
|
|
optionGapPx: 16,
|
|
textColorCustom: "#ffffff",
|
|
fillColorCustom: "#123456",
|
|
strokeColorCustom: "#ff0000",
|
|
borderRadius: "lg",
|
|
} as any);
|
|
|
|
expect(tokens.widthClass).toBe("");
|
|
expect(tokens.groupStyle.width).toBe("20em");
|
|
expect(tokens.optionsStyle.rowGap).toBe("1em");
|
|
expect(tokens.optionContainerStyle.paddingInline).toBe("0.5em");
|
|
expect(tokens.optionContainerStyle.paddingBlock).toBe("0.25em");
|
|
expect(tokens.optionContainerStyle.backgroundColor).toBe("#123456");
|
|
expect(tokens.optionContainerStyle.borderColor).toBe("#ff0000");
|
|
expect(tokens.optionContainerStyle.borderWidth).toBe("1px");
|
|
expect(tokens.optionContainerStyle.borderStyle).toBe("solid");
|
|
expect(tokens.optionContainerStyle.borderRadius).toBe("6px");
|
|
expect(tokens.groupTextStyle.color).toBe("#ffffff");
|
|
expect(tokens.optionTextStyle.color).toBe("#ffffff");
|
|
|
|
// 퍼블릭 체크박스 토큰 스타일에는 borderRadius/borderWidth 를 제외하고 px 단위가 없어야 한다.
|
|
expectNoPxInStyleObject(tokens.groupStyle);
|
|
expectNoPxInStyleObject(tokens.optionsStyle);
|
|
expectNoPxInStyleObject(tokens.optionContainerStyle, { allowKeys: ["borderRadius", "borderWidth"] });
|
|
expectNoPxInStyleObject(tokens.groupTextStyle);
|
|
expectNoPxInStyleObject(tokens.optionTextStyle);
|
|
});
|
|
|
|
it("computeFormRadioPublicTokens: 퍼블릭 라디오 그룹 스타일을 계산해야 한다", () => {
|
|
const tokens = computeFormRadioPublicTokens({
|
|
...baseRadio,
|
|
widthMode: "fixed",
|
|
widthPx: 320,
|
|
paddingX: 8,
|
|
paddingY: 4,
|
|
optionGapPx: 16,
|
|
textColorCustom: "#ffffff",
|
|
fillColorCustom: "#123456",
|
|
strokeColorCustom: "#ff0000",
|
|
borderRadius: "lg",
|
|
} as any);
|
|
|
|
expect(tokens.widthClass).toBe("");
|
|
expect(tokens.groupStyle.width).toBe("20em");
|
|
expect(tokens.optionsStyle.rowGap).toBe("1em");
|
|
expect(tokens.optionContainerStyle.paddingInline).toBe("0.5em");
|
|
expect(tokens.optionContainerStyle.paddingBlock).toBe("0.25em");
|
|
expect(tokens.optionContainerStyle.backgroundColor).toBe("#123456");
|
|
expect(tokens.optionContainerStyle.borderColor).toBe("#ff0000");
|
|
expect(tokens.optionContainerStyle.borderWidth).toBe("1px");
|
|
expect(tokens.optionContainerStyle.borderStyle).toBe("solid");
|
|
expect(tokens.optionContainerStyle.borderRadius).toBe("6px");
|
|
expect(tokens.groupTextStyle.color).toBe("#ffffff");
|
|
expect(tokens.optionTextStyle.color).toBe("#ffffff");
|
|
});
|
|
});
|
|
|
|
describe("formHelpers.computeFormControllerPublicTokens", () => {
|
|
it("fieldIds 를 기반으로 컨트롤러 필드를 매핑하고 레이아웃/submitLabel 을 계산해야 한다", () => {
|
|
const formBlock: Block = {
|
|
id: "form-1",
|
|
type: "form",
|
|
props: {
|
|
kind: "contact",
|
|
submitTarget: "internal",
|
|
fieldIds: ["input-1", "checkbox-1"],
|
|
submitButtonId: "btn-1",
|
|
formWidthMode: "fixed",
|
|
formWidthPx: 320,
|
|
marginYPx: 32,
|
|
backgroundColorCustom: " #123456 ",
|
|
} as FormBlockProps,
|
|
} as Block;
|
|
|
|
const inputBlock: Block = {
|
|
id: "input-1",
|
|
type: "formInput",
|
|
props: {
|
|
label: "이름",
|
|
formFieldName: "name",
|
|
inputType: "email",
|
|
required: true,
|
|
} as any,
|
|
} as Block;
|
|
|
|
const checkboxBlock: Block = {
|
|
id: "checkbox-1",
|
|
type: "formCheckbox",
|
|
props: {
|
|
groupLabel: "옵션",
|
|
formFieldName: "options",
|
|
required: true,
|
|
groupLabelMode: "image",
|
|
groupLabelImageUrl: "https://example.com/group.png",
|
|
options: [
|
|
{ label: "A", value: "a" },
|
|
{ label: "B", value: "b" },
|
|
],
|
|
} as any,
|
|
} as Block;
|
|
|
|
const buttonBlock: Block = {
|
|
id: "btn-1",
|
|
type: "button",
|
|
props: {
|
|
label: "컨트롤러 버튼",
|
|
} as any,
|
|
} as Block;
|
|
|
|
const blocks: Block[] = [formBlock, inputBlock, checkboxBlock, buttonBlock];
|
|
|
|
const tokens = computeFormControllerPublicTokens(formBlock, blocks);
|
|
|
|
expect(tokens.fields).toHaveLength(2);
|
|
const inputField = tokens.fields[0];
|
|
const checkboxField = tokens.fields[1];
|
|
|
|
expect(inputField.id).toBe("input-1");
|
|
expect(inputField.name).toBe("name");
|
|
expect(inputField.label).toBe("이름");
|
|
expect(inputField.type).toBe("text");
|
|
expect(inputField.required).toBe(true);
|
|
|
|
expect(checkboxField.id).toBe("checkbox-1");
|
|
expect(checkboxField.name).toBe("options");
|
|
expect(checkboxField.label).toBe("옵션");
|
|
expect(checkboxField.type).toBe("checkbox");
|
|
expect(checkboxField.required).toBe(true);
|
|
expect(checkboxField.options).toHaveLength(2);
|
|
expect(checkboxField.groupLabelMode).toBe("image");
|
|
expect(checkboxField.groupLabelImageUrl).toBe("https://example.com/group.png");
|
|
|
|
expect(tokens.formClassName).toBe("space-y-3");
|
|
// FormBlock 은 레이아웃 정보는 가지지 않지만, backgroundColorCustom 으로 폼 래퍼 배경색만 제어할 수 있다.
|
|
expect(tokens.formStyle).toEqual({ backgroundColor: "#123456" });
|
|
|
|
expect(tokens.submitLabel).toBe("컨트롤러 버튼");
|
|
});
|
|
|
|
it("fields 가 있을 때는 fields 를 사용하고, 없으면 빈 배열을 반환해야 한다", () => {
|
|
const formWithFields: Block = {
|
|
id: "form-2",
|
|
type: "form",
|
|
props: {
|
|
kind: "contact",
|
|
submitTarget: "internal",
|
|
fields: [
|
|
{
|
|
id: "f1",
|
|
name: "email",
|
|
label: "이메일",
|
|
type: "email",
|
|
required: true,
|
|
},
|
|
],
|
|
} as FormBlockProps,
|
|
} as Block;
|
|
|
|
const tokensWithFields = computeFormControllerPublicTokens(formWithFields, [formWithFields]);
|
|
|
|
expect(tokensWithFields.fields).toHaveLength(1);
|
|
expect(tokensWithFields.fields[0].id).toBe("f1");
|
|
expect(tokensWithFields.fields[0].name).toBe("email");
|
|
expect(tokensWithFields.fields[0].label).toBe("이메일");
|
|
expect(tokensWithFields.fields[0].type).toBe("email");
|
|
expect(tokensWithFields.fields[0].required).toBe(true);
|
|
|
|
const formFallback: Block = {
|
|
id: "form-3",
|
|
type: "form",
|
|
props: {
|
|
kind: "contact",
|
|
submitTarget: "internal",
|
|
} as FormBlockProps,
|
|
} as Block;
|
|
|
|
const tokensFallback = computeFormControllerPublicTokens(formFallback, [formFallback]);
|
|
|
|
expect(tokensFallback.fields).toHaveLength(0);
|
|
});
|
|
});
|
|
|
|
describe("formHelpers.computeFormBlockExportTokens", () => {
|
|
it("fieldIds 가 있을 때 컨트롤러 필드와 폼 배경 스타일을 계산해야 한다", () => {
|
|
const formBlock: Block = {
|
|
id: "form-export-1",
|
|
type: "form",
|
|
props: {
|
|
kind: "contact",
|
|
submitTarget: "internal",
|
|
fieldIds: ["input-1", "select-1"],
|
|
fields: [],
|
|
backgroundColorCustom: " #123456 ",
|
|
} as FormBlockProps,
|
|
} as Block;
|
|
|
|
const inputBlock: Block = {
|
|
id: "input-1",
|
|
type: "formInput",
|
|
props: {
|
|
label: "이름",
|
|
formFieldName: "name",
|
|
inputType: "email",
|
|
required: true,
|
|
} as any,
|
|
} as Block;
|
|
|
|
const selectBlock: Block = {
|
|
id: "select-1",
|
|
type: "formSelect",
|
|
props: {
|
|
label: "플랜",
|
|
formFieldName: "plan",
|
|
options: [
|
|
{ label: "Basic", value: "basic" },
|
|
{ label: "Pro", value: "pro" },
|
|
],
|
|
} as any,
|
|
} as Block;
|
|
|
|
const blocks: Block[] = [formBlock, inputBlock, selectBlock];
|
|
|
|
const tokens = computeFormBlockExportTokens(formBlock, blocks);
|
|
|
|
expect(tokens.hasControllerFields).toBe(true);
|
|
expect(tokens.controllerFields).toHaveLength(2);
|
|
expect(tokens.controllerFields[0].id).toBe("input-1");
|
|
expect(tokens.controllerFields[1].id).toBe("select-1");
|
|
expect(tokens.fallbackFields).toHaveLength(0);
|
|
// FormBlock 은 Export 레이어에서도 컨테이너 배경/레이아웃 스타일을 가지지 않는다.
|
|
expect(tokens.formStyleParts).toEqual([]);
|
|
});
|
|
|
|
it("fieldIds 가 없고 fields 가 있을 때 fallbackFields 를 사용해야 한다", () => {
|
|
const formBlock: Block = {
|
|
id: "form-export-2",
|
|
type: "form",
|
|
props: {
|
|
kind: "contact",
|
|
submitTarget: "internal",
|
|
fieldIds: [],
|
|
fields: [
|
|
{
|
|
id: "f1",
|
|
name: "email",
|
|
label: "이메일",
|
|
type: "email",
|
|
required: true,
|
|
},
|
|
],
|
|
backgroundColorCustom: undefined,
|
|
} as FormBlockProps,
|
|
} as Block;
|
|
|
|
const tokens = computeFormBlockExportTokens(formBlock, [formBlock]);
|
|
|
|
expect(tokens.hasControllerFields).toBe(false);
|
|
expect(tokens.controllerFields).toHaveLength(0);
|
|
expect(tokens.fallbackFields).toHaveLength(1);
|
|
expect(tokens.fallbackFields[0].id).toBe("f1");
|
|
expect(tokens.formStyleParts).toEqual([]);
|
|
});
|
|
|
|
it("fieldIds 와 fields 가 모두 없으면 fallbackFields 도 빈 배열이어야 한다", () => {
|
|
const formBlock: Block = {
|
|
id: "form-export-3",
|
|
type: "form",
|
|
props: {
|
|
kind: "contact",
|
|
submitTarget: "internal",
|
|
} as FormBlockProps,
|
|
} as Block;
|
|
|
|
const tokens = computeFormBlockExportTokens(formBlock, [formBlock]);
|
|
|
|
expect(tokens.hasControllerFields).toBe(false);
|
|
expect(tokens.controllerFields).toHaveLength(0);
|
|
expect(tokens.fallbackFields).toHaveLength(0);
|
|
});
|
|
});
|