import { describe, it, expect } from "vitest";
import { render, screen } from "@testing-library/react";
import React from "react";
import Title from "./title";
describe("Title Component", () => {
describe("Rendering", () => {
it("should render with default h2 level", () => {
render(
Default Title);
const heading = screen.getByRole("heading", { level: 2 });
expect(heading).toBeInTheDocument();
expect(heading).toHaveTextContent("Default Title");
});
it("should render children content", () => {
render(Test Content);
expect(screen.getByText("Test Content")).toBeInTheDocument();
});
it("should render complex children with elements", () => {
render(
📄
Document Title
);
expect(screen.getByTestId("icon")).toBeInTheDocument();
expect(screen.getByText("Document Title")).toBeInTheDocument();
});
});
describe("Heading Levels", () => {
it.each([
["h1", 1],
["h2", 2],
["h3", 3],
["h4", 4],
["h5", 5],
["h6", 6],
] as const)("should render %s with level %i", (level, numericLevel) => {
render(Heading {level});
const heading = screen.getByRole("heading", { level: numericLevel });
expect(heading).toBeInTheDocument();
expect(heading.tagName.toLowerCase()).toBe(level);
});
});
describe("Props and Attributes", () => {
it("should apply id attribute", () => {
render(Title with ID);
const heading = screen.getByRole("heading", { level: 2 });
expect(heading).toHaveAttribute("id", "test-heading");
});
it("should apply data-ui attribute", () => {
render(Styled Title);
const heading = screen.getByRole("heading", { level: 2 });
expect(heading).toHaveAttribute("data-ui", "section-title");
});
it("should apply className", () => {
render(Classed Title);
const heading = screen.getByRole("heading", { level: 2 });
expect(heading).toHaveClass("custom-class");
});
it("should apply multiple class names", () => {
render(Title);
const heading = screen.getByRole("heading", { level: 2 });
expect(heading).toHaveClass("class-one");
expect(heading).toHaveClass("class-two");
});
it("should apply inline styles", () => {
const styles = { color: "red", fontSize: "24px" };
render(Styled Title);
const heading = screen.getByRole("heading", { level: 2 });
// Browsers convert color values, so check for rgb format
expect(heading).toHaveStyle({ color: "rgb(255, 0, 0)", fontSize: "24px" });
});
});
describe("Accessibility", () => {
it("should have proper heading role", () => {
render(Accessible Title);
const heading = screen.getByRole("heading", { level: 1 });
expect(heading).toBeInTheDocument();
});
it("should support aria-label", () => {
render(
Dashboard
);
const heading = screen.getByRole("heading", { level: 2 });
expect(heading).toHaveAttribute("aria-label", "Dashboard overview");
});
it("should support aria-labelledby", () => {
render(
<>
Section Label
Title
>
);
const heading = screen.getByRole("heading", { level: 2 });
expect(heading).toHaveAttribute("aria-labelledby", "label-element");
});
it("should support aria-describedby", () => {
render(
<>
This is a description
Title
>
);
const heading = screen.getByRole("heading", { level: 2 });
expect(heading).toHaveAttribute("aria-describedby", "description");
});
it("should be findable by accessible name", () => {
render(Findable Heading);
expect(
screen.getByRole("heading", { name: "Findable Heading" })
).toBeInTheDocument();
});
});
describe("Ref Forwarding", () => {
it("should forward ref to the heading element", () => {
const ref = React.createRef();
render(Title with Ref);
expect(ref.current).toBeInstanceOf(HTMLHeadingElement);
expect(ref.current?.tagName.toLowerCase()).toBe("h2");
expect(ref.current?.textContent).toBe("Title with Ref");
});
it("should allow focus via ref", () => {
const ref = React.createRef();
render(Focusable Title);
ref.current?.focus();
expect(ref.current).toHaveFocus();
});
});
describe("Component API", () => {
it("should have displayName set", () => {
expect(Title.displayName).toBe("Title");
});
it("should accept all native heading attributes", () => {
render(
Title
);
const heading = screen.getByTestId("custom-heading");
expect(heading).toHaveAttribute("tabIndex", "0");
expect(heading).toHaveAttribute("title", "Tooltip text");
});
});
describe("Edge Cases", () => {
it("should handle empty string children", () => {
render({""});
const heading = screen.getByRole("heading", { level: 2 });
expect(heading).toBeInTheDocument();
expect(heading).toHaveTextContent("");
});
it("should handle null children gracefully", () => {
render({null});
const heading = screen.getByRole("heading", { level: 2 });
expect(heading).toBeInTheDocument();
});
it("should handle multiple text nodes", () => {
render(
Text one
{" "}
Text two
);
const heading = screen.getByRole("heading", { level: 2 });
expect(heading).toHaveTextContent("Text one Text two");
});
it("should handle conditional rendering", () => {
const showIcon = true;
render(
{showIcon && 📄}
Document
);
expect(screen.getByTestId("icon")).toBeInTheDocument();
});
});
describe("Integration with UI Component", () => {
it("should pass through all UI component props", () => {
render(
Integration Test
);
const heading = screen.getByRole("heading", { level: 3 });
expect(heading).toHaveAttribute("id", "integration-test");
expect(heading).toHaveClass("test-class");
expect(heading).toHaveStyle({ margin: "10px" });
expect(heading).toHaveAttribute("data-custom", "value");
});
});
describe("Memoization", () => {
it("should be a memoized component", () => {
// Title is wrapped with React.memo, so it should have the memo properties
expect(Title.$$typeof).toBeDefined();
});
});
});
describe("Title Component - Backwards Compatibility", () => {
describe("Migration from Heading", () => {
it("should work with level prop (new API)", () => {
render(New API);
expect(screen.getByRole("heading", { level: 2 })).toHaveTextContent("New API");
});
it("should use h2 as default (changed from h3)", () => {
// This tests the new default behavior
render(Default is now h2);
expect(screen.getByRole("heading", { level: 2 })).toBeInTheDocument();
});
});
});
describe("Title Size Variants", () => {
it("should render with size prop and correct data-title", () => {
render(Large);
const heading = screen.getByRole("heading", { level: 2 });
expect(heading).toHaveAttribute("data-title", "lg");
});
it.each([
["xs"],
["sm"],
["md"],
["lg"],
["xl"],
["2xl"],
] as const)("should render size variant %s", (size) => {
render({size} Title);
const heading = screen.getByRole("heading", { level: 2 });
expect(heading).toHaveAttribute("data-title", size);
});
});
describe("Title Color Variants", () => {
it("should render with color prop and correct data-title", () => {
render(Primary);
const heading = screen.getByRole("heading", { level: 2 });
expect(heading).toHaveAttribute("data-title", "primary");
});
it.each([
["primary"],
["secondary"],
["accent"],
["muted"],
["inherit"],
] as const)("should render color variant %s", (color) => {
render({color} Title);
const heading = screen.getByRole("heading", { level: 2 });
expect(heading).toHaveAttribute("data-title", color);
});
});
describe("Combined Size and Color Variants", () => {
it("should combine size and color in data-title attribute", () => {
render(Combined);
const heading = screen.getByRole("heading", { level: 2 });
expect(heading).toHaveAttribute("data-title", "lg primary");
});
it("should render only size when no color provided", () => {
render(Size Only);
const heading = screen.getByRole("heading", { level: 2 });
expect(heading).toHaveAttribute("data-title", "xl");
});
it("should render only color when no size provided", () => {
render(Color Only);
const heading = screen.getByRole("heading", { level: 2 });
expect(heading).toHaveAttribute("data-title", "accent");
});
it("should render no data-title when no variants provided", () => {
render(No Variants);
const heading = screen.getByRole("heading", { level: 2 });
expect(heading).not.toHaveAttribute("data-title");
});
});
describe("Backward Compatibility with New Props", () => {
it("should maintain className functionality with variants", () => {
render(Custom);
const heading = screen.getByRole("heading", { level: 2 });
expect(heading).toHaveClass("custom-class");
expect(heading).toHaveAttribute("data-title", "lg");
});
it("should maintain styles prop functionality with variants", () => {
render(Styled);
const heading = screen.getByRole("heading", { level: 2 });
expect(heading).toHaveStyle({ marginTop: "2rem" });
expect(heading).toHaveAttribute("data-title", "primary");
});
it("should preserve semantic heading level with visual size", () => {
render(Large h3);
const heading = screen.getByRole("heading", { level: 3 });
expect(heading).toBeInTheDocument();
expect(heading).toHaveAttribute("data-title", "xl");
expect(heading.tagName.toLowerCase()).toBe("h3");
});
it("should work with all existing props plus new variants", () => {
render(
Full Props Test
);
const heading = screen.getByRole("heading", { level: 2 });
expect(heading).toHaveAttribute("id", "test-id");
expect(heading).toHaveClass("test-class");
expect(heading).toHaveAttribute("data-ui", "section-title");
expect(heading).toHaveAttribute("data-title", "lg primary");
expect(heading).toHaveStyle({ padding: "1rem" });
});
});