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(); }); });