Files
page-builder/tests/unit/PublicPageRendererButtonStyles.spec.tsx
jaybe 6ad731b6e2
CI / test (push) Failing after 13m50s
CI / pr_and_merge (push) Has been cancelled
에디터 정리 및 버그 수정
2025-12-01 13:34:16 +09:00

165 lines
5.5 KiB
TypeScript

import { describe, it, expect, afterEach } from "vitest";
import { render, cleanup } from "@testing-library/react";
import { PublicPageRenderer } from "@/features/editor/components/PublicPageRenderer";
import type { Block } from "@/features/editor/state/editorStore";
// PublicPageRenderer 버튼 스타일 TDD
// - fillColorCustom / strokeColorCustom / textColorCustom
// - widthMode=fixed + widthPx, paddingX/paddingY
// - borderRadius 토큰에 따른 둥글기
describe("PublicPageRenderer - 버튼 블록 스타일", () => {
afterEach(() => {
cleanup();
});
it("버튼 색상 커스텀 값은 배경/외곽선/텍스트 색상 스타일에 반영되어야 한다", () => {
const blocks: Block[] = [
{
id: "btn_colors",
type: "button",
props: {
label: "컬러 버튼",
href: "#",
align: "center",
fillColorCustom: "#123456",
strokeColorCustom: "#ff0000",
textColorCustom: "#00ff00",
} as any,
},
];
const { container } = render(<PublicPageRenderer blocks={blocks} />);
const link = container.querySelector("a") as HTMLAnchorElement | null;
expect(link).not.toBeNull();
const wrapper = link!.parentElement as HTMLElement | null;
expect(wrapper).not.toBeNull();
// 정렬/버튼 베이스는 pb-* 토큰 클래스로 렌더된다.
expect(wrapper!.className).toContain("pb-text-center");
expect(link!.className).toContain("pb-btn-base");
expect(link!.className).toContain("pb-btn-size-md");
expect(link!.className).toContain("pb-btn-variant-solid-primary");
expect(link!.className).toContain("pb-btn-radius-md");
// 배경색: #123456 → rgb(18, 52, 86)
expect(link!.style.backgroundColor).toBe("rgb(18, 52, 86)");
// 외곽선 색상: #ff0000 → rgb(255, 0, 0)
expect(link!.style.borderColor).toBe("rgb(255, 0, 0)");
// strokeColorCustom 이 있을 때 border-width/style 도 설정되어야 한다.
expect(link!.style.borderWidth).toBe("1px");
expect(link!.style.borderStyle).toBe("solid");
// 텍스트 색상: #00ff00 → rgb(0, 255, 0)
expect(link!.style.color).toBe("rgb(0, 255, 0)");
});
it("widthMode=fixed 인 버튼은 widthPx/paddingX/paddingY 가 em 단위 스타일로 반영되어야 한다", () => {
const blocks: Block[] = [
{
id: "btn_layout",
type: "button",
props: {
label: "레이아웃 버튼",
href: "#",
align: "left",
widthMode: "fixed",
widthPx: 320,
paddingX: 16,
paddingY: 8,
} as any,
},
];
const { container } = render(<PublicPageRenderer blocks={blocks} />);
const link = container.querySelector("a") as HTMLAnchorElement | null;
expect(link).not.toBeNull();
// width: 320px / 16 = 20em
expect(link!.style.width).toBe("20em");
// padding-inline: 16px / 16 = 1em
expect(link!.style.paddingInline).toBe("1em");
// padding-block: 8px / 16 = 0.5em
expect(link!.style.paddingBlock).toBe("0.5em");
});
it("borderRadius 토큰은 em 단위 둥글기로 반영되어야 한다", () => {
const blocks: Block[] = [
{
id: "btn_radius",
type: "button",
props: {
label: "둥근 버튼",
href: "#",
borderRadius: "lg",
} as any,
},
];
const { container } = render(<PublicPageRenderer blocks={blocks} />);
const link = container.querySelector("a") as HTMLAnchorElement | null;
expect(link).not.toBeNull();
// lg → 12px → 12 / 16 = 0.75em
expect(link!.style.borderRadius).toBe("0.75em");
});
it("버튼에 imageSrc 가 있으면 이미지와 텍스트를 함께 렌더링해야 한다 (좌측 배치)", () => {
const blocks: Block[] = [
{
id: "btn_img_left",
type: "button",
props: {
label: "이미지 버튼",
href: "#",
imageSrc: "https://example.com/icon.png",
imageAlt: "아이콘",
imagePlacement: "left",
} as any,
},
];
const { container } = render(<PublicPageRenderer blocks={blocks} />);
const link = container.querySelector("a") as HTMLAnchorElement | null;
expect(link).not.toBeNull();
const img = link!.querySelector("img") as HTMLImageElement | null;
expect(img).not.toBeNull();
expect(img!.src).toContain("https://example.com/icon.png");
expect(img!.alt).toBe("아이콘");
const labelNode = Array.from(link!.querySelectorAll("span")).find((el) =>
el.textContent?.includes("이미지 버튼"),
);
expect(labelNode).toBeTruthy();
});
it("imagePlacement 가 top 인 경우 이미지가 텍스트 위쪽에 렌더링되어야 한다", () => {
const blocks: Block[] = [
{
id: "btn_img_top",
type: "button",
props: {
label: "위쪽 이미지 버튼",
href: "#",
imageSrc: "https://example.com/icon-top.png",
imageAlt: "위쪽 아이콘",
imagePlacement: "top",
} as any,
},
];
const { container } = render(<PublicPageRenderer blocks={blocks} />);
const link = container.querySelector("a") as HTMLAnchorElement | null;
expect(link).not.toBeNull();
const wrapper = link!.querySelector("span") as HTMLSpanElement | null;
expect(wrapper).not.toBeNull();
expect(wrapper!.className).toContain("flex-col");
const firstChild = wrapper!.firstElementChild as HTMLElement | null;
expect(firstChild?.tagName.toLowerCase()).toBe("img");
});
});