import { describe, it, expect } from "vitest"; import { expectNoPxInStyleObject, expectNoPxInStyleParts } from "./styleInvariants"; import { computeTextPbTokens, computeTextEditorTokens, computeTextPublicTokens, } from "@/features/editor/utils/textHelpers"; import type { TextBlockProps } from "@/features/editor/state/editorStore"; const baseProps = { text: "텍스트", align: "left" as const, size: "base" as const, }; describe("textHelpers.computeTextPbTokens", () => { it("기본 텍스트는 pb-text-left / pb-text-base / pb-leading-normal / pb-font-normal / pb-text-color-strong / pb-whitespace-pre-wrap 를 포함해야 한다", () => { const tokens = computeTextPbTokens(baseProps); expect(tokens.alignClass).toBe("pb-text-left"); expect(tokens.sizeClass).toBe("pb-text-base"); expect(tokens.leadingClass).toBe("pb-leading-normal"); expect(tokens.weightClass).toBe("pb-font-normal"); expect(tokens.colorClass).toBe("pb-text-color-strong"); expect(tokens.extraClasses).toContain("pb-whitespace-pre-wrap"); }); it("스케일 모드 텍스트는 폰트/라인하이트/굵기 스케일 토큰을 올바르게 매핑해야 한다", () => { const tokens = computeTextPbTokens({ ...baseProps, align: "center", fontSizeMode: "scale", fontSizeScale: "lg", lineHeightMode: "scale", lineHeightScale: "relaxed", fontWeightMode: "scale", fontWeightScale: "semibold", colorMode: "palette", colorPalette: "strong", underline: true, italic: true, }); expect(tokens.alignClass).toBe("pb-text-center"); expect(tokens.sizeClass).toBe("pb-text-lg"); expect(tokens.leadingClass).toBe("pb-leading-relaxed"); expect(tokens.weightClass).toBe("pb-font-semibold"); expect(tokens.colorClass).toBe("pb-text-color-strong"); expect(tokens.extraClasses).toContain("pb-underline"); expect(tokens.extraClasses).toContain("pb-italic"); }); it("커스텀 색상과 배경색은 inlineStyles 에 color/background-color 로 포함되어야 한다", () => { const tokens = computeTextPbTokens({ ...baseProps, colorMode: "custom", colorCustom: "#ff0000", backgroundColorCustom: "#123456", }); expect(tokens.inlineStyles).toContain("color:#ff0000"); expect(tokens.inlineStyles).toContain("background-color:#123456"); }); it("maxWidthScale 가 prose/narrow 인 경우 pb-text-maxw-* 클래스를 포함해야 한다", () => { const prose = computeTextPbTokens({ ...baseProps, maxWidthMode: "scale", maxWidthScale: "prose" }); const narrow = computeTextPbTokens({ ...baseProps, maxWidthMode: "scale", maxWidthScale: "narrow" }); expect(prose.maxWidthClass).toBe("pb-text-maxw-prose"); expect(narrow.maxWidthClass).toBe("pb-text-maxw-narrow"); }); it("텍스트 Pb inlineStyles 에는 px 단위 값이 포함되지 않아야 한다", () => { const tokens = computeTextPbTokens({ ...baseProps, colorMode: "custom", colorCustom: "#ff0000", backgroundColorCustom: "#123456", }); expectNoPxInStyleParts(tokens.inlineStyles); }); }); describe("textHelpers.computeTextEditorTokens", () => { const baseBlockProps: TextBlockProps = { text: "텍스트", align: "left", size: "base", } as TextBlockProps; it("기본 텍스트는 pb-text-left / pb-text-base / pb-leading-normal / pb-font-normal / pb-text-color-default 를 사용해야 한다", () => { const tokens = computeTextEditorTokens(baseBlockProps); expect(tokens.alignClass).toBe("pb-text-left"); expect(tokens.sizeClass).toBe("pb-text-base"); expect(tokens.leadingClass).toBe("pb-leading-normal"); expect(tokens.weightClass).toBe("pb-font-normal"); expect(tokens.colorClass).toBe("pb-text-color-default"); expect(tokens.styleOverrides.color).toBeUndefined(); }); it("colorCustom / backgroundColorCustom / maxWidthCustom 이 styleOverrides 에 반영되어야 한다", () => { const tokens = computeTextEditorTokens({ ...baseBlockProps, align: "center", colorCustom: "#ff0000", backgroundColorCustom: "#123456", maxWidthCustom: "320px", } as TextBlockProps); expect(tokens.alignClass).toBe("pb-text-center"); expect(tokens.styleOverrides.color).toBe("#ff0000"); expect(tokens.styleOverrides.backgroundColor).toBe("#123456"); expect(tokens.styleOverrides.maxWidth).toBe("320px"); }); }); describe("textHelpers.computeTextPublicTokens", () => { const baseBlockProps: TextBlockProps = { text: "텍스트", align: "left", size: "base", } as TextBlockProps; it("기본 텍스트는 text-left / text-base 를 사용하고 색상/배경 styleOverrides 는 없어야 한다", () => { const tokens = computeTextPublicTokens(baseBlockProps); expect(tokens.alignClass).toBe("text-left"); expect(tokens.sizeClass).toBe("text-base"); expect(tokens.styleOverrides.color).toBeUndefined(); expect(tokens.styleOverrides.backgroundColor).toBeUndefined(); }); it("fontSizeMode=custom 인 경우 sizeClass 는 비워지고 styleOverrides.fontSize 에 값이 설정되어야 한다", () => { const tokens = computeTextPublicTokens({ ...baseBlockProps, fontSizeMode: "custom", fontSizeCustom: "24px", } as TextBlockProps); expect(tokens.sizeClass).toBe(""); expect(tokens.styleOverrides.fontSize).toBe("1.5em"); }); it("colorCustom / backgroundColorCustom 이 styleOverrides 에 반영되어야 한다", () => { const tokens = computeTextPublicTokens({ ...baseBlockProps, colorCustom: "#ff0000", backgroundColorCustom: "#123456", } as TextBlockProps); expect(tokens.styleOverrides.color).toBe("#ff0000"); expect(tokens.styleOverrides.backgroundColor).toBe("#123456"); }); it("lineHeightCustom / letterSpacingCustom \uc774 px \uc778 \uacbd\uc6b0 em \ub2e8\uc704 \uc2a4\ud0c0\uc77c\ub85c \ubcc0\ud658\ub418\uc5b4\uc57c \ud55c\ub2e4", () => { const tokens = computeTextPublicTokens({ ...baseBlockProps, lineHeightCustom: "24px", letterSpacingCustom: "2px", } as TextBlockProps); expect(tokens.styleOverrides.lineHeight).toBe("1.5em"); expect(tokens.styleOverrides.letterSpacing).toBe("0.125em"); }); it("퍼블릭 텍스트 styleOverrides 에 px 단위 값이 포함되지 않아야 한다", () => { const tokens = computeTextPublicTokens({ ...baseBlockProps, fontSizeMode: "custom", fontSizeCustom: "24px", lineHeightCustom: "24px", letterSpacingCustom: "2px", colorCustom: "#ff0000", backgroundColorCustom: "#123456", } as TextBlockProps); expectNoPxInStyleObject(tokens.styleOverrides); }); });