import React from "react";
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { Card, CardHeader, CardTitle, CardContent, CardFooter } from "../Card";
describe("Card", () => {
// Basic rendering tests
describe("Rendering", () => {
it("renders card with children", () => {
render(Card content);
expect(screen.getByText("Card content")).toBeInTheDocument();
});
it("renders with custom className", () => {
render(Content);
const card = screen.getByText("Content").closest("div");
expect(card).toHaveClass("custom-class");
});
it("renders with different variants", () => {
const { rerender } = render(Default);
let card = screen.getByText("Default").closest("div");
expect(card).toHaveClass("bg-card", "border", "border-border");
rerender(Outline);
card = screen.getByText("Outline").closest("div");
expect(card).toHaveClass("border-2", "border-border", "bg-background");
rerender(Elevated);
card = screen.getByText("Elevated").closest("div");
expect(card).toHaveClass("bg-card", "shadow-md");
rerender(Ghost);
card = screen.getByText("Ghost").closest("div");
expect(card).toHaveClass("bg-background");
});
it("renders with different padding", () => {
const { rerender } = render(Small);
let card = screen.getByText("Small").closest("div");
expect(card).toHaveClass("p-4");
rerender(Medium);
card = screen.getByText("Medium").closest("div");
expect(card).toHaveClass("p-6");
rerender(Large);
card = screen.getByText("Large").closest("div");
expect(card).toHaveClass("p-8");
rerender(None);
card = screen.getByText("None").closest("div");
expect(card).not.toHaveClass("p-4", "p-6", "p-8");
});
});
// Props tests
describe("Props", () => {
it("handles hover prop", () => {
render(Hover content);
const card = screen.getByText("Hover content").closest("div");
expect(card).toHaveClass("hover:shadow-md");
});
it("handles clickable prop", () => {
render(Clickable content);
const card = screen.getByText("Clickable content").closest("div");
expect(card).toHaveClass("cursor-pointer", "hover:shadow-md");
});
it("makes card clickable when onClick is provided", () => {
const handleClick = jest.fn();
render(Click me);
const card = screen.getByText("Click me").closest("div");
expect(card).toHaveClass("cursor-pointer");
});
});
// Interaction tests
describe("Interactions", () => {
it("calls onClick when clicked", async () => {
const handleClick = jest.fn();
const user = userEvent.setup();
render(Click me);
await user.click(screen.getByText("Click me"));
expect(handleClick).toHaveBeenCalledTimes(1);
});
it("supports keyboard interaction when clickable", async () => {
const handleClick = jest.fn();
const user = userEvent.setup();
render(
Clickable
);
const card = screen.getByText("Clickable").closest("div");
expect(card).toHaveAttribute("tabIndex", "0");
// Focus and press Enter
card?.focus();
await user.keyboard("{Enter}");
expect(handleClick).toHaveBeenCalledTimes(1);
// Press Space
await user.keyboard(" ");
expect(handleClick).toHaveBeenCalledTimes(2);
});
});
// Edge cases
describe("Edge cases", () => {
it("forwards ref correctly", () => {
const ref = React.createRef();
render(Content);
expect(ref.current).toBeInstanceOf(HTMLDivElement);
});
});
});
describe("CardHeader", () => {
it("renders with children", () => {
render(Header content);
expect(screen.getByText("Header content")).toBeInTheDocument();
});
it("renders with custom className", () => {
render(Header);
const header = screen.getByText("Header");
expect(header).toHaveClass("custom-header");
});
it("has proper default styling", () => {
render(Header);
const header = screen.getByText("Header");
expect(header).toHaveClass("flex", "flex-col", "space-y-1.5", "p-6");
});
it("forwards ref correctly", () => {
const ref = React.createRef();
render(Header);
expect(ref.current).toBeInstanceOf(HTMLDivElement);
});
});
describe("CardTitle", () => {
it("renders with children", () => {
render(Title content);
expect(screen.getByText("Title content")).toBeInTheDocument();
});
it("renders with custom className", () => {
render(Title);
const title = screen.getByText("Title");
expect(title).toHaveClass("custom-title");
});
it("renders with different heading levels", () => {
const { rerender } = render(Title);
let title = screen.getByText("Title");
expect(title.tagName).toBe("H1");
rerender(Title);
title = screen.getByText("Title");
expect(title.tagName).toBe("H2");
rerender(Title);
title = screen.getByText("Title");
expect(title.tagName).toBe("H3");
});
it("defaults to h3", () => {
render(Default Title);
const title = screen.getByText("Default Title");
expect(title.tagName).toBe("H3");
});
it("forwards ref correctly", () => {
const ref = React.createRef();
render(Title);
expect(ref.current).toBeInstanceOf(HTMLHeadingElement);
});
});
describe("CardContent", () => {
it("renders with children", () => {
render(Content here);
expect(screen.getByText("Content here")).toBeInTheDocument();
});
it("renders with custom className", () => {
render(Content);
const content = screen.getByText("Content");
expect(content).toHaveClass("custom-content");
});
it("has proper default styling", () => {
render(Content);
const content = screen.getByText("Content");
expect(content).toHaveClass("p-6", "pt-0");
});
it("handles noPadding prop", () => {
render(No padding);
const content = screen.getByText("No padding");
expect(content).not.toHaveClass("p-6", "pt-0");
});
it("forwards ref correctly", () => {
const ref = React.createRef();
render(Content);
expect(ref.current).toBeInstanceOf(HTMLDivElement);
});
});
describe("CardFooter", () => {
it("renders with children", () => {
render(Footer content);
expect(screen.getByText("Footer content")).toBeInTheDocument();
});
it("renders with custom className", () => {
render(Footer);
const footer = screen.getByText("Footer");
expect(footer).toHaveClass("custom-footer");
});
it("has proper default styling", () => {
render(Footer);
const footer = screen.getByText("Footer");
expect(footer).toHaveClass("flex", "items-center", "p-6", "pt-0");
});
it("handles different justify options", () => {
const { rerender } = render(Start);
let footer = screen.getByText("Start");
expect(footer).toHaveClass("justify-start");
rerender(Center);
footer = screen.getByText("Center");
expect(footer).toHaveClass("justify-center");
rerender(End);
footer = screen.getByText("End");
expect(footer).toHaveClass("justify-end");
rerender(Between);
footer = screen.getByText("Between");
expect(footer).toHaveClass("justify-between");
});
it("forwards ref correctly", () => {
const ref = React.createRef();
render(Footer);
expect(ref.current).toBeInstanceOf(HTMLDivElement);
});
});