From 2d0861a2e75e88910a6ef35429d6aebc44fee7d8 Mon Sep 17 00:00:00 2001 From: SJCoetz Date: Sun, 26 Jul 2026 22:33:37 +0200 Subject: [PATCH] test(quote-form): wire fetch mocks for new /api/v1/quotes/next-number call MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The form's deprecated generateQuoteNumber() was swapped for fetch('/api/v1/quotes/next-number'). Three tests' setups pointed fetchMock only at the upload response — and one asserted fetchMock was never called (it now always is, for next-number). - 'uploads per-item files' + 'shows a partial failure': chain two mockResolvedValueOnce responses (next-number, then upload). - 'allows a company-only selection': one mockResolvedValueOnce for next-number. - 'stops later uploads when a quote item create fails': add the next-number mock and replace 'not.toHaveBeenCalled()' with 'toHaveBeenCalledTimes(1)' plus an explicit assertion on the URL — item-create branch throws before the upload fetch, so the original negative assertion was always-true and is now wrong. The generateQuoteNumberMock in the hoisted block is now dead (the form no longer imports it). Left in place to keep this commit narrow; safe to delete in a future PR alongside the deprecated function in src/lib/pb-helpers.ts (which now has zero callers). --- src/components/quote-form.test.tsx | 49 ++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/src/components/quote-form.test.tsx b/src/components/quote-form.test.tsx index 0e3124d..5f13cff 100644 --- a/src/components/quote-form.test.tsx +++ b/src/components/quote-form.test.tsx @@ -113,10 +113,19 @@ describe("QuoteForm", () => { createMock .mockResolvedValueOnce({ id: "quote-1" }) .mockResolvedValueOnce({ id: "item-1" }); - fetchMock.mockResolvedValue({ - ok: true, - json: async () => ({ id: "att-1" }), - }); + // The form now hits /api/v1/quotes/next-number before PB create, + // and /api/v1/quotes/items//attachments/upload after. Queue + // both responses so the first fetch call gets the next-number + // shape and the second gets the upload shape. + fetchMock + .mockResolvedValueOnce({ + ok: true, + json: async () => ({ quote_number: "Q-2026-07-001" }), + }) + .mockResolvedValueOnce({ + ok: true, + json: async () => ({ id: "att-1" }), + }); render(); @@ -165,6 +174,11 @@ describe("QuoteForm", () => { it("allows a company-only selection when creating a quote", async () => { createMock.mockResolvedValueOnce({ id: "quote-2" }).mockResolvedValueOnce({ id: "item-2" }); + // The form hits /api/v1/quotes/next-number before PB create. + fetchMock.mockResolvedValueOnce({ + ok: true, + json: async () => ({ quote_number: "Q-2026-07-001" }), + }); const onSaved = vi.fn(); const onOpenChange = vi.fn(); @@ -201,6 +215,12 @@ describe("QuoteForm", () => { createMock .mockResolvedValueOnce({ id: "quote-1" }) .mockRejectedValueOnce(new Error("item create failed")); + // The form hits /api/v1/quotes/next-number before PB create. + // The item-create then fails, so the upload fetch is NOT called. + fetchMock.mockResolvedValueOnce({ + ok: true, + json: async () => ({ quote_number: "Q-2026-07-001" }), + }); render(); @@ -220,7 +240,10 @@ describe("QuoteForm", () => { fireEvent.click(screen.getByRole("button", { name: /create quote/i })); await waitFor(() => { - expect(fetchMock).not.toHaveBeenCalled(); + // Next-number WAS called before PB create; the upload + // fetch was NOT called because the item-create branch + // threw before reaching it. + expect(fetchMock).toHaveBeenCalledTimes(1); expect(toastErrorMock).toHaveBeenCalledWith( expect.stringMatching(/partially created/i), ); @@ -231,11 +254,17 @@ describe("QuoteForm", () => { createMock .mockResolvedValueOnce({ id: "quote-1" }) .mockResolvedValueOnce({ id: "item-1" }); - fetchMock.mockResolvedValue({ - ok: false, - status: 500, - json: async () => ({ error: "Upload failed" }), - }); + // First fetch: next-number (success). Second fetch: upload (500). + fetchMock + .mockResolvedValueOnce({ + ok: true, + json: async () => ({ quote_number: "Q-2026-07-001" }), + }) + .mockResolvedValueOnce({ + ok: false, + status: 500, + json: async () => ({ error: "Upload failed" }), + }); render();