import { describe, it, expect } from "vitest"; import { expectNoPxInStyleObject, expectNoPxInStyleString } from "./styleInvariants"; import { computeDividerExportTokens, computeDividerEditorTokens, computeDividerPublicTokens, } from "@/features/editor/utils/dividerHelpers"; import type { DividerBlockProps } from "@/features/editor/state/editorStore"; const baseProps: DividerBlockProps = { align: "left", thickness: "thin", colorHex: undefined, marginYPx: undefined, }; describe("dividerHelpers.computeDividerExportTokens", () => { const escapers = { escapeAttr: (v: string) => v }; it("기본 값은 1px 두께, 기본 색상(#475569), marginY=1em 이어야 한다", () => { const tokens = computeDividerExportTokens(baseProps, escapers); expect(tokens.style).toBe("border:0;border-bottom:1px solid #475569;margin:1em 0;"); }); it("thickness=\"medium\" 인 경우 2px 두께로 렌더링되어야 한다", () => { const tokens = computeDividerExportTokens({ ...baseProps, thickness: "medium" }, escapers); expect(tokens.style).toContain("border-bottom:2px solid #475569;"); }); it("colorHex 가 설정되면 공백을 제거한 뒤 해당 색상으로 border-color 를 사용해야 한다", () => { const tokens = computeDividerExportTokens({ ...baseProps, colorHex: " #ff0000 " }, escapers); expect(tokens.style).toContain("border-bottom:1px solid #ff0000;"); }); it("marginYPx > 0 이면 해당 값을 margin Y 로 사용해야 한다", () => { const tokens = computeDividerExportTokens({ ...baseProps, marginYPx: 24 }, escapers); expect(tokens.style).toContain("margin:1.5em 0;"); }); it("marginYPx 가 0 이하이거나 숫자가 아니면 기본 1em 을 사용해야 한다", () => { const zeroTokens = computeDividerExportTokens({ ...baseProps, marginYPx: 0 }, escapers); expect(zeroTokens.style).toContain("margin:1em 0;"); }); it("widthMode=\"fixed\" 인 경우 widthPx 는 em 단위 width 로 Export style 에 반영되어야 한다", () => { const tokens = computeDividerExportTokens( { ...baseProps, align: "center", widthMode: "fixed", widthPx: 480, }, escapers, ); expect(tokens.style).toContain("width:30em"); expect(tokens.style).toContain("margin-left:auto"); expect(tokens.style).toContain("margin-right:auto"); }); it("Export 구분선 style 에서는 border-bottom 두께를 제외하고 px 단위가 없어야 한다", () => { const tokens = computeDividerExportTokens({ ...baseProps, thickness: "medium", colorHex: "#123456", marginYPx: 24, }, escapers); expectNoPxInStyleString(tokens.style, { allowPatterns: [/border-bottom:\d+px/], }); }); }); describe("dividerHelpers.computeDividerEditorTokens", () => { it("기본 값은 얇은 두께, 기본 색상, 전체 폭, margin 16px 이어야 한다", () => { const tokens = computeDividerEditorTokens(baseProps); expect(tokens.thicknessClass).toBe("border-t"); expect(tokens.borderColor).toBe("#475569"); expect(tokens.innerWidthClass).toBe("w-full"); expect(tokens.widthPx).toBeUndefined(); expect(tokens.marginPx).toBe(16); }); it("thickness=\"medium\", colorHex, widthMode=\"auto\", marginYPx 가 있으면 그대로 반영되어야 한다", () => { const tokens = computeDividerEditorTokens({ ...baseProps, thickness: "medium", colorHex: " #ff0000 ", widthMode: "auto", marginYPx: 20, }); expect(tokens.thicknessClass).toBe("border-t-2"); expect(tokens.borderColor).toBe("#ff0000"); expect(tokens.innerWidthClass).toBe("w-1/2"); expect(tokens.widthPx).toBeUndefined(); expect(tokens.marginPx).toBe(20); }); it("widthMode=\"fixed\" 이고 widthPx 가 양수이면 해당 px 로 width 를 사용해야 한다", () => { const tokens = computeDividerEditorTokens({ ...baseProps, widthMode: "fixed", widthPx: 480, marginY: "lg", }); expect(tokens.innerWidthClass).toBe(""); expect(tokens.widthPx).toBe(480); expect(tokens.marginPx).toBe(24); }); it("widthMode=\"fixed\" 이지만 widthPx 가 없거나 0 이하이면 기본 320px 을 사용해야 한다", () => { const tokens = computeDividerEditorTokens({ ...baseProps, widthMode: "fixed", widthPx: undefined, }); expect(tokens.innerWidthClass).toBe(""); expect(tokens.widthPx).toBe(320); }); }); describe("dividerHelpers.computeDividerPublicTokens", () => { it("colorHex 가 있으면 해당 색상을, 없으면 기본 색상(#475569)을 사용해야 한다", () => { const withColor = computeDividerPublicTokens({ thickness: "thin", colorHex: "#123456", } as DividerBlockProps); expect(withColor.lineStyle.backgroundColor).toBe("#123456"); const withoutColor = computeDividerPublicTokens({ thickness: "thin", } as DividerBlockProps); expect(withoutColor.lineStyle.backgroundColor).toBe("#475569"); }); it("marginYPx 와 marginY 토큰은 em 스케일 wrapperMarginEm 으로 변환되어야 한다", () => { const withMarginPx = computeDividerPublicTokens({ thickness: "thin", marginYPx: 32, } as DividerBlockProps); expect(withMarginPx.wrapperMarginEm).toBe("2em"); const withMarginToken = computeDividerPublicTokens({ thickness: "thin", marginY: "lg", } as DividerBlockProps); expect(withMarginToken.wrapperMarginEm).toBe("1.5em"); }); it("widthMode=fixed 인 경우 widthPx 는 라인 width 에 em 으로 반영되어야 한다", () => { const tokens = computeDividerPublicTokens({ thickness: "thin", widthMode: "fixed", widthPx: 480, } as DividerBlockProps); expect(tokens.innerWidthClass).toBe(""); expect(tokens.innerStyle.width).toBe("auto"); expect(tokens.lineStyle.width).toBe("30em"); }); it("퍼블릭 구분선 스타일에 px 단위 값이 포함되지 않아야 한다", () => { const tokens = computeDividerPublicTokens({ align: "center", thickness: "medium", colorHex: "#123456", marginYPx: 32, widthMode: "fixed", widthPx: 480, } as DividerBlockProps); expectNoPxInStyleObject(tokens.innerStyle); expectNoPxInStyleObject(tokens.lineStyle); }); });