import React from "react";
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { Input } from "../Input";
describe("Input", () => {
// Basic rendering tests
describe("Rendering", () => {
it("renders input with default props", () => {
render();
const input = screen.getByRole("textbox");
expect(input).toBeInTheDocument();
expect(input).toHaveAttribute("type", "text");
});
it("renders with custom className", () => {
render();
const input = screen.getByRole("textbox");
expect(input.parentElement?.parentElement).toHaveClass("custom-class");
});
it("renders with different input types", () => {
const { rerender } = render();
let input = screen.getByRole("textbox");
expect(input).toHaveAttribute("type", "email");
rerender();
input = screen.getByDisplayValue("") as HTMLInputElement;
expect(input).toHaveAttribute("type", "password");
rerender();
input = screen.getByRole("spinbutton");
expect(input).toHaveAttribute("type", "number");
});
it("renders with different sizes", () => {
const { rerender } = render();
let input = screen.getByRole("textbox");
expect(input).toHaveClass("h-8");
rerender();
input = screen.getByRole("textbox");
expect(input).toHaveClass("h-10");
rerender();
input = screen.getByRole("textbox");
expect(input).toHaveClass("h-12");
});
});
// Props tests
describe("Props", () => {
it("handles placeholder", () => {
render();
const input = screen.getByPlaceholderText("Enter text");
expect(input).toBeInTheDocument();
});
it("handles value and defaultValue", () => {
const { unmount } = render(
{}} />
);
let input = screen.getByDisplayValue("controlled");
expect(input).toBeInTheDocument();
unmount();
render();
input = screen.getByDisplayValue("uncontrolled");
expect(input).toBeInTheDocument();
});
it("handles disabled state", () => {
render();
const input = screen.getByRole("textbox");
expect(input).toBeDisabled();
});
it("handles readOnly state", () => {
render();
const input = screen.getByRole("textbox");
expect(input).toHaveAttribute("readonly");
});
it("handles required state", () => {
render();
const input = screen.getByRole("textbox");
expect(input).toHaveAttribute("required");
});
it("handles error state", () => {
render();
const input = screen.getByRole("textbox");
expect(input).toHaveClass("border-destructive");
expect(screen.getByText("Error message")).toBeInTheDocument();
});
});
// Label and help text tests
describe("Label and Help Text", () => {
it("renders with label", () => {
render();
const label = screen.getByText("Username");
const input = screen.getByRole("textbox");
expect(label).toBeInTheDocument();
expect(input).toHaveAttribute("id");
expect(label).toHaveAttribute("for", input.getAttribute("id"));
});
it("renders required indicator with label", () => {
render();
expect(screen.getByText("*")).toBeInTheDocument();
});
it("renders help text", () => {
render();
expect(screen.getByText("Enter your username")).toBeInTheDocument();
});
it("renders error message with proper ARIA", () => {
render();
const errorElement = screen.getByText("Username is required");
expect(errorElement).toHaveAttribute("role", "alert");
expect(errorElement).toHaveAttribute("aria-live", "polite");
});
});
// Icon tests
describe("Icons", () => {
it("renders with left icon", () => {
const icon = 🔍;
render();
expect(screen.getByTestId("left-icon")).toBeInTheDocument();
});
it("renders with right icon", () => {
const icon = ❯;
render();
expect(screen.getByTestId("right-icon")).toBeInTheDocument();
});
});
// Password toggle tests
describe("Password Toggle", () => {
it("renders password toggle button", () => {
render();
const toggleButton = screen.getByLabelText("Show password");
expect(toggleButton).toBeInTheDocument();
});
it("toggles password visibility", async () => {
const user = userEvent.setup();
render();
const input = screen.getByRole("textbox");
const toggleButton = screen.getByLabelText("Show password");
expect(input).toHaveAttribute("type", "password");
await user.click(toggleButton);
expect(input).toHaveAttribute("type", "text");
expect(screen.getByLabelText("Hide password")).toBeInTheDocument();
const hideButton = screen.getByLabelText("Hide password");
await user.click(hideButton);
expect(input).toHaveAttribute("type", "password");
expect(screen.getByLabelText("Show password")).toBeInTheDocument();
});
});
// Interaction tests
describe("Interactions", () => {
it("calls onChange when value changes", async () => {
const handleChange = jest.fn();
const user = userEvent.setup();
render();
const input = screen.getByRole("textbox");
await user.type(input, "hello");
expect(handleChange).toHaveBeenCalledTimes(5); // Called for each character
});
it("calls onFocus when focused", async () => {
const handleFocus = jest.fn();
const user = userEvent.setup();
render();
const input = screen.getByRole("textbox");
await user.click(input);
expect(handleFocus).toHaveBeenCalledTimes(1);
});
it("calls onBlur when blurred", async () => {
const handleBlur = jest.fn();
const user = userEvent.setup();
render();
const input = screen.getByRole("textbox");
await user.click(input);
await user.tab();
expect(handleBlur).toHaveBeenCalledTimes(1);
});
});
// Accessibility tests
describe("Accessibility", () => {
it("has proper ARIA attributes for error state", () => {
render();
const input = screen.getByRole("textbox");
expect(input).toHaveAttribute("aria-invalid", "true");
});
it("links help text with aria-describedby", () => {
render();
const input = screen.getByRole("textbox");
const helpTextId = input.getAttribute("aria-describedby");
expect(helpTextId).toBeTruthy();
expect(screen.getByText("Help text")).toHaveAttribute("id", helpTextId);
});
it("supports keyboard navigation", async () => {
const user = userEvent.setup();
render();
const input = screen.getByRole("textbox");
await user.tab();
expect(input).toHaveFocus();
});
});
// Edge cases
describe("Edge cases", () => {
it("forwards ref correctly", () => {
const ref = React.createRef();
render();
expect(ref.current).toBeInstanceOf(HTMLInputElement);
});
it("handles multiple class names", () => {
render();
const container =
screen.getByRole("textbox").parentElement?.parentElement;
expect(container).toHaveClass("class1", "class2");
});
});
});