81 lines
2.9 KiB
TypeScript
81 lines
2.9 KiB
TypeScript
import "@testing-library/jest-dom";
|
|
import { fireEvent, render, screen } from "@testing-library/react";
|
|
import { MemoryRouter } from "react-router";
|
|
import { describe, expect, it, vi } from "vitest";
|
|
import InputCombobox from "./InputCombobox";
|
|
|
|
vi.mock("react-i18next", () => ({
|
|
useTranslation: () => ({
|
|
t: (key: string) => {
|
|
const translations: Record<string, string> = {
|
|
"shared.clear": "Clear",
|
|
"shared.new": "New",
|
|
"shared.noRecords": "No records",
|
|
"shared.searchDot": "Search...",
|
|
"shared.select": "Select",
|
|
"shared.selectedCount": "selected",
|
|
};
|
|
return translations[key] ?? key;
|
|
},
|
|
}),
|
|
}));
|
|
|
|
const manyOptions = Array.from({ length: 12 }, (_, index) => ({
|
|
value: `option-${index}`,
|
|
name: `Option ${index}`,
|
|
}));
|
|
|
|
function renderCombobox(ui: React.ReactElement) {
|
|
return render(<MemoryRouter>{ui}</MemoryRouter>);
|
|
}
|
|
|
|
describe("InputCombobox", () => {
|
|
it("keeps the clear control reachable with a visible keyboard focus style", () => {
|
|
const onClear = vi.fn();
|
|
|
|
renderCombobox(<InputCombobox title="Workspace" value={["option-1"]} options={[{ value: "option-1", name: "Acme workspace" }]} onClear={onClear} />);
|
|
|
|
const clearButton = screen.getByRole("button", { name: "Clear" });
|
|
|
|
expect(clearButton).toHaveClass("min-h-8", "shrink-0", "focus-visible:ring-2");
|
|
|
|
fireEvent.click(clearButton);
|
|
|
|
expect(onClear).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it("gives the icon-only new button an accessible name and stable hit area", () => {
|
|
const onNew = vi.fn();
|
|
|
|
renderCombobox(<InputCombobox title="Workspace" options={manyOptions} onNew={onNew} />);
|
|
|
|
const newButton = screen.getByRole("button", { name: "New" });
|
|
|
|
expect(newButton).toHaveClass("h-9", "w-10", "shrink-0", "focus-visible:ring-2");
|
|
|
|
fireEvent.click(newButton);
|
|
|
|
expect(onNew).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it("keeps route-based icon-only creation and the popover constrained on mobile", () => {
|
|
renderCombobox(<InputCombobox title="Workspace" options={manyOptions} onNewRoute="/admin/workspaces/new" />);
|
|
|
|
const newLink = screen.getByRole("link", { name: "New" });
|
|
|
|
expect(newLink).toHaveAttribute("href", "/admin/workspaces/new");
|
|
expect(newLink).toHaveClass("h-9", "w-10", "shrink-0", "focus-visible:ring-2");
|
|
|
|
fireEvent.click(screen.getByText("Select...").closest("button") as HTMLButtonElement);
|
|
|
|
expect(screen.getByRole("listbox").parentElement).toHaveClass("max-w-[calc(100vw-2rem)]", "overflow-hidden");
|
|
expect(screen.getByRole("listbox")).toHaveClass("max-h-72", "overflow-auto");
|
|
});
|
|
|
|
it("localizes collapsed multi-select counts instead of appending hard-coded English", () => {
|
|
renderCombobox(<InputCombobox title="Workspace" value={["option-1", "option-2", "option-3"]} options={manyOptions} minDisplayCount={3} />);
|
|
|
|
expect(screen.getByRole("button", { name: /workspace selected/i })).toBeInTheDocument();
|
|
});
|
|
});
|