105 lines
3.8 KiB
TypeScript
105 lines
3.8 KiB
TypeScript
import { describe, it, expect, afterEach } from "vitest";
|
|
import { render, screen, cleanup } from "@testing-library/react";
|
|
import { PublicPageRenderer } from "@/features/editor/components/PublicPageRenderer";
|
|
import type { Block, SectionBlockProps, TextBlockProps } from "@/features/editor/state/editorStore";
|
|
|
|
// PublicPageRenderer 섹션/루트 레이아웃 TDD
|
|
// - Export 레이어의 pb-root / pb-section-* 구조와 개념적으로 대응되는 프리뷰 레이아웃을 고정한다.
|
|
// - Tailwind 유틸 대신 builder.css 의 pb-* 레이아웃 클래스를 기준으로 검증한다.
|
|
|
|
describe("PublicPageRenderer - 섹션/루트 레이아웃", () => {
|
|
afterEach(() => {
|
|
cleanup();
|
|
});
|
|
|
|
const makeSectionWithText = (sectionOverride: Partial<SectionBlockProps> = {}, textOverride: Partial<TextBlockProps> = {}): Block[] => {
|
|
const sectionProps: SectionBlockProps = {
|
|
background: "default",
|
|
paddingY: "md",
|
|
columns: [{ id: "sec_layout_col_1", span: 12 }],
|
|
maxWidthMode: "normal",
|
|
gapX: "md",
|
|
alignItems: "top",
|
|
...sectionOverride,
|
|
} as SectionBlockProps;
|
|
|
|
const textProps: TextBlockProps = {
|
|
text: "섹션 레이아웃 텍스트",
|
|
align: "left",
|
|
size: "base",
|
|
...textOverride,
|
|
} as TextBlockProps;
|
|
|
|
const section: Block = {
|
|
id: "sec_layout_1",
|
|
type: "section",
|
|
props: sectionProps,
|
|
} as any;
|
|
|
|
const text: Block = {
|
|
id: "sec_layout_1_text",
|
|
type: "text",
|
|
sectionId: "sec_layout_1",
|
|
columnId: "sec_layout_col_1",
|
|
props: textProps,
|
|
} as any;
|
|
|
|
return [section, text];
|
|
};
|
|
|
|
it("섹션 레이아웃은 pb-section / pb-section-inner / pb-section-columns / pb-section-column 구조로 렌더되어야 한다", () => {
|
|
const blocks: Block[] = makeSectionWithText();
|
|
|
|
render(<PublicPageRenderer blocks={blocks} />);
|
|
|
|
const inner = screen.getByTestId("preview-section-inner") as HTMLDivElement;
|
|
const columns = screen.getByTestId("preview-section-columns") as HTMLDivElement;
|
|
|
|
// Export 의 pb-section-inner / pb-section-columns 에 대응되는 레이아웃 구조를 최소한으로 보장한다.
|
|
expect(inner.className).toContain("pb-section-inner");
|
|
expect(columns.className).toContain("pb-section-columns");
|
|
|
|
// 컬럼 래퍼는 pb-section-column 클래스를 가지며, 섹션 텍스트가 그 안에 포함되어야 한다.
|
|
const columnWrapper = columns.querySelector(".pb-section-column") as HTMLDivElement | null;
|
|
expect(columnWrapper).not.toBeNull();
|
|
|
|
// 섹션 텍스트가 실제로 섹션 안에 렌더되는지 확인한다.
|
|
const textEl = screen.getByText("섹션 레이아웃 텍스트");
|
|
const section = textEl.closest("section");
|
|
expect(section).not.toBeNull();
|
|
});
|
|
|
|
it("루트 블록은 pb-root / pb-root-inner 구조 안에서 렌더되어야 한다", () => {
|
|
const rootTextProps: TextBlockProps = {
|
|
text: "루트 레이아웃 텍스트",
|
|
align: "left",
|
|
size: "base",
|
|
} as TextBlockProps;
|
|
|
|
const rootTextBlock: Block = {
|
|
id: "root_layout_text",
|
|
type: "text",
|
|
props: rootTextProps,
|
|
} as any;
|
|
|
|
const sectionBlocks = makeSectionWithText();
|
|
|
|
const blocks: Block[] = [rootTextBlock, ...sectionBlocks];
|
|
|
|
render(<PublicPageRenderer blocks={blocks} />);
|
|
|
|
const rootTextEl = screen.getByText("루트 레이아웃 텍스트") as HTMLParagraphElement;
|
|
|
|
// 루트 텍스트는 상단 pb-root 섹션 안에 존재해야 한다.
|
|
const rootSection = rootTextEl.closest("section");
|
|
expect(rootSection).not.toBeNull();
|
|
if (!rootSection) return;
|
|
|
|
expect(rootSection.className).toContain("pb-root");
|
|
|
|
// 섹션 바로 아래의 pb-root-inner 컨테이너 구조를 확인한다.
|
|
const innerContainer = rootSection.querySelector(".pb-root-inner");
|
|
expect(innerContainer).not.toBeNull();
|
|
});
|
|
});
|