import React from "react";
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { Button } from "../Button";
describe("Button", () => {
// Basic rendering tests
describe("Rendering", () => {
it("renders button with children", () => {
render();
expect(
screen.getByRole("button", { name: "Click me" })
).toBeInTheDocument();
});
it("renders with custom className", () => {
render();
const button = screen.getByRole("button");
expect(button).toHaveClass("custom-class");
});
it("renders with different variants", () => {
const { rerender } = render();
let button = screen.getByRole("button");
expect(button).toHaveClass("bg-primary");
rerender();
button = screen.getByRole("button");
expect(button).toHaveClass("bg-secondary");
rerender();
button = screen.getByRole("button");
expect(button).toHaveClass("border-input");
rerender();
button = screen.getByRole("button");
expect(button).toHaveClass("hover:bg-accent");
rerender();
button = screen.getByRole("button");
expect(button).toHaveClass("bg-destructive");
});
it("renders with different sizes", () => {
const { rerender } = render();
let button = screen.getByRole("button");
expect(button).toHaveClass("h-9");
rerender();
button = screen.getByRole("button");
expect(button).toHaveClass("h-10");
rerender();
button = screen.getByRole("button");
expect(button).toHaveClass("h-11");
});
});
// Props tests
describe("Props", () => {
it("handles disabled state", () => {
render();
const button = screen.getByRole("button");
expect(button).toBeDisabled();
expect(button).toHaveClass("disabled:opacity-50");
});
it("handles loading state", () => {
render();
const button = screen.getByRole("button");
expect(button).toBeDisabled();
expect(screen.getByRole("status")).toBeInTheDocument();
});
it("renders with fullWidth", () => {
render();
const button = screen.getByRole("button");
expect(button).toHaveClass("w-full");
});
it("renders with leftIcon", () => {
const icon = ←;
render();
expect(screen.getByTestId("left-icon")).toBeInTheDocument();
});
it("renders with rightIcon", () => {
const icon = →;
render();
expect(screen.getByTestId("right-icon")).toBeInTheDocument();
});
it("renders with both icons", () => {
const leftIcon = ←;
const rightIcon = →;
render(
);
expect(screen.getByTestId("left-icon")).toBeInTheDocument();
expect(screen.getByTestId("right-icon")).toBeInTheDocument();
});
});
// Accessibility tests
describe("Accessibility", () => {
it("supports aria-label", () => {
render();
const button = screen.getByRole("button");
expect(button).toHaveAttribute("aria-label", "Custom label");
});
it("supports aria-describedby", () => {
render();
const button = screen.getByRole("button");
expect(button).toHaveAttribute("aria-describedby", "description");
});
it("supports aria-expanded", () => {
render();
const button = screen.getByRole("button");
expect(button).toHaveAttribute("aria-expanded", "true");
});
it("supports aria-pressed", () => {
render();
const button = screen.getByRole("button");
expect(button).toHaveAttribute("aria-pressed", "true");
});
it("has proper focus management", async () => {
const user = userEvent.setup();
render();
const button = screen.getByRole("button");
await user.tab();
expect(button).toHaveFocus();
});
it("announces loading state to screen readers", () => {
render();
const loadingIndicator = screen.getByRole("status");
expect(loadingIndicator).toHaveAttribute("aria-label", "Loading");
});
});
// Interaction tests
describe("Interactions", () => {
it("calls onClick when clicked", async () => {
const handleClick = jest.fn();
const user = userEvent.setup();
render();
await user.click(screen.getByRole("button"));
expect(handleClick).toHaveBeenCalledTimes(1);
});
it("does not call onClick when disabled", async () => {
const handleClick = jest.fn();
const user = userEvent.setup();
render(
);
await user.click(screen.getByRole("button"));
expect(handleClick).not.toHaveBeenCalled();
});
it("does not call onClick when loading", async () => {
const handleClick = jest.fn();
const user = userEvent.setup();
render(
);
await user.click(screen.getByRole("button"));
expect(handleClick).not.toHaveBeenCalled();
});
it("supports keyboard navigation (Enter)", async () => {
const handleClick = jest.fn();
const user = userEvent.setup();
render();
const button = screen.getByRole("button");
button.focus();
await user.keyboard("{Enter}");
expect(handleClick).toHaveBeenCalledTimes(1);
});
it("supports keyboard navigation (Space)", async () => {
const handleClick = jest.fn();
const user = userEvent.setup();
render();
const button = screen.getByRole("button");
button.focus();
await user.keyboard(" ");
expect(handleClick).toHaveBeenCalledTimes(1);
});
});
// Button type tests
describe("Button types", () => {
it("renders as submit button", () => {
render();
const button = screen.getByRole("button");
expect(button).toHaveAttribute("type", "submit");
});
it("renders as reset button", () => {
render();
const button = screen.getByRole("button");
expect(button).toHaveAttribute("type", "reset");
});
it("defaults to button type", () => {
render();
const button = screen.getByRole("button");
expect(button).toHaveAttribute("type", "button");
});
});
// Edge cases
describe("Edge cases", () => {
it("handles empty children", () => {
render();
const button = screen.getByRole("button");
expect(button).toBeInTheDocument();
expect(button).toHaveTextContent("");
});
it("handles multiple class names", () => {
render();
const button = screen.getByRole("button");
expect(button).toHaveClass("class1", "class2");
});
it("forwards ref correctly", () => {
const ref = React.createRef();
render();
expect(ref.current).toBeInstanceOf(HTMLButtonElement);
});
});
});