import { render, screen, within } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { describe, expect, it, vi } from "vitest"; import type { ConfirmRow } from "@/lib/server/collection-confirm-service"; import { makeAiRow, makeCheck } from "./ai-fixtures"; import { ItemsTable } from "./items-table"; const plain: ConfirmRow = { id: "row-p", sku: "SK-9", description: "Plain", order_qty: 1, qty_collected: 1, cost_price: 1, surplus_qty: 0, }; const noop = vi.fn(); describe("ItemsTable AI layer", () => { it("renders the original six headers and no AI/tick/robot UI without AI data", () => { render(); const headers = screen.getAllByRole("columnheader").map((h) => h.textContent); expect(headers).toEqual(["SKU", "Description", "Ord", "Coll", "Cost", "Surplus"]); expect(screen.queryByLabelText(/^SKU: /)).not.toBeInTheDocument(); expect(screen.queryByTestId("ai-sku-robot-row-p")).not.toBeInTheDocument(); expect(screen.queryByText(/add to pos/i)).not.toBeInTheDocument(); }); it("adds AI and POS headers when a row has ai_check", () => { render(); const headers = screen.getAllByRole("columnheader").map((h) => h.textContent); expect(headers).toEqual(["SKU", "Description", "Ord", "Coll", "Cost", "Surplus", "AI", "POS"]); }); it("renders four ticks with aria-labels from computeLineTicks and ours-vs-document titles", () => { const row = makeAiRow({ sku: "SK-001", qty_collected: 9, cost_price: 45, ai_check: makeCheck({ same_item: "likely", doc_line: { line_no: 1, code: "SK-001", description: "Totally different thing", quantity: 10, unit: "ea", unit_price: null, line_total: null, to_follow: null, price_note: "", }, }), }); render(); expect(screen.getByLabelText(/^SKU: matches/)).toBeInTheDocument(); expect(screen.getByLabelText(/^Qty: doesn't match/)).toBeInTheDocument(); expect(screen.getByLabelText(/^Desc: have a look/)).toBeInTheDocument(); expect(screen.getByLabelText(/^Price: can't check/)).toBeInTheDocument(); expect(screen.getByLabelText(/^Qty: doesn't match/)).toHaveAttribute( "title", expect.stringContaining("9"), ); expect(screen.getByLabelText(/^Price: can't check — Ours: R45.00/)).toBeInTheDocument(); const desc = screen.getByLabelText(/^Desc: have a look/).getAttribute("title") ?? ""; expect(desc).toContain("Item 1"); expect(desc).toContain("Totally different thing"); }); it("a 'different' verdict is all red even when the code matches", () => { const row = makeAiRow({ ai_check: makeCheck({ same_item: "different" }) }); render(); expect(screen.getByLabelText(/^SKU: doesn't match/)).toBeInTheDocument(); expect(screen.getByLabelText(/^Desc: doesn't match/)).toBeInTheDocument(); }); it("shows the amber label", () => { const row = makeAiRow({ ai_check: makeCheck({ same_item: "likely" }) }); render(); expect(screen.getByLabelText(/^Desc: have a look/)).toBeInTheDocument(); }); it("shows the robot next to the SKU only while the SKU equals sku_filled_value", () => { const filled = makeAiRow({ id: "a", sku: "SK-001", ai_check: makeCheck({ sku_filled_value: "SK-001" }) }); const human = makeAiRow({ id: "b", sku: "HUMAN", ai_check: makeCheck({ sku_filled_value: "SK-001" }) }); render(); expect(screen.getByTestId("ai-sku-robot-a")).toBeInTheDocument(); expect(screen.queryByTestId("ai-sku-robot-b")).not.toBeInTheDocument(); }); it("stock rows: shows, ticks and robot-marks the line's own SKU (line_sku)", () => { const stock = makeAiRow({ id: "s1", source: "stock", sku: undefined, line_sku: "SK-001", ai_check: makeCheck({ sku_filled_value: "SK-001" }), }); render(); const rowEl = screen.getByTestId("ai-ticks-s1").closest("tr")!; expect(within(rowEl).getByText("SK-001")).toBeInTheDocument(); expect(screen.getByTestId("ai-sku-robot-s1")).toBeInTheDocument(); expect(screen.getByLabelText(/^SKU: matches/)).toBeInTheDocument(); }); it("shows In POS with a stock link when in_pos", () => { const row = makeAiRow({ in_pos: true, pos_stock_item_id: "stock-77" }); render(); const link = screen.getByRole("link", { name: /in pos/i }); expect(link).toHaveAttribute("href", "/stock/stock-77"); expect(screen.queryByRole("button", { name: /add to pos/i })).not.toBeInTheDocument(); }); it("calls onAddToPos with the row id when not in POS", async () => { const onAdd = vi.fn(); render(); await userEvent.click(screen.getByRole("button", { name: /add to pos/i })); expect(onAdd).toHaveBeenCalledWith("row-1"); }); it("disables Add to POS on a row without a SKU (Set a SKU first)", async () => { const onAdd = vi.fn(); render( , ); const button = screen.getByRole("button", { name: /add to pos/i }); expect(button).toBeDisabled(); expect(button).toHaveAttribute("title", "Set a SKU first"); await userEvent.click(button); expect(onAdd).not.toHaveBeenCalled(); }); it("renders no tick icons for a row without ai_check inside an AI table", () => { render( , ); const rowEl = screen.getByText("Plain").closest("tr")!; expect(within(rowEl).queryByLabelText(/^SKU: /)).not.toBeInTheDocument(); }); });