import { describe, expect, it } from "vitest"; import { PENDING_UPLIFT_GAP } from "@/domain/work-orders/utils/wo-closability"; import { AUTOMATIC_LIFECYCLE_STATUSES, BOARD_INLINE_STATUSES, COMPLETED_DOC_BLOCK_MSG, COMPLETED_MEDIA_BLOCK_MSG, CREATION_INLINE_STATUSES, SCHEDULED_NEEDS_DATE_MSG, formatAapCompletedBlockTitle, getInlineStatusTransitions, getStatusOptionState, hasConcreteScheduleDate, maybeAutoSchedulePatch, mergeAutoSchedulePatch, resolveCreationStatus, withAutoScheduleStatus, } from "@/domain/work-orders/utils/wo-lifecycle-transitions"; import type { WorkOrderTableRow } from "@/domain/work-orders/types/work-order-table-row"; describe("wo-lifecycle-transitions", () => { it("lists only manual board statuses: no automatic states and no Canceled (SH-169)", () => { expect(BOARD_INLINE_STATUSES).toEqual([ "En Route", "On Site", "In Progress", "Completed", "Rescheduled", "Pending Quote", ]); expect(BOARD_INLINE_STATUSES).not.toContain("Canceled"); for (const automatic of AUTOMATIC_LIFECYCLE_STATUSES) { expect(BOARD_INLINE_STATUSES).not.toContain(automatic); } }); it("never offers the current status as a transition (SH-358)", () => { expect(getInlineStatusTransitions("On Site")).toEqual([ "En Route", "In Progress", "Completed", "Rescheduled", "Pending Quote", ]); expect(getInlineStatusTransitions("Incomplete")).toEqual(BOARD_INLINE_STATUSES); }); it("lists AAP creation statuses only", () => { expect(CREATION_INLINE_STATUSES).toEqual(["Incomplete", "Pending", "Scheduled"]); }); it("treats week-only as no concrete schedule date", () => { expect(hasConcreteScheduleDate({ scheduledOn: "2026-06-01", scheduleWeekOnly: true })).toBe( false, ); expect(hasConcreteScheduleDate({ scheduledOn: "2026-06-01", schedMode: "week" })).toBe(false); expect(hasConcreteScheduleDate({ scheduledOn: "—" })).toBe(false); expect(hasConcreteScheduleDate({ scheduledOn: "2026-06-01" })).toBe(true); }); it("disables Scheduled without a concrete date", () => { expect(getStatusOptionState({ scheduledOn: "" }, "Scheduled", [])).toEqual({ disabled: true, title: SCHEDULED_NEEDS_DATE_MSG, }); }); it("promotes Incomplete/Pending when a concrete date exists", () => { expect( maybeAutoSchedulePatch({ status: "Incomplete", scheduledOn: "2026-06-01", scheduleWeekOnly: false, }), ).toEqual({ status: "Scheduled" }); expect( maybeAutoSchedulePatch({ status: "Pending", scheduledOn: "2026-06-01", scheduleWeekOnly: false, }), ).toEqual({ status: "Scheduled" }); }); it("reverts Scheduled without a concrete date to Pending", () => { expect( maybeAutoSchedulePatch({ status: "Scheduled", scheduledOn: "", scheduleWeekOnly: false, }), ).toEqual({ status: "Pending" }); }); it("does not auto-change In Progress when a date exists", () => { expect( maybeAutoSchedulePatch({ status: "In Progress", scheduledOn: "2026-06-01", scheduleWeekOnly: false, }), ).toEqual({}); }); it("merges auto-schedule onto an existing-row patch", () => { const row = { status: "Incomplete", scheduledOn: "", scheduleWeekOnly: false, } as WorkOrderTableRow; expect( mergeAutoSchedulePatch(row, { scheduledOn: "2026-06-02", scheduleWeekOnly: false }), ).toEqual({ scheduledOn: "2026-06-02", scheduleWeekOnly: false, status: "Scheduled", }); expect(mergeAutoSchedulePatch(row, { vendorId: "9" })).toEqual({ vendorId: "9" }); expect( mergeAutoSchedulePatch({ ...row, scheduledOn: "2026-06-01" }, { vendorId: "9" }), ).toEqual({ vendorId: "9" }); }); it("promotes a pre-schedule wizard pick to Scheduled on a dated draft", () => { const draft = { status: "Scheduled", scheduledOn: "2026-06-01", scheduleWeekOnly: false, }; expect(withAutoScheduleStatus(draft, { status: "Pending" })).toEqual({ status: "Scheduled" }); expect(withAutoScheduleStatus(draft, { status: "Incomplete" })).toEqual({ status: "Scheduled", }); }); it("still derives Scheduled when a concrete date is set on a draft", () => { const draft = { status: "Pending", scheduledOn: "", scheduleWeekOnly: false, }; expect( withAutoScheduleStatus(draft, { scheduledOn: "2026-06-02", scheduleWeekOnly: false }), ).toEqual({ scheduledOn: "2026-06-02", scheduleWeekOnly: false, status: "Scheduled", }); }); it("promotes pre-schedule picks on dated rows", () => { const row = { status: "Scheduled", scheduledOn: "2026-06-01", scheduleWeekOnly: false, } as WorkOrderTableRow; expect(mergeAutoSchedulePatch(row, { status: "Pending" })).toEqual({ status: "Scheduled" }); expect(mergeAutoSchedulePatch(row, { status: "Incomplete" })).toEqual({ status: "Scheduled", }); }); it("still demotes Scheduled when the schedule is cleared", () => { const row = { status: "Scheduled", scheduledOn: "2026-06-01", scheduleWeekOnly: false, } as WorkOrderTableRow; expect(mergeAutoSchedulePatch(row, { scheduledOn: "", scheduleWeekOnly: false })).toEqual({ scheduledOn: "", scheduleWeekOnly: false, status: "Pending", }); }); it("promotes a Pending create pick to Scheduled when the draft is dated", () => { expect( resolveCreationStatus({ status: "Pending", scheduledOn: "2026-06-01", scheduleWeekOnly: false, }), ).toBe("Scheduled"); }); it("demotes Scheduled without a concrete date at create", () => { expect( resolveCreationStatus({ status: "Scheduled", scheduledOn: "", scheduleWeekOnly: false, }), ).toBe("Pending"); }); it("uses AAP completed tooltip hierarchy", () => { expect(formatAapCompletedBlockTitle(["Dispatcher", "Company"])).toBe( "Fill in Assigned To, Vendor before completing this work order.", ); expect(formatAapCompletedBlockTitle(["Service"])).toBe( "Select a service before completing this work order.", ); expect(formatAapCompletedBlockTitle(["WO number"])).toBe( "Assign a real APM number (not SH) before completing this work order.", ); expect(formatAapCompletedBlockTitle([PENDING_UPLIFT_GAP])).toBe( "This work order has an uplift pending approval and cannot be completed.", ); expect(formatAapCompletedBlockTitle(["Completion document"])).toBe(COMPLETED_DOC_BLOCK_MSG); expect(formatAapCompletedBlockTitle(["At least 1 photo"])).toBe(COMPLETED_MEDIA_BLOCK_MSG); expect(formatAapCompletedBlockTitle(["Company", PENDING_UPLIFT_GAP])).toBe( "Fill in Vendor before completing this work order.", ); }); });