mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-10-04 11:22:05 +00:00
# Conflicts: # src/app/(protected)/workorders/_components/wizard/use-new-wo-wizard-controller.ts # src/app/(protected)/workorders/_components/wizard/wizard-step-location-service-select.tsx # src/app/(protected)/workorders/_components/wizard/wizard-step-vendor-time.tsx # src/test/app/(protected)/workorders/wizard-service-clears-vendor.test.tsx
260 lines
8.5 KiB
TypeScript
260 lines
8.5 KiB
TypeScript
import { act, fireEvent, render, renderHook, screen } from "@testing-library/react";
|
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
import { WizardStepVendorTime } from "@/app/(protected)/workorders/_components/wizard/wizard-step-vendor-time";
|
|
import { useWizardCreateWithTechnicians } from "@/app/(protected)/workorders/_components/wizard/use-wizard-create-with-technicians";
|
|
import type { VendorDropdownItem } from "@/domain/work-orders/types/work-order";
|
|
import {
|
|
EMPTY_WIZARD_DRAFT,
|
|
type WorkOrderWizardDraft,
|
|
} from "@/domain/work-orders/types/work-order-wizard";
|
|
|
|
const mutateAsync = vi.fn();
|
|
|
|
vi.mock("@/domain/vendors/use-cases/use-add-technicians-to-company", () => ({
|
|
useAddTechniciansToCompany: () => ({ mutateAsync, isPending: false }),
|
|
}));
|
|
|
|
vi.mock("react-toastify", () => ({ toast: { error: vi.fn(), success: vi.fn() } }));
|
|
|
|
const vendors: VendorDropdownItem[] = [
|
|
{
|
|
id: 41,
|
|
companyName: "Vinewood LLC",
|
|
contactName: "Adam Whyte",
|
|
tradeSpecialties: "HVAC",
|
|
address: "1 Market St",
|
|
distanceMiles: null,
|
|
},
|
|
{
|
|
id: 52,
|
|
companyName: "Gateway Plumbing",
|
|
contactName: "Casey Pipe",
|
|
tradeSpecialties: "Plumbing",
|
|
address: "",
|
|
distanceMiles: null,
|
|
},
|
|
];
|
|
|
|
function draftWith(patch: Partial<WorkOrderWizardDraft>): WorkOrderWizardDraft {
|
|
return { ...EMPTY_WIZARD_DRAFT, type: "PM", status: "Incomplete", ...patch };
|
|
}
|
|
|
|
describe("WizardStepVendorTime inline technician (SH-364)", () => {
|
|
it("hides the add action until a company is picked", () => {
|
|
render(
|
|
<WizardStepVendorTime
|
|
draft={draftWith({})}
|
|
vendors={vendors}
|
|
onUpdate={vi.fn()}
|
|
onPatch={vi.fn()}
|
|
/>,
|
|
);
|
|
|
|
expect(screen.queryByRole("button", { name: "+ Add technician" })).not.toBeInTheDocument();
|
|
expect(screen.getByRole("button", { name: /pick a company first/i })).toBeDisabled();
|
|
});
|
|
|
|
it("stages a new technician row for the picked company", () => {
|
|
const onPatch = vi.fn();
|
|
render(
|
|
<WizardStepVendorTime
|
|
draft={draftWith({ vendorCompany: "Vinewood LLC" })}
|
|
vendors={vendors}
|
|
onUpdate={vi.fn()}
|
|
onPatch={onPatch}
|
|
/>,
|
|
);
|
|
|
|
fireEvent.click(screen.getByRole("button", { name: "+ Add technician" }));
|
|
expect(onPatch).toHaveBeenCalledWith({ newTechnicians: [{ name: "", phone: "" }] });
|
|
});
|
|
|
|
it("scopes technicians to the picked company", () => {
|
|
render(
|
|
<WizardStepVendorTime
|
|
draft={draftWith({
|
|
vendorCompany: "Vinewood LLC",
|
|
newTechnicians: [{ name: "Jordan Lee", phone: "" }],
|
|
})}
|
|
vendors={vendors}
|
|
onUpdate={vi.fn()}
|
|
onPatch={vi.fn()}
|
|
/>,
|
|
);
|
|
|
|
expect(screen.getByRole("group", { name: "New Technician 1" })).toBeInTheDocument();
|
|
fireEvent.click(screen.getByRole("button", { name: /select technician/i }));
|
|
expect(screen.getByRole("button", { name: "Adam Whyte" })).toBeInTheDocument();
|
|
expect(screen.queryByRole("button", { name: "Casey Pipe" })).not.toBeInTheDocument();
|
|
});
|
|
|
|
it("clears the picked vendor and staged technicians when the company changes", () => {
|
|
const onPatch = vi.fn();
|
|
render(
|
|
<WizardStepVendorTime
|
|
draft={draftWith({
|
|
vendorCompany: "Vinewood LLC",
|
|
vendorId: "41",
|
|
vendorName: "Vinewood LLC",
|
|
techPhone: "(314) 555-0100",
|
|
newTechnicians: [{ name: "Jordan Lee", phone: "(314) 555-0199" }],
|
|
})}
|
|
vendors={vendors}
|
|
onUpdate={vi.fn()}
|
|
onPatch={onPatch}
|
|
/>,
|
|
);
|
|
|
|
fireEvent.click(screen.getByRole("button", { name: "Vinewood LLC" }));
|
|
fireEvent.click(screen.getByRole("button", { name: "Gateway Plumbing" }));
|
|
|
|
expect(onPatch).toHaveBeenCalledWith({
|
|
vendorCompany: "Gateway Plumbing",
|
|
vendorId: "",
|
|
vendorName: "",
|
|
techPhone: "",
|
|
vendorNotes: "",
|
|
newTechnicians: [],
|
|
});
|
|
});
|
|
|
|
it("preserves the picked vendor and staged technicians when the same company is re-picked", () => {
|
|
const onPatch = vi.fn();
|
|
render(
|
|
<WizardStepVendorTime
|
|
draft={draftWith({
|
|
vendorCompany: "Vinewood LLC",
|
|
vendorId: "41",
|
|
vendorName: "Vinewood LLC",
|
|
techPhone: "(314) 555-0100",
|
|
newTechnicians: [{ name: "Jordan Lee", phone: "(314) 555-0199" }],
|
|
})}
|
|
vendors={vendors}
|
|
onUpdate={vi.fn()}
|
|
onPatch={onPatch}
|
|
/>,
|
|
);
|
|
|
|
fireEvent.click(screen.getByRole("button", { name: "Vinewood LLC" }));
|
|
const companyOptions = screen.getAllByRole("button", { name: "Vinewood LLC" });
|
|
fireEvent.click(companyOptions[companyOptions.length - 1]);
|
|
|
|
expect(onPatch).toHaveBeenCalledWith({ vendorCompany: "Vinewood LLC" });
|
|
});
|
|
});
|
|
|
|
describe("useWizardCreateWithTechnicians (SH-364)", () => {
|
|
beforeEach(() => {
|
|
mutateAsync.mockReset();
|
|
});
|
|
|
|
it("creates the work order directly when no technician was staged", () => {
|
|
const createMutation = { mutate: vi.fn(), isPending: false };
|
|
const { result } = renderHook(() =>
|
|
useWizardCreateWithTechnicians({ vendors, patchDraft: vi.fn(), createMutation }),
|
|
);
|
|
const draft = draftWith({ vendorCompany: "Vinewood LLC", newTechnicians: [] });
|
|
|
|
result.current.mutate(draft);
|
|
|
|
expect(mutateAsync).not.toHaveBeenCalled();
|
|
expect(createMutation.mutate).toHaveBeenCalledWith(draft);
|
|
});
|
|
|
|
it("persists staged technicians first and assigns a single new technician", async () => {
|
|
mutateAsync.mockResolvedValue([{ ...vendors[0], id: 99, contactName: "Jordan Lee" }]);
|
|
const createMutation = { mutate: vi.fn(), isPending: false };
|
|
const patchDraft = vi.fn();
|
|
const { result } = renderHook(() =>
|
|
useWizardCreateWithTechnicians({ vendors, patchDraft, createMutation }),
|
|
);
|
|
|
|
await act(async () => {
|
|
result.current.mutate(
|
|
draftWith({
|
|
vendorCompany: "Vinewood LLC",
|
|
newTechnicians: [
|
|
{ name: "Jordan Lee", phone: "(314) 555-0199" },
|
|
{ name: " ", phone: "" },
|
|
],
|
|
}),
|
|
);
|
|
});
|
|
|
|
expect(mutateAsync).toHaveBeenCalledWith({
|
|
anchorVendorId: 41,
|
|
technicians: [
|
|
{ contactName: "Jordan Lee", phone: "(314) 555-0199", tradeSpecialties: "HVAC" },
|
|
],
|
|
});
|
|
const expectedPatch = {
|
|
newTechnicians: [],
|
|
vendorId: "99",
|
|
vendorName: "Vinewood LLC",
|
|
techPhone: "(314) 555-0199",
|
|
};
|
|
expect(patchDraft).toHaveBeenCalledWith(expectedPatch);
|
|
expect(createMutation.mutate).toHaveBeenCalledWith(expect.objectContaining(expectedPatch));
|
|
});
|
|
|
|
it("blocks Create and skips the roster save when a staged phone is not a valid number", async () => {
|
|
const createMutation = { mutate: vi.fn(), isPending: false };
|
|
const { result } = renderHook(() =>
|
|
useWizardCreateWithTechnicians({ vendors, patchDraft: vi.fn(), createMutation }),
|
|
);
|
|
|
|
await act(async () => {
|
|
result.current.mutate(
|
|
draftWith({
|
|
vendorCompany: "Vinewood LLC",
|
|
newTechnicians: [{ name: "Jordan Lee", phone: "(314) 555-01" }],
|
|
}),
|
|
);
|
|
});
|
|
|
|
expect(mutateAsync).not.toHaveBeenCalled();
|
|
expect(createMutation.mutate).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("allows a staged technician with a blank phone, matching the modal path", async () => {
|
|
mutateAsync.mockResolvedValue([{ ...vendors[0], id: 99, contactName: "Jordan Lee" }]);
|
|
const createMutation = { mutate: vi.fn(), isPending: false };
|
|
const { result } = renderHook(() =>
|
|
useWizardCreateWithTechnicians({ vendors, patchDraft: vi.fn(), createMutation }),
|
|
);
|
|
|
|
await act(async () => {
|
|
result.current.mutate(
|
|
draftWith({
|
|
vendorCompany: "Vinewood LLC",
|
|
newTechnicians: [{ name: "Jordan Lee", phone: "" }],
|
|
}),
|
|
);
|
|
});
|
|
|
|
expect(mutateAsync).toHaveBeenCalledWith({
|
|
anchorVendorId: 41,
|
|
technicians: [{ contactName: "Jordan Lee", phone: "", tradeSpecialties: "HVAC" }],
|
|
});
|
|
expect(createMutation.mutate).toHaveBeenCalled();
|
|
});
|
|
|
|
it("does not create the work order when the technician save fails", async () => {
|
|
mutateAsync.mockRejectedValue(new Error("boom"));
|
|
const createMutation = { mutate: vi.fn(), isPending: false };
|
|
const { result } = renderHook(() =>
|
|
useWizardCreateWithTechnicians({ vendors, patchDraft: vi.fn(), createMutation }),
|
|
);
|
|
|
|
await act(async () => {
|
|
result.current.mutate(
|
|
draftWith({
|
|
vendorCompany: "Vinewood LLC",
|
|
newTechnicians: [{ name: "Jordan Lee", phone: "" }],
|
|
}),
|
|
);
|
|
});
|
|
|
|
expect(createMutation.mutate).not.toHaveBeenCalled();
|
|
});
|
|
});
|