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(); 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; function renderSlideOver(onDraftChange = vi.fn()) { renderWithProviders( , ); 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( , ); 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(
, { 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: "" }), ); }); });