import React from "react";
import { describe, it, expect } from "vitest";
import { render, screen } from "@testing-library/react";
import { Cluster } from "./cluster";
import type { ClusterProps } from "./cluster.types";
describe("Cluster Component", () => {
describe("Rendering", () => {
it("should render with default props", () => {
render(Content);
const cluster = screen.getByTestId("cluster");
expect(cluster).toBeInTheDocument();
expect(cluster.tagName).toBe("DIV");
expect(cluster).toHaveClass("cluster");
});
it("should render children correctly", () => {
render(
Tag 1
Tag 2
Tag 3
);
expect(screen.getByText("Tag 1")).toBeInTheDocument();
expect(screen.getByText("Tag 2")).toBeInTheDocument();
expect(screen.getByText("Tag 3")).toBeInTheDocument();
});
it("should render with text content", () => {
render(Plain text content);
expect(screen.getByText("Plain text content")).toBeInTheDocument();
});
it("should render with multiple children types", () => {
render(
Span
Link
);
expect(screen.getByText("Span")).toBeInTheDocument();
expect(screen.getByText("Button")).toBeInTheDocument();
expect(screen.getByText("Link")).toBeInTheDocument();
});
});
describe("Polymorphic Rendering (as prop)", () => {
it("should render as div by default", () => {
render(Content);
expect(screen.getByTestId("cluster").tagName).toBe("DIV");
});
it("should render as ul when as='ul'", () => {
render(
Item 1
Item 2
);
expect(screen.getByTestId("cluster").tagName).toBe("UL");
});
it("should render as ol when as='ol'", () => {
render(
Item 1
Item 2
);
expect(screen.getByTestId("cluster").tagName).toBe("OL");
});
it("should render as nav when as='nav'", () => {
render(
Link 1
Link 2
);
expect(screen.getByTestId("cluster").tagName).toBe("NAV");
});
it("should render as section when as='section'", () => {
render(
Content
);
expect(screen.getByTestId("cluster").tagName).toBe("SECTION");
});
});
describe("Gap Prop", () => {
it("should apply gap='0' class", () => {
render(
Content
);
expect(screen.getByTestId("cluster")).toHaveClass("cluster-gap-0");
});
it("should apply gap='xs' class", () => {
render(
Content
);
expect(screen.getByTestId("cluster")).toHaveClass("cluster-gap-xs");
});
it("should apply gap='sm' class", () => {
render(
Content
);
expect(screen.getByTestId("cluster")).toHaveClass("cluster-gap-sm");
});
it("should apply gap='md' class", () => {
render(
Content
);
expect(screen.getByTestId("cluster")).toHaveClass("cluster-gap-md");
});
it("should apply gap='lg' class", () => {
render(
Content
);
expect(screen.getByTestId("cluster")).toHaveClass("cluster-gap-lg");
});
it("should apply gap='xl' class", () => {
render(
Content
);
expect(screen.getByTestId("cluster")).toHaveClass("cluster-gap-xl");
});
it("should work without gap prop", () => {
render(Content);
const cluster = screen.getByTestId("cluster");
expect(cluster).toHaveClass("cluster");
expect(cluster).not.toHaveClass("cluster-gap-0");
expect(cluster).not.toHaveClass("cluster-gap-xs");
expect(cluster).not.toHaveClass("cluster-gap-sm");
});
});
describe("Justify Prop (Horizontal Alignment)", () => {
it("should apply justify='start' class", () => {
render(
Content
);
expect(screen.getByTestId("cluster")).toHaveClass("cluster-justify-start");
});
it("should apply justify='center' class", () => {
render(
Content
);
expect(screen.getByTestId("cluster")).toHaveClass("cluster-justify-center");
});
it("should apply justify='end' class", () => {
render(
Content
);
expect(screen.getByTestId("cluster")).toHaveClass("cluster-justify-end");
});
it("should apply justify='between' class", () => {
render(
Content
);
expect(screen.getByTestId("cluster")).toHaveClass("cluster-justify-between");
});
it("should work without justify prop", () => {
render(Content);
const cluster = screen.getByTestId("cluster");
expect(cluster).not.toHaveClass("cluster-justify-start");
expect(cluster).not.toHaveClass("cluster-justify-center");
});
});
describe("Align Prop (Vertical Alignment)", () => {
it("should apply align='start' class", () => {
render(
Content
);
expect(screen.getByTestId("cluster")).toHaveClass("cluster-align-start");
});
it("should apply align='center' class", () => {
render(
Content
);
expect(screen.getByTestId("cluster")).toHaveClass("cluster-align-center");
});
it("should apply align='end' class", () => {
render(
Content
);
expect(screen.getByTestId("cluster")).toHaveClass("cluster-align-end");
});
it("should apply align='baseline' class", () => {
render(
Content
);
expect(screen.getByTestId("cluster")).toHaveClass("cluster-align-baseline");
});
it("should work without align prop", () => {
render(Content);
const cluster = screen.getByTestId("cluster");
expect(cluster).not.toHaveClass("cluster-align-start");
expect(cluster).not.toHaveClass("cluster-align-center");
});
});
describe("Combined Props", () => {
it("should apply gap and justify together", () => {
render(
Content
);
const cluster = screen.getByTestId("cluster");
expect(cluster).toHaveClass("cluster");
expect(cluster).toHaveClass("cluster-gap-md");
expect(cluster).toHaveClass("cluster-justify-center");
});
it("should apply gap and align together", () => {
render(
Content
);
const cluster = screen.getByTestId("cluster");
expect(cluster).toHaveClass("cluster");
expect(cluster).toHaveClass("cluster-gap-lg");
expect(cluster).toHaveClass("cluster-align-baseline");
});
it("should apply all props together", () => {
render(
Content
);
const cluster = screen.getByTestId("cluster");
expect(cluster).toHaveClass("cluster");
expect(cluster).toHaveClass("cluster-gap-sm");
expect(cluster).toHaveClass("cluster-justify-between");
expect(cluster).toHaveClass("cluster-align-center");
});
it("should combine all props with polymorphic as prop", () => {
render(
Link
);
const cluster = screen.getByTestId("cluster");
expect(cluster.tagName).toBe("NAV");
expect(cluster).toHaveClass("cluster");
expect(cluster).toHaveClass("cluster-gap-md");
expect(cluster).toHaveClass("cluster-justify-center");
expect(cluster).toHaveClass("cluster-align-baseline");
});
});
describe("ClassName and Classes Props", () => {
it("should merge className prop with utility classes", () => {
render(
Content
);
const cluster = screen.getByTestId("cluster");
expect(cluster).toHaveClass("cluster");
expect(cluster).toHaveClass("custom-class");
});
it("should merge classes prop with utility classes", () => {
render(
Content
);
const cluster = screen.getByTestId("cluster");
expect(cluster).toHaveClass("cluster");
expect(cluster).toHaveClass("another-class");
});
it("should merge both className and classes", () => {
render(
Content
);
const cluster = screen.getByTestId("cluster");
expect(cluster).toHaveClass("cluster");
expect(cluster).toHaveClass("custom");
expect(cluster).toHaveClass("another");
});
it("should merge custom classes with all utility classes", () => {
render(
Content
);
const cluster = screen.getByTestId("cluster");
expect(cluster).toHaveClass("cluster");
expect(cluster).toHaveClass("cluster-gap-md");
expect(cluster).toHaveClass("cluster-justify-center");
expect(cluster).toHaveClass("cluster-align-baseline");
expect(cluster).toHaveClass("custom");
expect(cluster).toHaveClass("utility");
});
});
describe("Inline Styles", () => {
it("should apply inline styles via styles prop", () => {
render(
Content
);
const cluster = screen.getByTestId("cluster");
expect(cluster).toHaveStyle({ maxWidth: "500px" });
});
it("should apply inline styles via style prop", () => {
render(
Content
);
const cluster = screen.getByTestId("cluster");
expect(cluster).toHaveStyle({ border: "1px solid red" });
});
it("should apply CSS custom properties", () => {
render(
Content
);
const cluster = screen.getByTestId("cluster");
expect(cluster).toHaveStyle({ "--custom-color": "blue" });
});
});
describe("Ref Forwarding", () => {
it("should forward ref to the underlying element", () => {
const ref = { current: null as HTMLDivElement | null };
render(
Content
);
expect(ref.current).toBeInstanceOf(HTMLDivElement);
expect(ref.current).toBe(screen.getByTestId("cluster"));
});
it("should forward ref with polymorphic as prop", () => {
const ref = { current: null as HTMLElement | null };
render(
Content
);
expect(ref.current).toBeInstanceOf(HTMLElement);
expect(ref.current?.tagName).toBe("NAV");
});
});
describe("ARIA and Accessibility Attributes", () => {
it("should accept aria-label", () => {
render(
Content
);
expect(screen.getByTestId("cluster")).toHaveAttribute("aria-label", "Tag cloud");
});
it("should accept aria-labelledby", () => {
render(
<>
Filter Tags
Tag 1
>
);
expect(screen.getByTestId("cluster")).toHaveAttribute("aria-labelledby", "cluster-title");
});
it("should accept aria-describedby", () => {
render(
<>
Select tags to filter results
Tag 1
>
);
expect(screen.getByTestId("cluster")).toHaveAttribute("aria-describedby", "cluster-desc");
});
it("should accept role attribute", () => {
render(
Content
);
expect(screen.getByTestId("cluster")).toHaveAttribute("role", "group");
});
it("should accept data attributes", () => {
render(
Content
);
expect(screen.getByTestId("cluster")).toHaveAttribute("data-component", "tag-cloud");
});
it("should accept id attribute", () => {
render(
Content
);
expect(screen.getByTestId("cluster")).toHaveAttribute("id", "tags-cluster");
});
});
describe("Edge Cases", () => {
it("should handle empty children", () => {
render();
expect(screen.getByTestId("cluster")).toBeInTheDocument();
expect(screen.getByTestId("cluster")).toBeEmptyDOMElement();
});
it("should handle null children", () => {
render({null});
expect(screen.getByTestId("cluster")).toBeInTheDocument();
});
it("should handle undefined children", () => {
render({undefined});
expect(screen.getByTestId("cluster")).toBeInTheDocument();
});
it("should handle false children (conditional rendering)", () => {
const shouldShow = false;
render({shouldShow && Hidden});
expect(screen.getByTestId("cluster")).toBeInTheDocument();
expect(screen.queryByText("Hidden")).not.toBeInTheDocument();
});
it("should handle fragments", () => {
render(
<>
Item 1
Item 2
>
);
expect(screen.getByText("Item 1")).toBeInTheDocument();
expect(screen.getByText("Item 2")).toBeInTheDocument();
});
it("should handle mixed content types", () => {
render(
Text node
Element
{null}
{undefined}
{false}
);
expect(screen.getByText("Text node")).toBeInTheDocument();
expect(screen.getByText("Element")).toBeInTheDocument();
expect(screen.getByText("Button")).toBeInTheDocument();
});
});
describe("Real-World Usage Patterns", () => {
it("should render tag cloud correctly", () => {
const tags = ["React", "TypeScript", "CSS", "Accessibility"];
render(
{tags.map((tag) => (
{tag}
))}
);
const cluster = screen.getByTestId("cluster");
expect(cluster).toHaveClass("cluster-gap-sm");
expect(cluster).toHaveClass("cluster-justify-center");
tags.forEach((tag) => {
expect(screen.getByText(tag)).toBeInTheDocument();
});
});
it("should render button group correctly", () => {
render(
);
expect(screen.getByTestId("cluster")).toHaveClass("cluster-gap-md");
expect(screen.getByText("Action 1")).toBeInTheDocument();
expect(screen.getByText("Action 2")).toBeInTheDocument();
expect(screen.getByText("Action 3")).toBeInTheDocument();
});
it("should render navigation links with baseline alignment", () => {
render(
Home
About
Contact
);
const cluster = screen.getByTestId("cluster");
expect(cluster.tagName).toBe("NAV");
expect(cluster).toHaveClass("cluster-gap-lg");
expect(cluster).toHaveClass("cluster-align-baseline");
expect(cluster).toHaveClass("cluster-justify-center");
});
it("should render filter pills with semantic list", () => {
render(
);
const cluster = screen.getByTestId("cluster");
expect(cluster.tagName).toBe("UL");
expect(cluster).toHaveClass("cluster-gap-sm");
});
it("should render badge cluster with zero gap", () => {
render(
Active
New
Beta
);
expect(screen.getByTestId("cluster")).toHaveClass("cluster-gap-xs");
expect(screen.getByText("Active")).toBeInTheDocument();
expect(screen.getByText("New")).toBeInTheDocument();
expect(screen.getByText("Beta")).toBeInTheDocument();
});
});
describe("TypeScript Type Safety", () => {
it("should accept valid ClusterProps", () => {
const props: ClusterProps = {
gap: "md",
justify: "center",
align: "baseline",
as: "nav",
className: "custom",
children: Content,
};
render();
expect(screen.getByTestId("cluster")).toBeInTheDocument();
});
it("should accept all SpacingScale values", () => {
const spacingScales: ClusterProps["gap"][] = ["0", "xs", "sm", "md", "lg", "xl"];
spacingScales.forEach((gap, index) => {
render(
Content
);
expect(screen.getByTestId(`cluster-${index}`)).toHaveClass(`cluster-gap-${gap}`);
});
});
it("should accept all ClusterElement values", () => {
const elements: ClusterProps["as"][] = ["div", "ul", "ol", "nav", "section"];
elements.forEach((element, index) => {
if (!element) return;
render(
Content
);
expect(screen.getByTestId(`cluster-${index}`).tagName).toBe(element.toUpperCase());
});
});
});
});