diff --git a/src/app/v/[token]/dispatch/_components/uplift-requests-section.tsx b/src/app/v/[token]/dispatch/_components/uplift-requests-section.tsx index 820c186e..e157b2bf 100644 --- a/src/app/v/[token]/dispatch/_components/uplift-requests-section.tsx +++ b/src/app/v/[token]/dispatch/_components/uplift-requests-section.tsx @@ -15,6 +15,8 @@ import { } from "@/app/v/[token]/dispatch/_components/use-uplift-evidence"; const ACTIVE_STATUSES = new Set(["Pending", "ChangesRequested"]); +const UPLIFT_REQUEST_PERMISSION_DENIAL = "Your role can't request uplifts on this work order."; +const UPLIFT_REQUEST_ERROR_FALLBACK = "Unable to request the NTE uplift. Please try again."; type UpliftRequestsSectionProps = { token: string; @@ -41,6 +43,12 @@ function scanErrorMessage(scan: EvidenceScanState): string { return "Security scan timed out. Please try again once the file finishes scanning."; } +function mapUpliftRequestError(error: unknown): string { + return error instanceof Error && error.message === UPLIFT_REQUEST_PERMISSION_DENIAL + ? UPLIFT_REQUEST_PERMISSION_DENIAL + : UPLIFT_REQUEST_ERROR_FALLBACK; +} + function useVendorUpliftActions({ token, dispatchId, @@ -97,11 +105,7 @@ function useVendorUpliftActions({ setInfoMessage("No approval required — the existing NTE already covers this amount."); } } catch (error) { - setFormError( - error instanceof Error && error.message - ? error.message - : "Unable to request the NTE uplift. Please try again.", - ); + setFormError(mapUpliftRequestError(error)); } finally { setSubmitting(false); } diff --git a/src/test/app/v/uplift-requests-section.test.tsx b/src/test/app/v/uplift-requests-section.test.tsx index 306848fd..51416c19 100644 --- a/src/test/app/v/uplift-requests-section.test.tsx +++ b/src/test/app/v/uplift-requests-section.test.tsx @@ -8,6 +8,26 @@ afterEach(() => { vi.restoreAllMocks(); }); +function renderCreateForm() { + renderWithProviders( + undefined)} + />, + { withAuth: false }, + ); +} + +function submitCreateForm() { + fireEvent.change(screen.getByLabelText("New total NTE"), { target: { value: "100" } }); + fireEvent.click(screen.getByRole("button", { name: "Request uplift" })); +} + describe("UpliftRequestsSection create workflow", () => { it("renders the server denial message when requestUplifts is denied", async () => { const denialMessage = "Your role can't request uplifts on this work order."; @@ -18,26 +38,32 @@ describe("UpliftRequestsSection create workflow", () => { }), ); - renderWithProviders( - undefined)} - />, - { withAuth: false }, - ); + renderCreateForm(); + submitCreateForm(); - fireEvent.change(screen.getByLabelText("New total NTE"), { target: { value: "100" } }); - fireEvent.click(screen.getByRole("button", { name: "Request uplift" })); - - await waitFor(() => expect(screen.getByRole("alert")).toHaveTextContent(denialMessage)); + await waitFor(() => expect(screen.getByRole("alert").textContent).toBe(denialMessage)); expect(fetchMock).toHaveBeenCalledWith( expect.stringMatching(/\/api\/vendor-portal\/dispatches\/7\/uplift-request$/), expect.objectContaining({ method: "POST" }), ); }); + + it("keeps unexpected server messages behind the generic fallback", async () => { + const unexpectedMessage = "SqlException: work order row failed"; + vi.spyOn(globalThis, "fetch").mockResolvedValue( + new Response(JSON.stringify({ message: unexpectedMessage }), { + status: 500, + headers: { "Content-Type": "application/json" }, + }), + ); + + renderCreateForm(); + submitCreateForm(); + + await waitFor(() => + expect(screen.getByRole("alert").textContent).toBe( + "Unable to request the NTE uplift. Please try again.", + ), + ); + }); });