import React from "react";
import { render, screen } from "@testing-library/react";
import { describe, it, expect, vi } from "vitest";
import List from "./list";
describe("List Component", () => {
describe("Basic Rendering", () => {
it("renders an unordered list by default", () => {
render(
Item 1
Item 2
);
const list = screen.getByRole("list");
expect(list.tagName).toBe("UL");
});
it("renders an ordered list when type='ol'", () => {
render(
First
Second
);
const list = screen.getByRole("list");
expect(list.tagName).toBe("OL");
});
it("renders a definition list when type='dl'", () => {
render(
Term
Definition
);
const term = screen.getByText("Term");
const definition = screen.getByText("Definition");
expect(term.tagName).toBe("DT");
expect(definition.tagName).toBe("DD");
});
it("renders children correctly", () => {
render(
Test Item 1
Test Item 2
Test Item 3
);
expect(screen.getByText("Test Item 1")).toBeInTheDocument();
expect(screen.getByText("Test Item 2")).toBeInTheDocument();
expect(screen.getByText("Test Item 3")).toBeInTheDocument();
});
});
describe("ListItem Component", () => {
it("renders a list item (li) by default", () => {
render(
Default item
);
const item = screen.getByText("Default item");
expect(item.tagName).toBe("LI");
});
it("renders a definition term (dt) when type='dt'", () => {
render(
Term
);
const item = screen.getByText("Term");
expect(item.tagName).toBe("DT");
});
it("renders a definition description (dd) when type='dd'", () => {
render(
Description
);
const item = screen.getByText("Description");
expect(item.tagName).toBe("DD");
});
it("accepts custom id prop", () => {
render(
Item with ID
);
const item = screen.getByText("Item with ID");
expect(item).toHaveAttribute("id", "custom-item");
});
it("accepts custom classes prop", () => {
render(
Styled item
);
const item = screen.getByText("Styled item");
expect(item).toHaveClass("custom-class");
});
it("accepts custom styles prop", () => {
render(
Styled item
);
const item = screen.getByText("Styled item");
expect(item).toHaveStyle({ paddingLeft: "1rem" });
});
});
describe("Props and Attributes", () => {
it("applies variant via data-variant attribute", () => {
render(
Item
);
const list = screen.getByRole("list");
expect(list).toHaveAttribute("data-variant", "inline");
});
it("applies custom CSS classes", () => {
render(
Item
);
const list = screen.getByRole("list");
expect(list).toHaveClass("custom-list-class");
});
it("applies inline styles", () => {
render(
Item
);
const list = screen.getByRole("list");
expect(list).toHaveStyle({ marginTop: "2rem" });
});
it("applies custom id", () => {
render(
Item
);
const list = screen.getByRole("list");
expect(list).toHaveAttribute("id", "main-list");
});
it("supports role override for accessibility", () => {
render(
Item
);
const list = screen.getByRole("list");
expect(list).toHaveAttribute("role", "list");
});
it("supports aria-label", () => {
render(
Home
);
const list = screen.getByRole("list", { name: "Navigation menu" });
expect(list).toBeInTheDocument();
});
it("supports aria-labelledby", () => {
render(
Features
Feature 1
);
const list = screen.getByRole("list");
expect(list).toHaveAttribute("aria-labelledby", "list-heading");
});
});
describe("Nested Lists", () => {
it("renders nested unordered lists", () => {
render(
Parent item
Child item 1
Child item 2
);
const lists = screen.getAllByRole("list");
expect(lists).toHaveLength(2); // Parent and nested list
});
it("renders nested ordered lists", () => {
render(
Step 1
Sub-step 1.1
Sub-step 1.2
);
const lists = screen.getAllByRole("list");
expect(lists).toHaveLength(2);
lists.forEach((list) => {
expect(list.tagName).toBe("OL");
});
});
it("renders mixed nested list types", () => {
render(
Parent
Nested ordered
);
const lists = screen.getAllByRole("list");
expect(lists).toHaveLength(2);
expect(lists[0].tagName).toBe("UL");
expect(lists[1].tagName).toBe("OL");
});
});
describe("Ref Forwarding", () => {
it("forwards ref to List component", () => {
const ref = vi.fn();
render(
Item
);
expect(ref).toHaveBeenCalled();
const refValue = ref.mock.calls[0][0];
expect(refValue).toBeInstanceOf(HTMLUListElement);
});
it("forwards ref to ordered list", () => {
const ref = vi.fn();
render(
Item
);
expect(ref).toHaveBeenCalled();
const refValue = ref.mock.calls[0][0];
expect(refValue).toBeInstanceOf(HTMLOListElement);
});
it("forwards ref to definition list", () => {
const ref = vi.fn();
render(
Term
);
expect(ref).toHaveBeenCalled();
const refValue = ref.mock.calls[0][0];
expect(refValue).toBeInstanceOf(HTMLDListElement);
});
it("forwards ref to ListItem", () => {
const ref = vi.fn();
render(
Item
);
expect(ref).toHaveBeenCalled();
const refValue = ref.mock.calls[0][0];
expect(refValue).toBeInstanceOf(HTMLLIElement);
});
});
describe("Compound Component Pattern", () => {
it("List.ListItem is attached to List", () => {
expect(List.ListItem).toBeDefined();
expect(typeof List.ListItem).toBe("object"); // forwardRef returns an object
});
it("works with List.ListItem syntax", () => {
render(
Item 1
Item 2
);
expect(screen.getByText("Item 1")).toBeInTheDocument();
expect(screen.getByText("Item 2")).toBeInTheDocument();
});
});
describe("Display Names", () => {
it("List has correct displayName", () => {
expect(List.displayName).toBe("List");
});
it("ListItem has correct displayName", () => {
expect(List.ListItem.displayName).toBe("ListItem");
});
});
describe("Accessibility", () => {
it("renders with proper ARIA attributes", () => {
render(
Feature 1
);
const list = screen.getByRole("list", { name: "Product features" });
expect(list).toBeInTheDocument();
});
it("supports role override for styled lists", () => {
render(
Item 1
);
const list = screen.getByRole("list");
expect(list).toHaveAttribute("role", "list");
});
it("maintains semantic structure with nested lists", () => {
render(
Parent
Nested item
);
const lists = screen.getAllByRole("list");
expect(lists).toHaveLength(2); // Parent and nested list
});
it("renders definition lists with proper semantics", () => {
render(
Term
Definition
);
const term = screen.getByText("Term");
const definition = screen.getByText("Definition");
expect(term.tagName).toBe("DT");
expect(definition.tagName).toBe("DD");
});
});
describe("Edge Cases", () => {
it("renders with no children", () => {
// @ts-expect-error - Testing edge case where children is missing
render(
);
const list = screen.getByRole("list");
expect(list).toBeInTheDocument();
expect(list).toBeEmptyDOMElement();
});
it("renders with single child", () => {
render(
Single item
);
expect(screen.getByText("Single item")).toBeInTheDocument();
});
it("renders with many children", () => {
const items = Array.from({ length: 100 }, (_, i) => `Item ${i + 1}`);
render(
{items.map((item) => (
{item}
))}
);
expect(screen.getByText("Item 1")).toBeInTheDocument();
expect(screen.getByText("Item 100")).toBeInTheDocument();
});
it("handles complex children", () => {
render(
Bold and italic text
);
expect(screen.getByText("Bold")).toBeInTheDocument();
expect(screen.getByText("italic")).toBeInTheDocument();
});
it("spreads additional props to list", () => {
render(
Item
);
const list = screen.getByTestId("custom-list");
expect(list).toHaveAttribute("data-custom", "value");
});
it("spreads additional props to list item", () => {
render(
Item
);
const item = screen.getByTestId("custom-item");
expect(item).toHaveAttribute("data-custom", "value");
});
});
describe("Real-World Use Cases", () => {
it("renders a navigation list", () => {
render(
);
const list = screen.getByRole("list", { name: "Main navigation" });
expect(list).toHaveAttribute("data-variant", "inline");
});
it("renders a steps list", () => {
render(
Download the package
Extract the files
Run the installer
);
const list = screen.getByRole("list", { name: "Installation steps" });
expect(list.tagName).toBe("OL");
});
it("renders a glossary", () => {
render(
HTML
HyperText Markup Language
CSS
Cascading Style Sheets
);
expect(screen.getByText("HTML")).toBeInTheDocument();
expect(screen.getByText("HyperText Markup Language")).toBeInTheDocument();
});
it("renders a feature list with custom styling", () => {
render(
Feature 1
Feature 2
);
const list = screen.getByRole("list");
expect(list).toHaveAttribute("data-variant", "custom");
expect(list).toHaveStyle({
"--list-marker-color": "blue",
"--list-marker-content": "'✓'",
});
});
});
});