shoc-frontend-new/src/test/app/v/signoff-section.test.tsx

103 lines
3.7 KiB
TypeScript
Raw Normal View History

refactor: move vendor-portal dispatch actions and 2 pages onto proper data hooks Two groups of components were calling the API directly instead of using this codebase's standard React Query hooks, with no shared caching and duplicated submit/error-handling boilerplate. - checklist-section.tsx, comments-section.tsx, signoff-section.tsx, uplift-requests-section.tsx: replace direct vendorPortalApi calls and hand-rolled submitting/error state with mutation hooks in the new use-vendor-portal-dispatch-actions.ts. uplift-requests-section keeps its existing multi-step evidence-upload orchestration intact and only swaps the 3 raw API calls for mutateAsync, since restructuring that flow around declarative mutation callbacks carried more risk than value. Caught and fixed a real bug along the way: two components were passing a prop callback directly as a mutation's onSuccess, which would have forwarded React Query's extra callback arguments into it. - followups/index.tsx, vendors/preferences.tsx: split from a single file doing state, API calls, and rendering into a hook (owns data and handlers) plus small presentational components, matching the task-templates refactor pattern. Added a missing centralized query key (vendorOperations.sitePreferences) that preferences.tsx was building as a raw inline array. - use-list-page-filters.ts: small shared hook for search/page/pageSize state, used by the new followups hook. 29 new/updated tests across all touched files; none of these had coverage for their mutation/error paths before.
2026-09-25 10:53:23 -05:00
import { fireEvent, screen, waitFor } from "@testing-library/react";
import { afterEach, describe, expect, it, vi } from "vitest";
import { SignoffSection } from "@/app/v/[token]/dispatch/_components/signoff-section";
refactor: move vendor-portal dispatch actions and 2 pages onto proper data hooks Two groups of components were calling the API directly instead of using this codebase's standard React Query hooks, with no shared caching and duplicated submit/error-handling boilerplate. - checklist-section.tsx, comments-section.tsx, signoff-section.tsx, uplift-requests-section.tsx: replace direct vendorPortalApi calls and hand-rolled submitting/error state with mutation hooks in the new use-vendor-portal-dispatch-actions.ts. uplift-requests-section keeps its existing multi-step evidence-upload orchestration intact and only swaps the 3 raw API calls for mutateAsync, since restructuring that flow around declarative mutation callbacks carried more risk than value. Caught and fixed a real bug along the way: two components were passing a prop callback directly as a mutation's onSuccess, which would have forwarded React Query's extra callback arguments into it. - followups/index.tsx, vendors/preferences.tsx: split from a single file doing state, API calls, and rendering into a hook (owns data and handlers) plus small presentational components, matching the task-templates refactor pattern. Added a missing centralized query key (vendorOperations.sitePreferences) that preferences.tsx was building as a raw inline array. - use-list-page-filters.ts: small shared hook for search/page/pageSize state, used by the new followups hook. 29 new/updated tests across all touched files; none of these had coverage for their mutation/error paths before.
2026-09-25 10:53:23 -05:00
import { vendorPortalApi } from "@/domain/vendor-portal/api/vendor-portal-api";
import { renderWithProviders } from "@/test/test-utils";
afterEach(() => {
vi.restoreAllMocks();
});
function renderSection(overrides: Partial<React.ComponentProps<typeof SignoffSection>> = {}) {
refactor: move vendor-portal dispatch actions and 2 pages onto proper data hooks Two groups of components were calling the API directly instead of using this codebase's standard React Query hooks, with no shared caching and duplicated submit/error-handling boilerplate. - checklist-section.tsx, comments-section.tsx, signoff-section.tsx, uplift-requests-section.tsx: replace direct vendorPortalApi calls and hand-rolled submitting/error state with mutation hooks in the new use-vendor-portal-dispatch-actions.ts. uplift-requests-section keeps its existing multi-step evidence-upload orchestration intact and only swaps the 3 raw API calls for mutateAsync, since restructuring that flow around declarative mutation callbacks carried more risk than value. Caught and fixed a real bug along the way: two components were passing a prop callback directly as a mutation's onSuccess, which would have forwarded React Query's extra callback arguments into it. - followups/index.tsx, vendors/preferences.tsx: split from a single file doing state, API calls, and rendering into a hook (owns data and handlers) plus small presentational components, matching the task-templates refactor pattern. Added a missing centralized query key (vendorOperations.sitePreferences) that preferences.tsx was building as a raw inline array. - use-list-page-filters.ts: small shared hook for search/page/pageSize state, used by the new followups hook. 29 new/updated tests across all touched files; none of these had coverage for their mutation/error paths before.
2026-09-25 10:53:23 -05:00
return renderWithProviders(
<SignoffSection
token="portal-token"
dispatchId={9}
existing={[]}
signoffType="customer"
nameLabel="Customer name"
onAdded={vi.fn()}
{...overrides}
/>,
refactor: move vendor-portal dispatch actions and 2 pages onto proper data hooks Two groups of components were calling the API directly instead of using this codebase's standard React Query hooks, with no shared caching and duplicated submit/error-handling boilerplate. - checklist-section.tsx, comments-section.tsx, signoff-section.tsx, uplift-requests-section.tsx: replace direct vendorPortalApi calls and hand-rolled submitting/error state with mutation hooks in the new use-vendor-portal-dispatch-actions.ts. uplift-requests-section keeps its existing multi-step evidence-upload orchestration intact and only swaps the 3 raw API calls for mutateAsync, since restructuring that flow around declarative mutation callbacks carried more risk than value. Caught and fixed a real bug along the way: two components were passing a prop callback directly as a mutation's onSuccess, which would have forwarded React Query's extra callback arguments into it. - followups/index.tsx, vendors/preferences.tsx: split from a single file doing state, API calls, and rendering into a hook (owns data and handlers) plus small presentational components, matching the task-templates refactor pattern. Added a missing centralized query key (vendorOperations.sitePreferences) that preferences.tsx was building as a raw inline array. - use-list-page-filters.ts: small shared hook for search/page/pageSize state, used by the new followups hook. 29 new/updated tests across all touched files; none of these had coverage for their mutation/error paths before.
2026-09-25 10:53:23 -05:00
{ withAuth: false },
);
}
describe("SignoffSection", () => {
it("shows status-neutral locked copy when submission is locked", () => {
renderSection({ submissionLocked: true });
expect(screen.getByText(/Signoff is unavailable for this dispatch/i)).toBeInTheDocument();
expect(screen.queryByText(/refused/i)).not.toBeInTheDocument();
expect(screen.queryByRole("button", { name: "Submit Signoff" })).not.toBeInTheDocument();
});
it("shows the submission form when unlocked", () => {
renderSection();
expect(screen.getByRole("button", { name: "Submit Signoff" })).toBeInTheDocument();
});
it("shows an existing signoff even while locked", () => {
renderSection({
signoffType: "vendor",
nameLabel: "Your name",
submissionLocked: true,
existing: [{ signoffType: "vendor", name: "Grace Hopper", signatureMethod: "typed" }],
});
expect(screen.getByText("Grace Hopper")).toBeInTheDocument();
expect(screen.queryByText(/Signoff is unavailable/i)).not.toBeInTheDocument();
expect(screen.queryByRole("button", { name: "Submit Signoff" })).not.toBeInTheDocument();
});
refactor: move vendor-portal dispatch actions and 2 pages onto proper data hooks Two groups of components were calling the API directly instead of using this codebase's standard React Query hooks, with no shared caching and duplicated submit/error-handling boilerplate. - checklist-section.tsx, comments-section.tsx, signoff-section.tsx, uplift-requests-section.tsx: replace direct vendorPortalApi calls and hand-rolled submitting/error state with mutation hooks in the new use-vendor-portal-dispatch-actions.ts. uplift-requests-section keeps its existing multi-step evidence-upload orchestration intact and only swaps the 3 raw API calls for mutateAsync, since restructuring that flow around declarative mutation callbacks carried more risk than value. Caught and fixed a real bug along the way: two components were passing a prop callback directly as a mutation's onSuccess, which would have forwarded React Query's extra callback arguments into it. - followups/index.tsx, vendors/preferences.tsx: split from a single file doing state, API calls, and rendering into a hook (owns data and handlers) plus small presentational components, matching the task-templates refactor pattern. Added a missing centralized query key (vendorOperations.sitePreferences) that preferences.tsx was building as a raw inline array. - use-list-page-filters.ts: small shared hook for search/page/pageSize state, used by the new followups hook. 29 new/updated tests across all touched files; none of these had coverage for their mutation/error paths before.
2026-09-25 10:53:23 -05:00
it("requires a name and signature before submitting", () => {
const addSignoff = vi.spyOn(vendorPortalApi, "addSignoff");
renderSection();
fireEvent.click(screen.getByRole("button", { name: "Submit Signoff" }));
expect(screen.getByText("Name and signature are required.")).toBeInTheDocument();
expect(addSignoff).not.toHaveBeenCalled();
});
it("submits a typed signoff and calls onAdded", async () => {
vi.spyOn(vendorPortalApi, "addSignoff").mockResolvedValue({
signoffType: "customer",
name: "Jane Doe",
signatureMethod: "typed",
});
const onAdded = vi.fn();
renderSection({ onAdded });
fireEvent.click(screen.getByRole("radio", { name: "Typed" }));
const [nameInput, signatureInput] = screen.getAllByRole("textbox");
fireEvent.change(nameInput, { target: { value: "Jane Doe" } });
fireEvent.change(signatureInput, { target: { value: "Jane Doe" } });
fireEvent.click(screen.getByRole("button", { name: "Submit Signoff" }));
await waitFor(() => expect(onAdded).toHaveBeenCalled());
expect(vendorPortalApi.addSignoff).toHaveBeenCalledWith("portal-token", 9, {
name: "Jane Doe",
signature: "Jane Doe",
signatureMethod: "typed",
signoffType: "customer",
});
});
it("shows an error message when the submission fails", async () => {
vi.spyOn(vendorPortalApi, "addSignoff").mockRejectedValue(new Error("boom"));
renderSection();
fireEvent.click(screen.getByRole("radio", { name: "Typed" }));
const [nameInput, signatureInput] = screen.getAllByRole("textbox");
fireEvent.change(nameInput, { target: { value: "Jane Doe" } });
fireEvent.change(signatureInput, { target: { value: "Jane Doe" } });
fireEvent.click(screen.getByRole("button", { name: "Submit Signoff" }));
expect(
await screen.findByText("Unable to submit the signoff. Please try again."),
).toBeInTheDocument();
});
});