import { render, screen } from "@testing-library/react"
import React from "react"
import "@testing-library/jest-dom"
import { PageLoader } from "./PageLoader"
describe("PageLoader", () => {
describe("Rendering", () => {
it("renders correctly with various props", () => {
render(
,
)
const loader = screen.getByRole("status")
expect(loader).toBeInTheDocument()
expect(loader).toHaveAttribute("data-testid", "test-loader")
expect(loader).toHaveAttribute("aria-label", "Loading content")
expect(loader).toHaveClass("custom-loader")
})
it("renders with minimum props", () => {
render()
const loader = screen.getByRole("status")
expect(loader).toBeInTheDocument()
expect(loader).toHaveAttribute("aria-label", "Loading")
})
})
describe("Sizes", () => {
it.each([
{ size: "xs" as const, expectedClass: "size--xs" },
{ size: "sm" as const, expectedClass: "size--sm" },
{ size: "md" as const, expectedClass: "size--md" },
{ size: "lg" as const, expectedClass: "size--lg" },
{ size: "xl" as const, expectedClass: "size--xl" },
])("applies $size size", ({ size, expectedClass }) => {
render()
const loader = screen.getByRole("status")
expect(loader).toHaveClass(expectedClass)
})
it("applies default size", () => {
render()
const loader = screen.getByRole("status")
expect(loader).toHaveClass("size--md")
})
it("changes size dynamically", () => {
const { rerender } = render()
const loader = screen.getByRole("status")
expect(loader).toHaveClass("size--sm")
rerender()
expect(loader).toHaveClass("size--lg")
})
})
describe("Emphasis", () => {
it.each([
{ emphasis: "subtle" as const, expectedClass: "emphasis--subtle" },
{ emphasis: "normal" as const, expectedClass: "emphasis--normal" },
{ emphasis: "strong" as const, expectedClass: "emphasis--strong" },
])("applies $emphasis emphasis", ({ emphasis, expectedClass }) => {
render()
const loader = screen.getByRole("status")
expect(loader).toHaveClass(expectedClass)
})
it("applies default emphasis", () => {
render()
const loader = screen.getByRole("status")
expect(loader).toHaveClass("emphasis--normal")
})
})
describe("Tone", () => {
it.each([
{ tone: "neutral" as const, expectedClass: "tone--neutral" },
{ tone: "accent" as const, expectedClass: "tone--accent" },
{ tone: "positive" as const, expectedClass: "tone--positive" },
{ tone: "critical" as const, expectedClass: "tone--critical" },
])("applies $tone tone", ({ tone, expectedClass }) => {
render()
const loader = screen.getByRole("status")
expect(loader).toHaveClass(expectedClass)
})
it("applies default tone", () => {
render()
const loader = screen.getByRole("status")
expect(loader).toHaveClass("tone--accent")
})
})
describe("Animation Speed", () => {
it.each([
{ speed: "slow" as const, expectedClass: "speed--slow" },
{ speed: "normal" as const, expectedClass: "speed--normal" },
{ speed: "fast" as const, expectedClass: "speed--fast" },
])("applies $speed speed", ({ speed, expectedClass }) => {
render()
const loader = screen.getByRole("status")
expect(loader).toHaveClass(expectedClass)
})
it("applies default speed", () => {
render()
const loader = screen.getByRole("status")
expect(loader).toHaveClass("speed--normal")
})
})
describe("Display Modes", () => {
it.each([
{ display: "inline" as const, expectedClass: "display--inline" },
{ display: "block" as const, expectedClass: "display--block" },
{ display: "overlay" as const, expectedClass: "display--overlay" },
])("applies $display display", ({ display, expectedClass }) => {
render()
const loader = screen.getByRole("status")
expect(loader).toHaveClass(expectedClass)
})
it("applies default display", () => {
render()
const loader = screen.getByRole("status")
expect(loader).toHaveClass("display--inline")
})
it("applies backdrop with overlay mode", () => {
render()
const loader = screen.getByRole("status")
expect(loader).toHaveClass("display--overlay", "withBackdrop")
})
})
describe("Label", () => {
it("renders with custom label", () => {
render()
const loader = screen.getByRole("status")
expect(loader).toHaveAttribute("aria-label", "Custom loading text")
})
it("renders label text in non-overlay mode", () => {
render()
expect(screen.getByText("Processing...")).toBeInTheDocument()
})
it("does not render label text in overlay mode", () => {
render()
expect(screen.queryByText("Processing...")).not.toBeInTheDocument()
})
it("renders with default label", () => {
render()
const loader = screen.getByRole("status")
expect(loader).toHaveAttribute("aria-label", "Loading")
})
})
describe("CSS Classes", () => {
it("applies base loader classes", () => {
render()
const loader = screen.getByRole("status")
expect(loader).toHaveClass("loader")
})
it("applies custom className", () => {
render()
const loader = screen.getByRole("status")
expect(loader).toHaveClass("my-custom-class")
})
it("combines all classes correctly", () => {
render(
,
)
const loader = screen.getByRole("status")
expect(loader).toHaveClass(
"loader",
"emphasis--strong",
"tone--positive",
"size--lg",
"speed--fast",
"display--block",
"custom",
)
})
})
describe("HTML Attributes", () => {
it("passes through id", () => {
render()
const loader = screen.getByRole("status")
expect(loader).toHaveAttribute("id", "my-loader")
})
it("passes through data attributes", () => {
render()
const loader = screen.getByRole("status")
expect(loader).toHaveAttribute("data-testid", "test")
expect(loader).toHaveAttribute("data-custom", "value")
})
it("has correct ARIA attributes", () => {
render()
const loader = screen.getByRole("status")
expect(loader).toHaveAttribute("role", "status")
expect(loader).toHaveAttribute("aria-live", "polite")
})
})
describe("Spinner Structure", () => {
it("contains spinner element", () => {
const { container } = render()
const spinner = container.querySelector(".spinner")
expect(spinner).toBeInTheDocument()
expect(spinner).toHaveAttribute("aria-hidden", "true")
})
it("maintains structure with different props", () => {
const { container } = render(
,
)
const spinner = container.querySelector(".spinner")
expect(spinner).toBeInTheDocument()
})
})
describe("ResponsiveValue Support", () => {
it("handles responsive size values", () => {
render(
,
)
const loader = screen.getByRole("status")
expect(loader).toBeInTheDocument()
// Note: Actual responsive behavior would need to be tested with viewport changes
})
it("handles responsive emphasis values", () => {
render(
,
)
const loader = screen.getByRole("status")
expect(loader).toBeInTheDocument()
})
it("handles mixed responsive and static values", () => {
render(
,
)
const loader = screen.getByRole("status")
expect(loader).toBeInTheDocument()
})
})
describe("Edge Cases", () => {
it("handles undefined props gracefully", () => {
render()
const loader = screen.getByRole("status")
expect(loader).toHaveClass("size--md", "tone--accent")
})
it("handles rapid prop changes", () => {
const { rerender } = render()
const loader = screen.getByRole("status")
expect(loader).toHaveClass("emphasis--subtle")
rerender()
expect(loader).toHaveClass("emphasis--normal")
rerender()
expect(loader).toHaveClass("emphasis--strong")
})
it("handles multiple instances", () => {
render(
<>
>,
)
expect(screen.getByTestId("first")).toBeInTheDocument()
expect(screen.getByTestId("second")).toBeInTheDocument()
expect(screen.getByTestId("third")).toBeInTheDocument()
})
it("cleans up properly on unmount", () => {
const { unmount } = render()
expect(screen.getByRole("status")).toBeInTheDocument()
unmount()
expect(screen.queryByRole("status")).not.toBeInTheDocument()
})
})
describe("Accessibility", () => {
it("has proper loader semantics", () => {
render()
const loader = screen.getByRole("status")
expect(loader).toHaveAttribute("role", "status")
expect(loader).toHaveAttribute("aria-live", "polite")
})
it("supports custom ARIA attributes", () => {
render()
const loader = screen.getByRole("status")
expect(loader).toHaveAttribute("aria-describedby", "description")
expect(loader).toHaveAttribute("aria-busy", "true")
})
it("provides accessible label", () => {
render()
const loader = screen.getByRole("status")
expect(loader).toHaveAttribute(
"aria-label",
"Please wait while we load your data",
)
})
})
})