import { describe, it, expect, vi } from "vitest";
import { render, screen, fireEvent } from "@testing-library/react";
import { filterVendors, vendorLabel, VendorSelect, type VendorOption } from "@/components/ui/vendor-select";
const VENDORS: VendorOption[] = [
{ id: "1", name: "Acme Marine Supplies", vendorId: "V-1001" },
{ id: "2", name: "Bluewater Engineering", vendorId: "V-2002" },
{ id: "3", name: "Coastal Spares", vendorId: null }, // unverified — no code
{ id: "4", name: "Delta Pumps", vendorId: "ACME-99" },
];
// ── Pure filter logic ─────────────────────────────────────────────────────────
describe("filterVendors", () => {
it("returns all vendors for an empty query", () => {
expect(filterVendors(VENDORS, "")).toHaveLength(4);
});
it("returns all vendors for a whitespace-only query", () => {
expect(filterVendors(VENDORS, " ")).toHaveLength(4);
});
it("matches by name (case-insensitive)", () => {
const res = filterVendors(VENDORS, "bluewater");
expect(res.map((v) => v.id)).toEqual(["2"]);
});
it("matches by code (vendorId), case-insensitive", () => {
const res = filterVendors(VENDORS, "v-2002");
expect(res.map((v) => v.id)).toEqual(["2"]);
});
it("matches on a code substring even when the name does not contain it", () => {
// "acme" appears in vendor #1's name AND in vendor #4's code (ACME-99)
const res = filterVendors(VENDORS, "acme");
expect(res.map((v) => v.id).sort()).toEqual(["1", "4"]);
});
it("finds an unverified vendor (null code) by name only", () => {
expect(filterVendors(VENDORS, "coastal").map((v) => v.id)).toEqual(["3"]);
// ...and never crashes on the null code
expect(filterVendors(VENDORS, "9999")).toHaveLength(0);
});
it("returns no matches for an unrelated query", () => {
expect(filterVendors(VENDORS, "zzz")).toHaveLength(0);
});
});
describe("vendorLabel", () => {
it("formats a verified vendor as name (CODE)", () => {
expect(vendorLabel(VENDORS[0])).toBe("Acme Marine Supplies (V-1001)");
});
it("formats an unverified vendor as name (unverified)", () => {
expect(vendorLabel(VENDORS[2])).toBe("Coastal Spares (unverified)");
});
});
// ── Component behaviour ───────────────────────────────────────────────────────
describe("VendorSelect", () => {
it("posts a hidden vendorId input with the empty default", () => {
const { container } = render();
const hidden = container.querySelector('input[name="vendorId"]') as HTMLInputElement;
expect(hidden).toBeTruthy();
expect(hidden.value).toBe("");
});
it("honours initialValue", () => {
const { container } = render();
const hidden = container.querySelector('input[name="vendorId"]') as HTMLInputElement;
expect(hidden.value).toBe("2");
// Trigger shows the formatted label
expect(screen.getByText("Bluewater Engineering (V-2002)")).toBeTruthy();
});
it("opens and filters the list by typed query, then selects a vendor", () => {
const onChange = vi.fn();
const { container } = render();
fireEvent.click(screen.getByRole("button"));
const search = screen.getByPlaceholderText("Search by name or code…");
fireEvent.change(search, { target: { value: "v-2002" } });
// Only the matching vendor option is in the list
expect(screen.getByText("Bluewater Engineering")).toBeTruthy();
expect(screen.queryByText("Acme Marine Supplies")).toBeNull();
fireEvent.mouseDown(screen.getByText("Bluewater Engineering"));
expect(onChange).toHaveBeenCalledWith("2");
const hidden = container.querySelector('input[name="vendorId"]') as HTMLInputElement;
expect(hidden.value).toBe("2");
});
it("keeps 'No vendor selected' selectable to clear the choice", () => {
const onChange = vi.fn();
const { container } = render(
);
// The trigger shows the current selection's label; click it to open.
fireEvent.click(screen.getByText("Acme Marine Supplies (V-1001)"));
// The empty option is always present, even before typing
fireEvent.mouseDown(screen.getByText("No vendor selected"));
expect(onChange).toHaveBeenLastCalledWith("");
const hidden = container.querySelector('input[name="vendorId"]') as HTMLInputElement;
expect(hidden.value).toBe("");
});
it("shows an empty-state message when nothing matches", () => {
render();
fireEvent.click(screen.getByRole("button"));
fireEvent.change(screen.getByPlaceholderText("Search by name or code…"), {
target: { value: "zzz" },
});
expect(screen.getByText(/No vendors match/)).toBeTruthy();
});
});