mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-09-30 06:53:12 +00:00
142 lines
4.2 KiB
TypeScript
142 lines
4.2 KiB
TypeScript
import { createElement, type ReactNode } from "react";
|
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
import { renderHook } from "@testing-library/react";
|
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
import {
|
|
getSingleStatusOnlyChange,
|
|
useSaveVendorCompanyRoster,
|
|
} from "@/domain/vendors/use-cases/use-save-vendor-company-roster";
|
|
import type { VendorCompanyRoster } from "@/domain/vendors/types/vendor";
|
|
|
|
const updateVendor = vi.fn();
|
|
|
|
vi.mock("@/domain/vendors/api/vendors-api", () => ({
|
|
vendorsApi: {
|
|
update: (...args: unknown[]) => updateVendor(...args),
|
|
},
|
|
}));
|
|
|
|
vi.mock("@/domain/vendors/api/vendor-company-roster-api", () => ({
|
|
vendorCompanyRosterApi: {
|
|
update: vi.fn(),
|
|
create: vi.fn(),
|
|
},
|
|
}));
|
|
|
|
vi.mock("react-toastify", () => ({
|
|
toast: { success: vi.fn(), error: vi.fn() },
|
|
}));
|
|
|
|
const roster: VendorCompanyRoster = {
|
|
companyId: 10,
|
|
rowVersion: "rv-1",
|
|
name: "Legacy Plumbing",
|
|
companyPhone: "555-1234 ext 9",
|
|
email: "dispatch@example.test",
|
|
address: "1 Main St",
|
|
city: "St. Louis",
|
|
state: "MO",
|
|
zip: "63101",
|
|
googleMapsUrl: "http://maps.example.test/legacy",
|
|
notes: "Legacy values remain untouched",
|
|
technicians: [
|
|
{
|
|
id: 7,
|
|
contactName: "Taylor",
|
|
phone: "555-0199 ext 2",
|
|
email: "taylor@example.test",
|
|
preferredContact: "Phone",
|
|
tradeSpecialties: "Plumbing",
|
|
isActive: false,
|
|
totalJobs: 12,
|
|
},
|
|
],
|
|
};
|
|
|
|
const values = {
|
|
name: roster.name,
|
|
companyPhone: roster.companyPhone,
|
|
email: roster.email,
|
|
address: roster.address,
|
|
city: roster.city,
|
|
state: roster.state,
|
|
zip: roster.zip,
|
|
googleMapsUrl: roster.googleMapsUrl,
|
|
notes: roster.notes,
|
|
technicians: roster.technicians.map((technician) => ({
|
|
id: technician.id,
|
|
contactName: technician.contactName,
|
|
phone: technician.phone,
|
|
email: technician.email,
|
|
preferredContact: technician.preferredContact,
|
|
tradeSpecialties: technician.tradeSpecialties,
|
|
isActive: technician.isActive,
|
|
})),
|
|
};
|
|
|
|
describe("getSingleStatusOnlyChange", () => {
|
|
it("preserves the sparse legacy status path when exactly one technician status changes", () => {
|
|
expect(
|
|
getSingleStatusOnlyChange(roster, {
|
|
...values,
|
|
technicians: [{ ...values.technicians[0], isActive: true }],
|
|
}),
|
|
).toEqual({ id: 7, isActive: true });
|
|
});
|
|
|
|
it("requires aggregate reconciliation when company or technician data changes", () => {
|
|
expect(getSingleStatusOnlyChange(roster, { ...values, notes: "changed" })).toBeNull();
|
|
expect(
|
|
getSingleStatusOnlyChange(roster, {
|
|
...values,
|
|
technicians: [{ ...values.technicians[0], contactName: "Renamed", isActive: true }],
|
|
}),
|
|
).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe("useSaveVendorCompanyRoster status-only deactivation gate", () => {
|
|
beforeEach(() => {
|
|
updateVendor.mockReset();
|
|
updateVendor.mockResolvedValue(undefined);
|
|
});
|
|
|
|
it("rejects sparse Active→Inactive updates and still allows reactivation", async () => {
|
|
const client = new QueryClient({ defaultOptions: { queries: { retry: false } } });
|
|
const wrapper = ({ children }: { children: ReactNode }) =>
|
|
createElement(QueryClientProvider, { client }, children);
|
|
|
|
const { result } = renderHook(() => useSaveVendorCompanyRoster(), { wrapper });
|
|
|
|
const activeRoster: VendorCompanyRoster = {
|
|
...roster,
|
|
technicians: [{ ...roster.technicians[0], isActive: true }],
|
|
};
|
|
|
|
await expect(
|
|
result.current.mutateAsync({
|
|
mode: "update",
|
|
values: {
|
|
...values,
|
|
technicians: [{ ...values.technicians[0], isActive: false }],
|
|
},
|
|
companyId: 10,
|
|
rowVersion: "rv-1",
|
|
originalRoster: activeRoster,
|
|
}),
|
|
).rejects.toThrow("Use Deactivate to check open work orders before inactivating a technician.");
|
|
expect(updateVendor).not.toHaveBeenCalled();
|
|
|
|
await result.current.mutateAsync({
|
|
mode: "update",
|
|
values: {
|
|
...values,
|
|
technicians: [{ ...values.technicians[0], isActive: true }],
|
|
},
|
|
companyId: 10,
|
|
rowVersion: "rv-1",
|
|
originalRoster: roster,
|
|
});
|
|
expect(updateVendor).toHaveBeenCalledWith(7, { isActive: true });
|
|
});
|
|
});
|