shoc-frontend-new/src/test/app/(protected)/workorders/comp-doc-signed-pdf-upload.test.tsx

154 lines
4.8 KiB
TypeScript
Raw Normal View History

import { fireEvent, render, waitFor } from "@testing-library/react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { toast } from "react-toastify";
import { CompDocDialog } from "@/app/(protected)/workorders/_components/comp-doc/comp-doc-dialog";
import type { CompDocData, CompDocSaveStatus } from "@/domain/work-orders/types/completion-doc";
import type { WorkOrderTableRow } from "@/domain/work-orders/types/work-order-table-row";
type SaveHandler = (data: CompDocData, status: CompDocSaveStatus) => void | Promise<void>;
vi.mock("@/domain/locations/use-cases/use-location-detail", () => ({
useLocationDetail: () => ({ data: undefined }),
}));
vi.mock("@/domain/work-orders/use-cases/use-work-order-detail", () => ({
useWorkOrderBoardDetail: () => ({ data: undefined, isLoading: false }),
}));
// A completion-doc upload that settles successfully. The backend has already set
// DocStatus=Yes and bumped the work-order row version inside the same transaction,
// so any client follow-up patch would carry a stale version.
vi.mock("@/domain/work-orders/use-cases/use-work-order-mutations", () => ({
useUploadCompletionDoc: () => ({
mutate: (_vars: unknown, options?: { onSuccess?: () => void | Promise<void> }) => {
void options?.onSuccess?.();
},
isPending: false,
}),
}));
vi.mock("react-toastify", () => ({
toast: Object.assign(vi.fn(), { error: vi.fn(), success: vi.fn() }),
}));
vi.mock("@/app/(protected)/workorders/_components/comp-doc/comp-doc-draft-toast", () => ({
showCompDocDraftSavedToast: vi.fn(),
}));
function baseRow(overrides: Partial<WorkOrderTableRow> = {}): WorkOrderTableRow {
const defaults: WorkOrderTableRow = {
id: 42,
woNumber: "WO-42",
site: "SEA1",
type: "PM",
severity: null,
dispatcherId: "u1",
dispatcherName: "Dispatcher",
scheduledOn: "2026-06-01",
completedDate: "2026-07-01",
targetWeek: "",
dayGroup: "",
dayLabel: "",
location: "Seattle",
locationId: "mock-loc-1",
pm: "HVAC",
serviceNotes: "",
extraServices: [],
vendorId: "",
company: "Vendor Co",
tech: "Tech One",
techPhone: "",
apptStart: "",
apptEnd: "",
apptTime: "",
status: "Scheduled",
docStatus: "No",
pocName: "Amazon Rep",
pocPhone: "",
pocNotes: "",
additionalContacts: [],
dueDate: "",
rescheduleCount: 0,
carriedOver: 0,
originalWeek: "",
originalDate: "",
isPastDue: false,
scheduleWeekOnly: false,
mediaCount: 0,
title: "PM — SEA1",
priority: "Sev 3",
primaryDispatchId: null,
rowVersion: "v1",
dispatchRowVersion: "",
flagColor: null,
isAddOn: false,
upliftSummary: { hasUplift: false, pendingCount: 0 },
};
return { ...defaults, ...overrides };
}
describe("CompDocDialog signed PDF upload (SH-380)", () => {
beforeEach(() => {
vi.mocked(toast.error).mockReset();
});
function uploadSignedPdf(onSave: SaveHandler) {
const onDateCompletedChange = vi.fn().mockResolvedValue(undefined);
const client = new QueryClient({
defaultOptions: { queries: { retry: false }, mutations: { retry: false } },
});
render(
<QueryClientProvider client={client}>
<CompDocDialog
row={baseRow()}
open
onOpenChange={vi.fn()}
onSave={onSave}
onDateCompletedChange={onDateCompletedChange}
/>
</QueryClientProvider>,
);
const fileInput = document.querySelector('input[type="file"]');
expect(fileInput).not.toBeNull();
fireEvent.change(fileInput!, {
target: {
files: [
new File([new Uint8Array([0x25, 0x50, 0x44, 0x46, 0x2d])], "signed.pdf", {
type: "application/pdf",
}),
],
},
});
return { onSave, onDateCompletedChange };
}
it("does not re-run the docStatus persist after a successful upload", async () => {
// The server already marked the document generated; a client docStatus patch here would
// carry the pre-upload row version and 409. It must not run.
const onSave: SaveHandler = vi.fn(() =>
Promise.reject(new Error("The record was modified by another user. Refresh and retry.")),
);
uploadSignedPdf(onSave);
await waitFor(() => {
expect(vi.mocked(toast.error)).not.toHaveBeenCalled();
});
expect(onSave).not.toHaveBeenCalled();
});
it("surfaces no error toast to the user when the signed PDF upload settles", async () => {
const onSave: SaveHandler = vi.fn(() => Promise.reject(new Error("conflict")));
const { onDateCompletedChange } = uploadSignedPdf(onSave);
await waitFor(() => {
expect(vi.mocked(toast.error)).not.toHaveBeenCalled();
});
expect(onDateCompletedChange).not.toHaveBeenCalled();
});
});