Files
page-builder/tests/unit/sectionHelpers.spec.ts
jaybe a5b432fb7b
CI / test (push) Failing after 2m54s
CI / e2e (push) Has been skipped
CI / pr_and_merge (push) Has been skipped
TDD,E2E 개선, 미적용 스타일 개선
2025-12-07 09:52:42 +09:00

240 lines
10 KiB
TypeScript

import { describe, it, expect } from "vitest";
import { expectNoPxInStyleObject, expectNoPxInStyleParts } from "./styleInvariants";
import {
computeSectionExportTokens,
computeSectionPublicTokens,
computeSectionEditorTokens,
} from "@/features/editor/utils/sectionHelpers";
import type { SectionBlockProps } from "@/features/editor/state/editorStore";
const baseProps: SectionBlockProps = {
background: "default",
paddingY: "md",
columns: [{ id: "sec_col_1", span: 12 }],
maxWidthMode: "normal",
gapX: "md",
};
describe("sectionHelpers.computeSectionExportTokens", () => {
it("기본 섹션은 pb-section / pb-section-bg-default / pb-section-py-md 를 포함해야 한다", () => {
const tokens = computeSectionExportTokens(baseProps);
expect(tokens.sectionClasses).toContain("pb-section");
expect(tokens.sectionClasses).toContain("pb-section-bg-default");
expect(tokens.sectionClasses).toContain("pb-section-py-md");
expect(tokens.sectionStyleParts.length).toBe(0);
expect(tokens.backgroundVideoHtml).toBe("");
});
it("background=muted/primary 와 paddingY=sm/lg 는 각각 대응하는 pb-section-bg-*/pb-section-py-* 클래스로 매핑되어야 한다", () => {
const muted = computeSectionExportTokens({ ...baseProps, background: "muted" });
const primary = computeSectionExportTokens({ ...baseProps, background: "primary" });
const sm = computeSectionExportTokens({ ...baseProps, paddingY: "sm" });
const lg = computeSectionExportTokens({ ...baseProps, paddingY: "lg" });
expect(muted.sectionClasses).toContain("pb-section-bg-muted");
expect(primary.sectionClasses).toContain("pb-section-bg-primary");
expect(sm.sectionClasses).toContain("pb-section-py-sm");
expect(lg.sectionClasses).toContain("pb-section-py-lg");
});
it("backgroundColorCustom 이 있으면 background-color 스타일을 추가해야 한다", () => {
const tokens = computeSectionExportTokens({
...baseProps,
backgroundColorCustom: "#123456",
});
expect(tokens.sectionStyleParts).toContain("background-color:#123456");
});
it("backgroundImage 관련 옵션이 있으면 background-image/size/position/repeat 스타일을 추가해야 한다", () => {
const tokens = computeSectionExportTokens({
...baseProps,
backgroundImageSrc: "https://example.com/bg.png",
backgroundImageSize: "cover",
backgroundImagePositionMode: "preset",
backgroundImagePosition: "top",
backgroundImageRepeat: "repeat-x",
});
expect(tokens.sectionStyleParts).toContain("background-image:url(https://example.com/bg.png)");
expect(tokens.sectionStyleParts).toContain("background-size:cover");
expect(tokens.sectionStyleParts).toContain("background-position:center top");
expect(tokens.sectionStyleParts).toContain("background-repeat:repeat-x");
});
it("배경 비디오가 있으면 backgroundVideoHtml 과 position/overflow 스타일을 추가해야 한다", () => {
const tokens = computeSectionExportTokens({
...baseProps,
backgroundVideoSrc: "https://example.com/bg.mp4",
});
expect(tokens.backgroundVideoHtml).toContain("pb-section-bg-video");
expect(tokens.backgroundVideoHtml).toContain("https://example.com/bg.mp4");
expect(tokens.sectionStyleParts).toContain("position:relative");
expect(tokens.sectionStyleParts).toContain("overflow:hidden");
});
it("paddingYPx / maxWidthPx / gapXPx 는 Export 토큰 styleParts 에 em 단위로 반영되어야 한다", () => {
const tokens = computeSectionExportTokens({
...baseProps,
backgroundColorCustom: "#123456",
paddingYPx: 80,
maxWidthPx: 960,
gapXPx: 32,
} as SectionBlockProps);
// 섹션 wrapper: 배경색 + 세로 패딩(em)
expect(tokens.sectionStyleParts).toContain("background-color:#123456");
expect(tokens.sectionStyleParts).toContain("padding-top:5em");
expect(tokens.sectionStyleParts).toContain("padding-bottom:5em");
// 내부 래퍼: 최대 폭(em)
expect(tokens.innerWrapperStyleParts).toContain("max-width:60em");
// 컬럼 컨테이너: column-gap(em)
expect(tokens.columnsStyleParts).toContain("column-gap:2em");
// px 단위는 어디에도 포함되면 안 된다.
expectNoPxInStyleParts(tokens.sectionStyleParts);
expectNoPxInStyleParts(tokens.innerWrapperStyleParts);
expectNoPxInStyleParts(tokens.columnsStyleParts);
});
it("Export 섹션 sectionStyleParts 에는 px 단위 값이 포함되지 않아야 한다", () => {
const tokens = computeSectionExportTokens({
...baseProps,
backgroundColorCustom: "#123456",
backgroundImageSrc: "https://example.com/bg.png",
backgroundImageSize: "cover",
backgroundImagePositionMode: "preset",
backgroundImagePosition: "top",
backgroundImageRepeat: "repeat-x",
backgroundVideoSrc: "https://example.com/bg.mp4",
} as SectionBlockProps);
expectNoPxInStyleParts(tokens.sectionStyleParts);
expectNoPxInStyleParts(tokens.innerWrapperStyleParts);
expectNoPxInStyleParts(tokens.columnsStyleParts);
});
});
describe("sectionHelpers.computeSectionPublicTokens", () => {
it("배경색 / paddingYPx / maxWidthPx / gapXPx 를 em 단위 스타일로 계산해야 한다", () => {
const tokens = computeSectionPublicTokens({
...baseProps,
backgroundColorCustom: " #123456 ",
paddingYPx: 32,
maxWidthPx: 800,
gapXPx: 24,
});
expect(tokens.sectionStyle.backgroundColor).toBe("#123456");
expect(tokens.sectionStyle.paddingTop).toBe("2em");
expect(tokens.sectionStyle.paddingBottom).toBe("2em");
expect(tokens.innerWrapperStyle.maxWidth).toBe("50em");
expect(tokens.columnsContainerStyle.columnGap).toBe("1.5em");
// 퍼블릭 섹션 토큰 스타일에는 px 단위 값이 포함되지 않아야 한다.
expectNoPxInStyleObject(tokens.sectionStyle);
expectNoPxInStyleObject(tokens.innerWrapperStyle);
expectNoPxInStyleObject(tokens.columnsContainerStyle);
});
it("backgroundImage* 와 backgroundVideoSrc 를 올바르게 반영해야 한다", () => {
const tokens = computeSectionPublicTokens({
...baseProps,
backgroundImageSrc: "https://example.com/bg.png",
backgroundImageSize: "cover",
backgroundImagePositionMode: "preset",
backgroundImagePosition: "top",
backgroundImageRepeat: "repeat-x",
backgroundVideoSrc: " https://example.com/bg.mp4 ",
} as any);
expect(tokens.sectionStyle.backgroundImage).toBe("url(https://example.com/bg.png)");
expect(tokens.sectionStyle.backgroundSize).toBe("cover");
expect(tokens.sectionStyle.backgroundPosition).toBe("center top");
expect(tokens.sectionStyle.backgroundRepeat).toBe("repeat-x");
expect(tokens.hasBackgroundVideo).toBe(true);
expect(tokens.backgroundVideoSrc).toBe("https://example.com/bg.mp4");
expect(tokens.sectionStyle.position).toBe("relative");
expect(tokens.sectionStyle.overflow).toBe("hidden");
});
});
describe("sectionHelpers.computeSectionEditorTokens", () => {
it("backgroundColorCustom / paddingYPx / maxWidthPx / gapXPx 가 px 단위 스타일로 계산되어야 한다", () => {
const tokens = computeSectionEditorTokens({
...baseProps,
backgroundColorCustom: "#123456",
paddingYPx: 80,
maxWidthPx: 960,
gapXPx: 32,
alignItems: "center",
} as any);
expect(tokens.wrapperStyle.backgroundColor).toBe("#123456");
expect(tokens.wrapperStyle.paddingTop).toBe("80px");
expect(tokens.wrapperStyle.paddingBottom).toBe("80px");
expect(tokens.innerWrapperStyle.maxWidth).toBe("960px");
expect(tokens.columnsContainerStyle.columnGap).toBe("32px");
expect(tokens.alignItemsClass).toBe("items-center");
});
it("background 및 paddingY 값에 따라 bgClass/pyClass 를 계산해야 한다", () => {
const muted = computeSectionEditorTokens({ ...baseProps, background: "muted" } as any);
const primary = computeSectionEditorTokens({ ...baseProps, background: "primary" } as any);
const sm = computeSectionEditorTokens({ ...baseProps, paddingY: "sm" } as any);
const lg = computeSectionEditorTokens({ ...baseProps, paddingY: "lg" } as any);
expect(muted.bgClass).toContain("bg-slate-950/40");
expect(primary.bgClass).toContain("bg-sky-950/40");
expect(primary.bgClass).toContain("border-sky-900/60");
expect(sm.pyClass).toBe("py-4");
expect(lg.pyClass).toBe("py-10");
});
it("backgroundImage* 옵션이 있으면 wrapperStyle 에 backgroundImage/Size/Position/Repeat 이 반영되어야 한다", () => {
const tokens = computeSectionEditorTokens({
...baseProps,
backgroundImageSrc: "https://example.com/bg.png",
backgroundImageSize: "cover",
backgroundImagePositionMode: "preset",
backgroundImagePosition: "top",
backgroundImageRepeat: "repeat-x",
} as any);
expect(tokens.wrapperStyle.backgroundImage).toBe("url(https://example.com/bg.png)");
expect(tokens.wrapperStyle.backgroundSize).toBe("cover");
expect(tokens.wrapperStyle.backgroundPosition).toBe("center top");
expect(tokens.wrapperStyle.backgroundRepeat).toBe("repeat-x");
});
it("backgroundImagePositionMode 가 custom 이고 X/Y 퍼센트가 설정되면 background-position 은 'X% Y%' 로 설정되어야 한다", () => {
const tokens = computeSectionEditorTokens({
...baseProps,
backgroundImageSrc: "https://example.com/bg-xy.png",
backgroundImageSize: "cover",
backgroundImagePositionMode: "custom",
backgroundImagePositionXPercent: 30,
backgroundImagePositionYPercent: 70,
} as any);
expect(tokens.wrapperStyle.backgroundImage).toBe("url(https://example.com/bg-xy.png)");
expect(tokens.wrapperStyle.backgroundPosition).toBe("30% 70%");
});
it("backgroundVideoSrc 가 있으면 hasBackgroundVideo=true, backgroundVideoSrc 와 position/overflow 스타일이 설정되어야 한다", () => {
const tokens = computeSectionEditorTokens({
...baseProps,
backgroundVideoSrc: " https://example.com/bg.mp4 ",
} as any);
expect(tokens.hasBackgroundVideo).toBe(true);
expect(tokens.backgroundVideoSrc).toBe("https://example.com/bg.mp4");
expect(tokens.wrapperStyle.position).toBe("relative");
expect(tokens.wrapperStyle.overflow).toBe("hidden");
});
});