import { fireEvent, screen } from "@testing-library/react"; import { beforeEach, describe, expect, it, vi } from "vitest"; import type { UpliftQueueItem, UpliftsQueueResult } from "@/domain/uplifts/types/uplift"; import { renderWithProviders } from "@/test/test-utils"; const rejectMutate = vi.fn(); const approveMutate = vi.fn(); const revokeMutate = vi.fn(); const refetch = vi.fn(); const queueState = vi.hoisted(() => ({ data: null as UpliftsQueueResult | null, })); const canApproveState = vi.hoisted(() => ({ data: true as boolean | undefined })); const pendingItem: UpliftQueueItem = { id: 41, status: "Pending", currentNTE: 100, requestedNTE: 250, delta: 150, vendorReason: "Extra parts", requestedAt: "2026-01-15T10:00:00Z", requestedByVendorName: "Gateway", decidedAt: "", decidedByName: "", decisionNote: "", requiredTier: 1, canDecide: true, expiresAt: "", notificationStatus: "", notificationError: "", evidenceDocumentId: null, evidenceFileName: "", evidenceContentType: "", evidenceSizeBytes: null, dispatchNumber: "DSP-41", poNumber: "PO-41", vendorCompanyName: "Gateway Plumbing", workOrderId: 99, dispatchId: 7, woNumber: "WO-99", site: "Site A", serviceName: "Plumbing repair", attachmentCount: null, approvedOnWoAuto: null, approvedOnWoAdmin: null, approvedOnWoTotal: 100, workOrderClosed: false, }; vi.mock("@/domain/uplifts/use-cases/use-uplifts-queue", () => ({ useUpliftsQueue: () => ({ data: queueState.data, isLoading: false, isFetching: false, error: null, refetch, }), })); vi.mock("@/domain/uplifts/use-cases/use-uplift-actions", () => ({ useApproveUplift: () => ({ isPending: false, mutate: approveMutate }), useRejectUplift: () => ({ isPending: false, mutate: rejectMutate }), useRevokeUplift: () => ({ isPending: false, mutate: revokeMutate }), })); vi.mock("@/providers/auth-context", async (importOriginal) => ({ ...(await importOriginal()), useAuthContext: () => ({ user: { userRoles: "Admin" } }), })); vi.mock("@/domain/uplifts/use-cases/use-uplifts-can-approve", () => ({ useUpliftsCanApprove: () => ({ data: canApproveState.data }), })); describe("Uplift Approvals decision flow", () => { beforeEach(() => { rejectMutate.mockReset(); approveMutate.mockReset(); revokeMutate.mockReset(); refetch.mockReset(); canApproveState.data = true; queueState.data = { items: [pendingItem], totalCount: 1, page: 1, pageSize: 25, pendingExposureTotal: 250, }; }); it("shows the Uplift Approvals heading, count pill and exposure total", async () => { const { default: UpliftQueuePage } = await import("@/app/(protected)/uplifts/index"); renderWithProviders(); expect(screen.getByRole("heading", { name: "Uplift Approvals" })).toBeInTheDocument(); expect(screen.getByLabelText("1 pending uplifts")).toBeInTheDocument(); expect(screen.getByText("Pending exposure: $250.00")).toBeInTheDocument(); }); it("approves in one click without a confirmation dialog", async () => { const { default: UpliftQueuePage } = await import("@/app/(protected)/uplifts/index"); renderWithProviders(); fireEvent.click(screen.getByRole("button", { name: "Approve" })); expect(approveMutate).toHaveBeenCalledWith({ id: 41, amount: 150, woNumber: "WO-99" }); expect(screen.queryByRole("button", { name: "Confirm" })).not.toBeInTheDocument(); expect(screen.queryByRole("dialog")).not.toBeInTheDocument(); }); it("opens RejectDialog with the dispatcher copy and does not reject on Cancel", async () => { const { default: UpliftQueuePage } = await import("@/app/(protected)/uplifts/index"); renderWithProviders(); fireEvent.click(screen.getByRole("button", { name: "Reject" })); expect(screen.getByRole("heading", { name: /reject uplift of \$250/i })).toBeInTheDocument(); expect(screen.getByText("The dispatcher sees this reason on WO #WO-99.")).toBeInTheDocument(); fireEvent.click(screen.getByRole("button", { name: "Cancel" })); expect(rejectMutate).not.toHaveBeenCalled(); }); it("shows the pending empty copy when nothing is waiting", async () => { queueState.data = { items: [], totalCount: 0, page: 1, pageSize: 25, pendingExposureTotal: null, }; const { default: UpliftQueuePage } = await import("@/app/(protected)/uplifts/index"); renderWithProviders(); expect(screen.getByText("No uplifts waiting for approval.")).toBeInTheDocument(); }); it("disables Approve and Reject for a Dispatcher the backend refuses, without dialogs or toasts", async () => { canApproveState.data = false; const { default: UpliftQueuePage } = await import("@/app/(protected)/uplifts/index"); renderWithProviders(); const approve = screen.getByRole("button", { name: "Approve" }); const reject = screen.getByRole("button", { name: "Reject" }); expect(approve).toBeDisabled(); expect(reject).toBeDisabled(); fireEvent.mouseOver(approve); expect(await screen.findByText("Requires Tier 1 role")).toBeInTheDocument(); fireEvent.click(approve); fireEvent.click(reject); expect(approveMutate).not.toHaveBeenCalled(); expect(rejectMutate).not.toHaveBeenCalled(); expect(screen.queryByRole("dialog")).not.toBeInTheDocument(); expect(screen.queryByRole("heading", { name: /reject uplift of/i })).not.toBeInTheDocument(); }); it("keeps Approve and Reject enabled when the backend allows the tier", async () => { const { default: UpliftQueuePage } = await import("@/app/(protected)/uplifts/index"); renderWithProviders(); expect(screen.getByRole("button", { name: "Approve" })).toBeEnabled(); expect(screen.getByRole("button", { name: "Reject" })).toBeEnabled(); }); });