import { cleanup, render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import assert from "node:assert";
import {
describe, test, mock, afterEach,
} from "node:test";
import type { IChecker } from "../checker";
import { CheckerGroup } from "./index";
import type { ICheckerGroup, ICheckerGroupItemsGroup } from "./index";
describe("CheckerGroup", () => {
const mockItems: ICheckerGroup["items"] = [
{
name: "option1",
value: "1",
label: "Option 1",
isChecked: false,
},
{
name: "option2",
value: "2",
label: "Option 2",
isChecked: true,
},
{
name: "option3",
value: "3",
label: "Option 3",
isChecked: false,
},
];
const mockNestedItems: ICheckerGroup["items"] = [
{
label: "Group A",
items: [
{
name: "a1",
value: "a1",
label: "A1",
isChecked: false,
},
{
name: "a2",
value: "a2",
label: "A2",
isChecked: true,
},
],
},
{
label: "Group B",
items: [
{
name: "b1",
value: "b1",
label: "B1",
isChecked: false,
},
],
},
];
const radioItems: ICheckerGroup["items"] = [
{
name: "group1",
value: "opt1",
label: "Option 1",
isChecked: true,
},
{
name: "group1",
value: "opt2",
label: "Option 2",
isChecked: false,
},
];
/* COMMON */
afterEach(() => {
cleanup();
});
test("renders empty placeholder when items array is empty", () => {
render();
assert.notStrictEqual(screen.queryByText("No items available"), null);
const listItem = screen.getByRole("listitem");
assert.ok(listItem.className.includes("isEmpty")); // adjust if actual class name differs
});
test("renders checkboxes by default", () => {
render();
const checkboxes = screen.getAllByRole("checkbox");
assert.strictEqual(checkboxes.length, mockItems.length);
checkboxes.forEach((checker) => {
assert.strictEqual(checker.getAttribute("type"), "checkbox");
});
});
test("renders radio when type is 'radio'", () => {
render();
const radios = screen.getAllByRole("radio");
assert.strictEqual(radios.length, mockItems.length);
radios.forEach((radio) => {
assert.strictEqual(radio.getAttribute("type"), "radio");
});
});
test("renders nested groups with labels", () => {
render();
assert.notStrictEqual(screen.queryByText("Group A"), null);
assert.notStrictEqual(screen.queryByText("Group B"), null);
const checkboxes = screen.getAllByRole("checkbox");
const expectedLength = mockNestedItems.reduce((sum, currentGroup) => {
return sum + (currentGroup as ICheckerGroupItemsGroup).items.length;
}, 0);
assert.strictEqual(checkboxes.length, expectedLength);
});
/* HANDLERS */
test("calls onChange when checker is changed", async () => {
const user = userEvent.setup();
const onChange = mock.fn();
render();
const firstCheckbox = screen.getAllByRole("checkbox")[0];
await user.click(firstCheckbox);
assert.strictEqual(onChange.mock.calls.length, 1);
const args = onChange.mock.calls[0]?.arguments;
assert.ok(args);
const [ firstArg, secondArg ] = args;
assert.ok(firstArg && typeof firstArg === "object");
assert.ok("event" in firstArg);
assert.strictEqual(typeof secondArg, "function");
});
test("updates checkers state when onChange is not provided", async () => {
const user = userEvent.setup();
render();
const checkboxes = screen.getAllByRole("checkbox") as HTMLInputElement[];
assert.strictEqual(checkboxes[0].checked, false);
assert.strictEqual(checkboxes[1].checked, true);
await user.click(checkboxes[0]);
assert.strictEqual(checkboxes[0].checked, true);
assert.strictEqual(checkboxes[1].checked, true);
});
test("handles radio group behaviour correctly", async () => {
const user = userEvent.setup();
render();
const radios = screen.getAllByRole("radio") as HTMLInputElement[];
assert.strictEqual(radios[0].checked, true);
assert.strictEqual(radios[1].checked, false);
await user.click(radios[1]);
assert.strictEqual(radios[0].checked, false);
assert.strictEqual(radios[1].checked, true);
});
/* CHILDREN */
test("uses custom getItem function for rendering", () => {
const getItem = mock.fn>(({ item, checker }) => (
{checker}
Custom wrapper
));
render();
assert.strictEqual(getItem.mock.calls.length, 3);
mockItems.forEach((checker) => {
const testId = `custom-${(checker as IChecker).value}`;
assert.notStrictEqual(screen.queryByTestId(testId), null);
});
const wrappers = screen.getAllByText("Custom wrapper");
assert.strictEqual(wrappers.length, 3);
});
test("updates list when 'items' prop is changed", () => {
const { rerender } = render();
let checkboxes = screen.getAllByRole("checkbox");
assert.strictEqual(checkboxes.length, 1);
rerender();
checkboxes = screen.getAllByRole("checkbox");
assert.strictEqual(checkboxes.length, mockItems.length);
});
test("renders children instead of default content", () => {
render(
Custom content
);
assert.notStrictEqual(screen.queryByTestId("customContent"), null);
assert.strictEqual(screen.queryByRole("checkbox"), null);
});
});