fix(test): wire SendQuoteModal stub to props.onSend

The SendQuoteModal test stub had a noop triggerSend placeholder that
never called props.onSend, so the click on the modal's "Submit modal"
button would silently fail. Tests 3-7 (which assert on fetch /
refresh / toast effects from sendApprove) would all hang even after
Task 2 implements the page correctly.

Fix: call props.onSend directly from the button's onClick. Drop the
unused triggerSend hoisted spy. Tighten the stub's props type to
SendQuoteModalProps so Task 2's prop-name typos surface as TS errors.

Also widen the PB mock fallback's getList to return the full envelope
(items + totalItems + totalPages) so Task 2 reading quote_attachments
or any other collection doesn't crash on undefined totalItems.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Dave 2026-08-27 00:46:34 +02:00
parent 097ada09f8
commit 27f60e14ec

View file

@ -1,4 +1,5 @@
import { describe, it, expect, beforeEach, vi } from "vitest";
import * as React from "react";
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import ApprovalsPage from "./page";
@ -9,13 +10,11 @@ const {
refreshMock,
pushMock,
captureModalProps,
triggerSend,
} = vi.hoisted(() => ({
getListMock: vi.fn(),
refreshMock: vi.fn(),
pushMock: vi.fn(),
captureModalProps: vi.fn(),
triggerSend: vi.fn(),
}));
// Stub the PocketBase client so we can drive list contents from the test.
@ -29,7 +28,14 @@ vi.mock("@/lib/pocketbase", () => ({
if (name === "quote_items") {
return { getFullList: async () => [] };
}
return { getFullList: async () => [], getList: async () => ({ items: [] }) };
// Defensive fallback for any other collection the page or
// SendQuoteModal may read in Tasks 2-3 (e.g. quote_attachments,
// users). Match the PB envelope shape so code that consumes
// totalItems / totalPages doesn't crash on undefined.
return {
getFullList: async () => [],
getList: async () => ({ items: [], totalItems: 0, totalPages: 1 }),
};
},
}),
}));
@ -40,15 +46,16 @@ vi.mock("next/navigation", () => ({
}));
// Replace SendQuoteModal with a thin stub. The test grabs the props the
// page passed in via captureModalProps; triggerSend becomes the onSend
// prop, so tests can fire the modal submit callback directly.
// page passed in via captureModalProps; clicking the test's "Submit
// modal" button calls props.onSend directly, which is the page's
// sendApprove callback — so the fetch / refresh / toast assertions
// downstream observe real effects.
type SendQuoteModalProps = React.ComponentProps<
typeof import("@/components/quotes/send-quote-modal").SendQuoteModal
>;
vi.mock("@/components/quotes/send-quote-modal", () => ({
SendQuoteModal: (props: {
open: boolean;
submitLabel?: string;
onSend: (p: unknown) => Promise<void>;
quote: { id: string };
}) => {
SendQuoteModal: (props: SendQuoteModalProps) => {
captureModalProps(props);
if (!props.open) return null;
return (
@ -58,7 +65,7 @@ vi.mock("@/components/quotes/send-quote-modal", () => ({
<button
type="button"
onClick={() =>
triggerSend({
props.onSend({
to: "buyer@example.com",
cc: "",
bcc: "",