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 <noreply@anthropic.com>
Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
parent
f99ab7cf1a
commit
5af04e4b60
1 changed files with 89 additions and 9 deletions
|
|
@ -5,19 +5,30 @@ vi.mock("@/components/ui/customer-search", () => ({
|
|||
CustomerSearch: ({
|
||||
onValueChange,
|
||||
value,
|
||||
customers,
|
||||
}: {
|
||||
onValueChange: (v: string) => void;
|
||||
value: string;
|
||||
customers: unknown[];
|
||||
}) => (
|
||||
<button
|
||||
type="button"
|
||||
data-testid="customer-search-mock"
|
||||
onClick={() => onValueChange("cust-new")}
|
||||
>
|
||||
{value || "(unset)"}
|
||||
</button>
|
||||
),
|
||||
}) => {
|
||||
// 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 (
|
||||
<button
|
||||
type="button"
|
||||
data-testid="customer-search-mock"
|
||||
data-next-id={nextId}
|
||||
onClick={() => onValueChange(nextId)}
|
||||
>
|
||||
{value || "(unset)"}
|
||||
</button>
|
||||
);
|
||||
},
|
||||
}));
|
||||
|
||||
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(<OrdersReviewModal {...props} />);
|
||||
|
||||
// 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(<OrdersReviewModal {...props} />);
|
||||
|
||||
// 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();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue