mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-10-05 17:42:06 +00:00
194 lines
7.5 KiB
TypeScript
194 lines
7.5 KiB
TypeScript
import { fireEvent, render, renderHook, screen, within } from "@testing-library/react";
|
|
import { describe, expect, it, vi } from "vitest";
|
|
import { SlideOverInfoTabEditView } from "@/app/(protected)/workorders/_components/detail/tabs/slide-over-info-tab-edit-view";
|
|
import { WoTableInlineRow } from "@/app/(protected)/workorders/_components/list/table/wo-table-inline-row";
|
|
import { useWoTableRowHandlers } from "@/app/(protected)/workorders/_components/list/table/use-wo-table-row-handlers";
|
|
import { MiniCalendar } from "@/app/(protected)/workorders/_components/wizard/mini-calendar";
|
|
import { WizardStepTypeSchedule } from "@/app/(protected)/workorders/_components/wizard/wizard-step-type-schedule";
|
|
import { EMPTY_TABLE_DRAFT } from "@/domain/work-orders/types/work-order-table-row";
|
|
import type { WorkOrderTableRow } from "@/domain/work-orders/types/work-order-table-row";
|
|
import { EMPTY_WIZARD_DRAFT } from "@/domain/work-orders/types/work-order-wizard";
|
|
import { pad2, todayIso } from "@/domain/work-orders/utils/wizard-date-utils";
|
|
import { renderWithProviders } from "@/test/test-utils";
|
|
|
|
vi.mock("@/app/(protected)/workorders/_components/list/table/cells/site-cell", () => ({
|
|
SiteCell: () => null,
|
|
}));
|
|
vi.mock(
|
|
"@/app/(protected)/workorders/_components/list/table/cells/editable-wo-number-cell",
|
|
() => ({ EditableWONumberCell: () => null }),
|
|
);
|
|
vi.mock("@/app/(protected)/workorders/_components/list/table/cells/type-cell", () => ({
|
|
TypeCell: () => null,
|
|
}));
|
|
vi.mock("@/app/(protected)/workorders/_components/list/table/cells/assigned-to-cell", () => ({
|
|
AssignedToCell: () => null,
|
|
}));
|
|
vi.mock("@/app/(protected)/workorders/_components/list/table/cells/appt-time-cell", () => ({
|
|
ApptTimeCell: () => null,
|
|
}));
|
|
vi.mock("@/app/(protected)/workorders/_components/list/table/cells/pm-type-cell", () => ({
|
|
PMTypeCell: () => null,
|
|
}));
|
|
vi.mock("@/app/(protected)/workorders/_components/list/table/cells/vendor-cell", () => ({
|
|
VendorCell: () => null,
|
|
}));
|
|
vi.mock("@/app/(protected)/workorders/_components/list/table/cells/status-cell", () => ({
|
|
StatusCell: () => null,
|
|
}));
|
|
vi.mock("@/app/(protected)/workorders/_components/list/table/reschedule-notify", () => ({
|
|
rescheduleNotify: vi.fn(),
|
|
weekMoveNotify: vi.fn(),
|
|
}));
|
|
|
|
function day(n: number): string {
|
|
const [y, m] = todayIso().split("-").map(Number);
|
|
return `${y}-${pad2(m)}-${pad2(n)}`;
|
|
}
|
|
|
|
function rowByLabel(label: string) {
|
|
return screen.getByText(label, { selector: "span,label,div" }).closest("div")!;
|
|
}
|
|
|
|
describe("Due Date cannot be before Schedule On (SH-356)", () => {
|
|
it("disables calendar days before the floor", () => {
|
|
render(<MiniCalendar value={day(12)} minDate={day(10)} onChange={vi.fn()} />);
|
|
|
|
expect(screen.getByRole("button", { name: "9" })).toBeDisabled();
|
|
expect(screen.getByRole("button", { name: "10" })).toBeEnabled();
|
|
expect(screen.getByRole("button", { name: "11" })).toBeEnabled();
|
|
});
|
|
|
|
describe("slide-over edit", () => {
|
|
const draft = { scheduledOn: day(10), dueDate: day(12) } as Partial<WorkOrderTableRow>;
|
|
|
|
function renderSlideOver(onDraftChange = vi.fn()) {
|
|
renderWithProviders(
|
|
<SlideOverInfoTabEditView
|
|
draft={{ ...EMPTY_TABLE_DRAFT, id: 42, targetWeek: "", ...draft } as WorkOrderTableRow}
|
|
users={[]}
|
|
sites={[]}
|
|
vendors={[]}
|
|
pmTypes={[]}
|
|
allOrders={[]}
|
|
onDraftChange={onDraftChange}
|
|
/>,
|
|
);
|
|
return onDraftChange;
|
|
}
|
|
|
|
it("disables Due Date days before Schedule On", async () => {
|
|
renderSlideOver();
|
|
fireEvent.click(within(rowByLabel("Due Date")).getByRole("button"));
|
|
|
|
expect(await screen.findByRole("button", { name: "9" })).toBeDisabled();
|
|
expect(screen.getByRole("button", { name: "10" })).toBeEnabled();
|
|
});
|
|
|
|
it("clears the Due Date when Schedule On moves past it", async () => {
|
|
const onDraftChange = renderSlideOver();
|
|
fireEvent.click(within(rowByLabel("Schedule On")).getAllByRole("button")[0]);
|
|
fireEvent.click(await screen.findByRole("button", { name: "15" }));
|
|
|
|
expect(onDraftChange).toHaveBeenCalledWith(
|
|
expect.objectContaining({ scheduledOn: day(15), dueDate: "" }),
|
|
);
|
|
});
|
|
});
|
|
|
|
it("wizard: clears Due Date on a later Schedule On and floors the Due Date picker", async () => {
|
|
const onPatch = vi.fn();
|
|
render(
|
|
<WizardStepTypeSchedule
|
|
draft={{ ...EMPTY_WIZARD_DRAFT, scheduledOn: day(10), schedMode: "date", dueDate: day(12) }}
|
|
users={[]}
|
|
onUpdate={vi.fn()}
|
|
onPatch={onPatch}
|
|
/>,
|
|
);
|
|
|
|
const dueDateButtons = within(rowByLabel("Due Date").parentElement!).getAllByRole("button");
|
|
fireEvent.click(dueDateButtons[dueDateButtons.length - 1]);
|
|
expect(await screen.findByRole("button", { name: "9" })).toBeDisabled();
|
|
fireEvent.keyDown(document.activeElement ?? document.body, { key: "Escape" });
|
|
|
|
const scheduleBlock = rowByLabel("Schedule On").parentElement!;
|
|
const scheduleButtons = within(scheduleBlock).getAllByRole("button");
|
|
fireEvent.click(scheduleButtons[scheduleButtons.length - 1]);
|
|
const calendars = await screen.findAllByRole("button", { name: "15" });
|
|
fireEvent.click(calendars[calendars.length - 1]);
|
|
|
|
expect(onPatch).toHaveBeenCalledWith(
|
|
expect.objectContaining({ scheduledOn: day(15), dueDate: "" }),
|
|
);
|
|
});
|
|
|
|
it("inline creation row: clears Due Date on a later Schedule On", async () => {
|
|
const onChange = vi.fn();
|
|
renderWithProviders(
|
|
<table>
|
|
<tbody>
|
|
<WoTableInlineRow
|
|
draft={{ ...EMPTY_TABLE_DRAFT, scheduledOn: day(10), dueDate: day(12) }}
|
|
draftDayKey={day(10)}
|
|
users={[]}
|
|
sites={[]}
|
|
vendors={[]}
|
|
pmTypes={[]}
|
|
allRows={[]}
|
|
onChange={onChange}
|
|
onSave={vi.fn()}
|
|
onCancel={vi.fn()}
|
|
/>
|
|
</tbody>
|
|
</table>,
|
|
{ withAuth: false },
|
|
);
|
|
|
|
const cells = screen.getAllByRole("cell");
|
|
const dateButtons = cells.flatMap((cell) => within(cell).queryAllByRole("button"));
|
|
const scheduleButton = dateButtons.find(
|
|
(b) => b.textContent?.includes(day(10).slice(5).replace("-", "/")) ?? false,
|
|
);
|
|
fireEvent.click(scheduleButton ?? dateButtons[0]);
|
|
fireEvent.click(await screen.findByRole("button", { name: "15" }));
|
|
|
|
expect(onChange).toHaveBeenCalledWith(
|
|
expect.objectContaining({ scheduledOn: day(15), dueDate: "" }),
|
|
);
|
|
});
|
|
|
|
it("board row: sends the Due Date clear in the same schedule patch", () => {
|
|
const onPatch = vi.fn();
|
|
const { result } = renderHook(() =>
|
|
useWoTableRowHandlers({
|
|
tableData: {
|
|
dragState: { current: { dragId: null, overId: null } },
|
|
setDragViz: vi.fn(),
|
|
reorderRows: vi.fn(),
|
|
endDrag: vi.fn(),
|
|
revealScheduledRow: vi.fn(),
|
|
setSelectedId: vi.fn(),
|
|
} as never,
|
|
weekMonday: "2026-05-18",
|
|
onNavigateToDay: vi.fn(),
|
|
onOpenSlideOver: vi.fn(),
|
|
onOpenMedia: vi.fn(),
|
|
onPatch,
|
|
onScheduleHighlight: vi.fn(),
|
|
}),
|
|
);
|
|
|
|
result.current.onScheduleDate("wo-1", "2026-05-21", "2026-05-20");
|
|
expect(onPatch).toHaveBeenLastCalledWith("wo-1", expect.objectContaining({ dueDate: "" }));
|
|
|
|
result.current.onScheduleRange("wo-1", "2026-05-19", "2026-05-22", "2026-05-20");
|
|
expect(onPatch.mock.lastCall?.[1]).not.toHaveProperty("dueDate");
|
|
|
|
result.current.onScheduleWeek("wo-1", "2026-05-25", "2026-05-20");
|
|
expect(onPatch).toHaveBeenLastCalledWith(
|
|
"wo-1",
|
|
expect.objectContaining({ targetWeek: "2026-05-25", dueDate: "" }),
|
|
);
|
|
});
|
|
});
|