From 5af04e4b6046fbce195c66611fd3800d0d9e815b Mon Sep 17 00:00:00 2001 From: SJCoetz Date: Fri, 31 Jul 2026 15:31:07 +0200 Subject: [PATCH] test(orders): review modal cascade coverage Two new tests for the customer -> company/rep cascade: 1. Updates Company and Rep labels when the customer changes - renders with customer A (company 'Atlas Corp', rep 'Riya Patel') - clicks the customer-search mock (now picks the next non-selected customer from the list) - asserts the labels flip to B's values and A's are gone 2. Marks the modal dirty after a customer change cascades - asserts the Save button is disabled at baseline - after customer change, asserts it's enabled (proves the dirty check picks up the cascaded company/rep divergence) Also updates the CustomerSearch mock to compute the next non-selected customer id from the list (via data-next-id) so cascade tests can drive it without hardcoding 'cust-new'. Co-Authored-By: Claude Co-authored-by: Orca --- src/components/orders-review-modal.test.tsx | 98 +++++++++++++++++++-- 1 file changed, 89 insertions(+), 9 deletions(-) diff --git a/src/components/orders-review-modal.test.tsx b/src/components/orders-review-modal.test.tsx index 9db64ca..b6d2e35 100644 --- a/src/components/orders-review-modal.test.tsx +++ b/src/components/orders-review-modal.test.tsx @@ -5,19 +5,30 @@ vi.mock("@/components/ui/customer-search", () => ({ CustomerSearch: ({ onValueChange, value, + customers, }: { onValueChange: (v: string) => void; value: string; customers: unknown[]; - }) => ( - - ), + }) => { + // Surface the first non-selected customer's id as a data attribute + // so cascade tests can click it specifically. Falls back to + // "cust-new" when there's no other customer in the list. + const others = (customers as Array<{ id: string }>).filter( + (c) => c.id !== value, + ); + const nextId = others[0]?.id ?? "cust-new"; + return ( + + ); + }, })); vi.mock("sonner", () => ({ @@ -688,4 +699,73 @@ describe("OrdersReviewModal", () => { expect(rekeyedRow?.getAttribute("data-pending-removal")).toBe("false"); }); }); + + describe("customer cascade (company + rep)", () => { + const expandedCustomerA: Customer = { + ...baseCustomer, + id: "cust-A", + customer: "Alice Anderson", + company: "comp-A", + expand: { + company: { ...baseCompany, id: "comp-A", company: "Atlas Corp" }, + rep: { id: "user-1", name: "Riya Patel", email: "riya@atlas.test" }, + }, + }; + const expandedCustomerB: Customer = { + ...baseCustomer, + id: "cust-B", + customer: "Bob Brown", + company: "comp-B", + expand: { + company: { ...baseCompany, id: "comp-B", company: "Beacon Ltd" }, + rep: { id: "user-2", name: "Sam Jones", email: "sam@beacon.test" }, + }, + }; + + it("updates Company and Rep labels when the customer changes", () => { + const props = { + ...baseProps(), + customers: [expandedCustomerA, expandedCustomerB], + customer: expandedCustomerA, + order: { ...baseOrder, customer: "cust-A", company: "comp-A", rep: "Riya Patel" }, + }; + render(); + + // Baseline: Company and Rep show the persisted (A) values. + expect(screen.getByText("Atlas Corp")).toBeInTheDocument(); + expect(screen.getByText("Riya Patel")).toBeInTheDocument(); + expect(screen.queryByText("Beacon Ltd")).not.toBeInTheDocument(); + + // Pick customer B. The mock's data-next-id is computed from the + // customer list, so the click will pass cust-B. + fireEvent.click(screen.getByTestId("customer-search-mock")); + + // Company and Rep should now reflect customer B's expands. + expect(screen.getByText("Beacon Ltd")).toBeInTheDocument(); + expect(screen.getByText("Sam Jones")).toBeInTheDocument(); + // The original A values are no longer in the labels. + expect(screen.queryByText("Atlas Corp")).not.toBeInTheDocument(); + expect(screen.queryByText("Riya Patel")).not.toBeInTheDocument(); + }); + + it("marks the modal dirty after a customer change cascades", () => { + const props = { + ...baseProps(), + customers: [expandedCustomerA, expandedCustomerB], + customer: expandedCustomerA, + order: { ...baseOrder, customer: "cust-A", company: "comp-A", rep: "Riya Patel" }, + }; + render(); + + // Find the Save button by accessible name. The modal's primary + // save action is the only one in the footer. + const saveBefore = screen.getByRole("button", { name: /save/i }); + expect(saveBefore).toBeDisabled(); + + fireEvent.click(screen.getByTestId("customer-search-mock")); + + const saveAfter = screen.getByRole("button", { name: /save/i }); + expect(saveAfter).not.toBeDisabled(); + }); + }); });