From c14d07ecc0a504385562c7fceff1dc375c831627 Mon Sep 17 00:00:00 2001 From: SJCoetz Date: Mon, 3 Aug 2026 02:32:44 +0200 Subject: [PATCH] test(collection-confirm): add confirm-invoice-dialog tests Co-Authored-By: Claude Co-authored-by: Orca --- .../confirm-invoice-dialog.test.tsx | 210 ++++++++++++++++++ 1 file changed, 210 insertions(+) create mode 100644 src/components/collection-confirm/confirm-invoice-dialog.test.tsx diff --git a/src/components/collection-confirm/confirm-invoice-dialog.test.tsx b/src/components/collection-confirm/confirm-invoice-dialog.test.tsx new file mode 100644 index 0000000..14ea609 --- /dev/null +++ b/src/components/collection-confirm/confirm-invoice-dialog.test.tsx @@ -0,0 +1,210 @@ +import { render, screen, waitFor, within } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { afterEach, describe, expect, it, vi } from "vitest"; + +import { ConfirmInvoiceDialog } from "./confirm-invoice-dialog"; + +const costOverrides = { "row-1": 50 }; + +function getDialog() { + return screen.getByRole("dialog"); +} + +function getDateInput(dialog = getDialog()) { + return within(dialog).getByLabelText(/Supplier invoice date/i); +} + +function getNumberInput(dialog = getDialog()) { + return within(dialog).getByLabelText(/Supplier invoice number/i); +} + +function getSubmitButton(dialog = getDialog()) { + return within(dialog).getByRole("button", { name: /^Confirm(?: \d+ items)?$/i }); +} + +afterEach(() => { + vi.restoreAllMocks(); +}); + +describe("ConfirmInvoiceDialog", () => { + it("renders the row and cost override summary", () => { + render( + , + ); + + expect( + within(getDialog()).getByText(/Confirming 2 rows/i), + ).toHaveTextContent("Confirming 2 rows · 1 cost override applied"); + }); + + it("calls onSubmit with the invoice details and cost overrides", async () => { + const user = userEvent.setup(); + const onSubmit = vi.fn(); + render( + , + ); + + await user.type(getDateInput(), "2026-08-03"); + await user.type(getNumberInput(), "INV-001"); + await user.click(getSubmitButton()); + + expect(onSubmit).toHaveBeenCalledTimes(1); + expect(onSubmit).toHaveBeenCalledWith({ + invoice: { date: "2026-08-03", number: "INV-001" }, + costOverrides, + }); + }); + + it("disables submit when the date is empty", async () => { + const user = userEvent.setup(); + render( + , + ); + + await user.type(getNumberInput(), "INV-001"); + + expect(getSubmitButton()).toBeDisabled(); + }); + + it("disables submit when the invoice number is empty", async () => { + const user = userEvent.setup(); + render( + , + ); + + await user.type(getDateInput(), "2026-08-03"); + + expect(getSubmitButton()).toBeDisabled(); + }); + + it("disables submit when both invoice fields are empty", () => { + render( + , + ); + + expect(getSubmitButton()).toBeDisabled(); + }); + + it("trims the invoice number before submitting", async () => { + const user = userEvent.setup(); + const onSubmit = vi.fn(); + render( + , + ); + + await user.type(getNumberInput(), " INV-001 "); + await user.click(getSubmitButton()); + + expect(onSubmit).toHaveBeenCalledTimes(1); + expect(onSubmit.mock.calls[0][0].invoice.number).toBe("INV-001"); + }); + + it("uses singular row and plural zero-overrides labels", () => { + render( + , + ); + + expect( + within(getDialog()).getByText(/Confirming 1 row/i), + ).toHaveTextContent("Confirming 1 row · 0 cost overrides applied"); + }); + + it("re-applies updated defaults when reopened", async () => { + const onOpenChange = vi.fn(); + const onSubmit = vi.fn(); + const { rerender } = render( + , + ); + + expect(getDateInput()).toHaveValue("2026-08-01"); + expect(getNumberInput()).toHaveValue("INV-OLD"); + + rerender( + , + ); + + await waitFor(() => + expect(screen.queryByRole("dialog")).not.toBeInTheDocument(), + ); + + rerender( + , + ); + + const reopenedDialog = await screen.findByRole("dialog"); + await waitFor(() => { + expect(getDateInput(reopenedDialog)).toHaveValue("2026-08-02"); + expect(getNumberInput(reopenedDialog)).toHaveValue("INV-NEW"); + }); + }); + + it("closes the dialog when Cancel is clicked", async () => { + const user = userEvent.setup(); + const onOpenChange = vi.fn(); + render( + , + ); + + await user.click(within(getDialog()).getByRole("button", { name: /Cancel/i })); + + expect(onOpenChange).toHaveBeenCalledWith(false); + }); +});