import { describe, it, expect } from "vitest"; import { expectNoPxInStyleParts } from "./styleInvariants"; import { computeButtonPbTokens, computeButtonPublicTokens, computeButtonEditorTokens, } from "@/features/editor/utils/buttonHelpers"; import type { ButtonBlockProps } from "@/features/editor/state/editorStore"; const baseProps = { align: "left" as const, size: "md" as const, variant: "solid" as const, colorPalette: "primary" as const, }; describe("buttonHelpers.computeButtonPbTokens", () => { it("기본 버튼은 pb-text-left / pb-btn-size-md / pb-btn-variant-solid-primary / pb-btn-radius-md 를 포함해야 한다", () => { const tokens = computeButtonPbTokens(baseProps); expect(tokens.alignClass).toBe("pb-text-left"); expect(tokens.sizeClass).toBe("pb-btn-size-md"); expect(tokens.variantClass).toBe("pb-btn-variant-solid-primary"); expect(tokens.radiusClass).toBe("pb-btn-radius-md"); expect(tokens.inlineStyles.length).toBe(0); }); it("정렬/사이즈/팔레트/둥글기 스케일은 pb 토큰으로 올바르게 매핑되어야 한다", () => { const tokens = computeButtonPbTokens({ align: "center", size: "lg", variant: "outline", colorPalette: "danger", borderRadius: "full", }); expect(tokens.alignClass).toBe("pb-text-center"); expect(tokens.sizeClass).toBe("pb-btn-size-lg"); expect(tokens.variantClass).toBe("pb-btn-variant-outline-danger"); expect(tokens.radiusClass).toBe("pb-btn-radius-full"); }); it("widthMode=fixed, widthPx, paddingX/paddingY, fill/stroke/text 색상은 inlineStyles 에 포함되어야 한다", () => { const tokens = computeButtonPbTokens({ align: "right", size: "sm", widthMode: "fixed", widthPx: 200, paddingX: 16, paddingY: 8, fillColorCustom: "#111111", strokeColorCustom: "#222222", textColorCustom: "#ffffff", }); expect(tokens.alignClass).toBe("pb-text-right"); expect(tokens.inlineStyles).toContain("width:12.5em"); expect(tokens.inlineStyles).toContain("padding-left:1em"); expect(tokens.inlineStyles).toContain("padding-right:1em"); expect(tokens.inlineStyles).toContain("padding-top:0.5em"); expect(tokens.inlineStyles).toContain("padding-bottom:0.5em"); expect(tokens.inlineStyles).toContain("background-color:#111111"); expect(tokens.inlineStyles).toContain("border-color:#222222"); expect(tokens.inlineStyles).toContain("color:#ffffff"); }); it("Button Pb inlineStyles 에는 px 단위 값이 포함되지 않아야 한다", () => { const tokens = computeButtonPbTokens({ align: "right", size: "sm", widthMode: "fixed", widthPx: 200, paddingX: 16, paddingY: 8, fillColorCustom: "#111111", strokeColorCustom: "#222222", textColorCustom: "#ffffff", }); expectNoPxInStyleParts(tokens.inlineStyles); }); }); describe("buttonHelpers.computeButtonPublicTokens", () => { it("색상 커스텀 값은 backgroundColor/borderColor/borderWidth/borderStyle/color 에 반영되어야 한다", () => { const tokens = computeButtonPublicTokens({ label: "컬러 버튼", href: "#", align: "center", fillColorCustom: "#123456", strokeColorCustom: "#ff0000", textColorCustom: "#00ff00", } as ButtonBlockProps); expect(tokens.alignClass).toBe("text-center"); expect(tokens.styleOverrides.backgroundColor).toBe("#123456"); expect(tokens.styleOverrides.borderColor).toBe("#ff0000"); expect(tokens.styleOverrides.borderWidth).toBe("1px"); expect(tokens.styleOverrides.borderStyle).toBe("solid"); expect(tokens.styleOverrides.color).toBe("#00ff00"); }); it("widthMode=fixed 인 경우 widthPx/paddingX/paddingY 는 em 단위로 변환되어야 한다", () => { const tokens = computeButtonPublicTokens({ label: "레이아웃 버튼", href: "#", align: "left", widthMode: "fixed", widthPx: 320, paddingX: 16, paddingY: 8, } as ButtonBlockProps); expect(tokens.alignClass).toBe("text-left"); expect(tokens.styleOverrides.width).toBe("20em"); expect(tokens.styleOverrides.paddingInline).toBe("1em"); expect(tokens.styleOverrides.paddingBlock).toBe("0.5em"); }); it("borderRadius 토큰은 em 단위 둥글기 또는 9999px 로 변환되어야 한다", () => { const lgTokens = computeButtonPublicTokens({ label: "둥근 버튼", href: "#", borderRadius: "lg", } as ButtonBlockProps); expect(lgTokens.styleOverrides.borderRadius).toBe("0.75em"); const fullTokens = computeButtonPublicTokens({ label: "풀 둥근 버튼", href: "#", borderRadius: "full", } as ButtonBlockProps); expect(fullTokens.styleOverrides.borderRadius).toBe("9999px"); }); }); describe("buttonHelpers.computeButtonEditorTokens", () => { it("fullWidth 나 widthMode 에 따라 wrapperWidthClass 와 버튼 스타일이 계산되어야 한다", () => { const tokens = computeButtonEditorTokens({ label: "테스트 버튼", href: "#", align: "center", size: "lg", variant: "outline", colorPalette: "success", borderRadius: "full", fullWidth: true, fontSizeCustom: "18px", lineHeightCustom: "1.8", letterSpacingCustom: "0.1em", fillColorCustom: "#123456", strokeColorCustom: "#654321", textColorCustom: "#ff0000", paddingX: 16, paddingY: 10, } as ButtonBlockProps); // fullWidth=true 이므로 wrapperWidthClass 는 w-full 이어야 한다. expect(tokens.wrapperWidthClass).toBe("w-full"); // 버튼 스타일에 색상/패딩/타이포 값이 그대로 반영되어야 한다. expect(tokens.buttonStyle.backgroundColor).toBe("#123456"); expect(tokens.buttonStyle.borderColor).toBe("#654321"); expect(tokens.buttonStyle.color).toBe("#ff0000"); expect(tokens.buttonStyle.paddingInline).toBe("16px"); expect(tokens.buttonStyle.paddingBlock).toBe("10px"); expect(tokens.buttonStyle.fontSize).toBe("18px"); expect(tokens.buttonStyle.lineHeight).toBe("1.8"); expect(tokens.buttonStyle.letterSpacing).toBe("0.1em"); }); it("widthMode=fixed 인 경우 widthPx 는 버튼 width 스타일에 반영되고 wrapperWidthClass 는 inline-block 이어야 한다", () => { const tokens = computeButtonEditorTokens({ label: "고정 너비 버튼", href: "#", align: "left", size: "md", variant: "solid", colorPalette: "primary", borderRadius: "md", fullWidth: false, widthMode: "fixed", widthPx: 200, } as ButtonBlockProps); expect(tokens.wrapperWidthClass).toBe("inline-block"); expect(tokens.buttonStyle.width).toBe("200px"); }); });