|
|
|
@ -1,56 +1,75 @@ |
|
|
|
import { Checkbox } from "@components/UI/Checkbox/index.tsx"; |
|
|
|
import { cleanup, fireEvent, render, screen } from "@testing-library/react"; |
|
|
|
import type React from "react"; |
|
|
|
import { beforeEach, describe, expect, it, vi } from "vitest"; |
|
|
|
|
|
|
|
vi.mock("@components/UI/Label.tsx", () => ({ |
|
|
|
Label: ({ |
|
|
|
children, |
|
|
|
className, |
|
|
|
htmlFor, |
|
|
|
id, |
|
|
|
}: { |
|
|
|
children: React.ReactNode; |
|
|
|
className: string; |
|
|
|
htmlFor: string; |
|
|
|
id: string; |
|
|
|
}) => ( |
|
|
|
<label |
|
|
|
data-testid="label-component" |
|
|
|
className={className} |
|
|
|
htmlFor={htmlFor} |
|
|
|
id={id} |
|
|
|
> |
|
|
|
{children} |
|
|
|
</label> |
|
|
|
), |
|
|
|
})); |
|
|
|
|
|
|
|
describe("Checkbox", () => { |
|
|
|
beforeEach(cleanup); |
|
|
|
|
|
|
|
it("renders unchecked by default", () => { |
|
|
|
it("renders unchecked by default (uncontrolled)", () => { |
|
|
|
render(<Checkbox />); |
|
|
|
const checkbox = screen.getByRole("checkbox"); |
|
|
|
const presentation = screen.getByRole("presentation"); |
|
|
|
expect(checkbox).not.toBeChecked(); |
|
|
|
expect(screen.queryByText("Check")).not.toBeInTheDocument(); |
|
|
|
// unchecked -> no filled bg class
|
|
|
|
expect(presentation).not.toHaveClass("bg-slate-500"); |
|
|
|
}); |
|
|
|
|
|
|
|
it("renders checked when checked prop is true", () => { |
|
|
|
render(<Checkbox checked />); |
|
|
|
it("respects defaultChecked in uncontrolled mode", () => { |
|
|
|
render(<Checkbox defaultChecked />); |
|
|
|
expect(screen.getByRole("checkbox")).toBeChecked(); |
|
|
|
expect(screen.getByRole("presentation")).toBeInTheDocument(); |
|
|
|
}); |
|
|
|
|
|
|
|
it("calls onChange when clicked", () => { |
|
|
|
it("renders checked when controlled with checked=true", () => { |
|
|
|
render(<Checkbox checked />); |
|
|
|
const checkbox = screen.getByRole("checkbox"); |
|
|
|
const presentation = screen.getByRole("presentation"); |
|
|
|
expect(checkbox).toBeChecked(); |
|
|
|
expect(presentation).toHaveClass("bg-slate-500"); |
|
|
|
}); |
|
|
|
|
|
|
|
it("calls onChange when clicked (uncontrolled) and toggles DOM state", () => { |
|
|
|
const onChange = vi.fn(); |
|
|
|
render(<Checkbox onChange={onChange} />); |
|
|
|
|
|
|
|
fireEvent.click(screen.getByRole("presentation")); |
|
|
|
expect(onChange).toHaveBeenCalledWith(true); |
|
|
|
const checkbox = screen.getByRole("checkbox"); |
|
|
|
const presentation = screen.getByRole("presentation"); |
|
|
|
|
|
|
|
fireEvent.click(screen.getByRole("presentation")); |
|
|
|
expect(onChange).toHaveBeenCalledWith(false); |
|
|
|
fireEvent.click(presentation); |
|
|
|
expect(onChange).toHaveBeenLastCalledWith(true); |
|
|
|
expect(checkbox).toBeChecked(); |
|
|
|
|
|
|
|
fireEvent.click(presentation); |
|
|
|
expect(onChange).toHaveBeenLastCalledWith(false); |
|
|
|
expect(checkbox).not.toBeChecked(); |
|
|
|
}); |
|
|
|
|
|
|
|
it("controlled: calls onChange but does not toggle without prop update", () => { |
|
|
|
const onChange = vi.fn(); |
|
|
|
render(<Checkbox checked={false} onChange={onChange} />); |
|
|
|
|
|
|
|
const checkbox = screen.getByRole("checkbox"); |
|
|
|
const presentation = screen.getByRole("presentation"); |
|
|
|
|
|
|
|
fireEvent.click(presentation); |
|
|
|
expect(onChange).toHaveBeenLastCalledWith(true); |
|
|
|
// still unchecked because parent didn't update prop
|
|
|
|
expect(checkbox).not.toBeChecked(); |
|
|
|
}); |
|
|
|
|
|
|
|
it("controlled: reflects external prop changes after onChange", () => { |
|
|
|
const onChange = vi.fn(); |
|
|
|
const { rerender } = render(<Checkbox checked={false} onChange={onChange} />); |
|
|
|
|
|
|
|
const checkbox = screen.getByRole("checkbox"); |
|
|
|
const presentation = screen.getByRole("presentation"); |
|
|
|
|
|
|
|
fireEvent.click(presentation); |
|
|
|
expect(onChange).toHaveBeenLastCalledWith(true); |
|
|
|
|
|
|
|
// parent updates `checked` based on onChange
|
|
|
|
rerender(<Checkbox checked={true} onChange={onChange} />); |
|
|
|
expect(checkbox).toBeChecked(); |
|
|
|
expect(presentation).toHaveClass("bg-slate-500"); |
|
|
|
}); |
|
|
|
|
|
|
|
it("uses provided id", () => { |
|
|
|
@ -58,23 +77,16 @@ describe("Checkbox", () => { |
|
|
|
expect(screen.getByRole("checkbox").id).toBe("custom-id"); |
|
|
|
}); |
|
|
|
|
|
|
|
it("renders children in Label component", () => { |
|
|
|
it("renders children inside the label", () => { |
|
|
|
render(<Checkbox>Test Label</Checkbox>); |
|
|
|
expect(screen.getByTestId("label-component")).toHaveTextContent( |
|
|
|
"Test Label", |
|
|
|
); |
|
|
|
expect(screen.getByTestId("label-component")).toHaveTextContent("Test Label"); |
|
|
|
}); |
|
|
|
|
|
|
|
it("applies custom className", () => { |
|
|
|
it("applies custom className to wrapper label", () => { |
|
|
|
const { container } = render(<Checkbox className="custom-class" />); |
|
|
|
expect(container.firstChild).toHaveClass("custom-class"); |
|
|
|
}); |
|
|
|
|
|
|
|
it("applies labelClassName to Label", () => { |
|
|
|
render(<Checkbox labelClassName="label-class">Test</Checkbox>); |
|
|
|
expect(screen.getByTestId("label-component")).toHaveClass("label-class"); |
|
|
|
}); |
|
|
|
|
|
|
|
it("disables checkbox when disabled prop is true", () => { |
|
|
|
render(<Checkbox disabled />); |
|
|
|
expect(screen.getByRole("checkbox")).toBeDisabled(); |
|
|
|
@ -84,7 +96,6 @@ describe("Checkbox", () => { |
|
|
|
it("does not call onChange when disabled", () => { |
|
|
|
const onChange = vi.fn(); |
|
|
|
render(<Checkbox onChange={onChange} disabled />); |
|
|
|
|
|
|
|
fireEvent.click(screen.getByRole("presentation")); |
|
|
|
expect(onChange).not.toHaveBeenCalled(); |
|
|
|
}); |
|
|
|
@ -99,24 +110,19 @@ describe("Checkbox", () => { |
|
|
|
expect(screen.getByRole("checkbox")).toHaveAttribute("name", "test-name"); |
|
|
|
}); |
|
|
|
|
|
|
|
it("passes through additional props", () => { |
|
|
|
it("passes through additional props to the input", () => { |
|
|
|
render(<Checkbox data-testid="extra-prop" />); |
|
|
|
expect(screen.getByRole("checkbox")).toHaveAttribute( |
|
|
|
"data-testid", |
|
|
|
"extra-prop", |
|
|
|
); |
|
|
|
expect(screen.getByRole("checkbox")).toHaveAttribute("data-testid", "extra-prop"); |
|
|
|
}); |
|
|
|
|
|
|
|
it("toggles checked state correctly", () => { |
|
|
|
it("uncontrolled: toggles checked state when clicking the visual box", () => { |
|
|
|
render(<Checkbox />); |
|
|
|
const checkbox = screen.getByRole("checkbox"); |
|
|
|
const presentation = screen.getByRole("presentation"); |
|
|
|
|
|
|
|
expect(checkbox).not.toBeChecked(); |
|
|
|
|
|
|
|
fireEvent.click(presentation); |
|
|
|
expect(checkbox).toBeChecked(); |
|
|
|
|
|
|
|
fireEvent.click(presentation); |
|
|
|
expect(checkbox).not.toBeChecked(); |
|
|
|
}); |
|
|
|
|