diff --git a/src/app/dashboard/workforce/missing-rate-overview.test.tsx b/src/app/dashboard/workforce/missing-rate-overview.test.tsx index 31a6e7c..6f21995 100644 --- a/src/app/dashboard/workforce/missing-rate-overview.test.tsx +++ b/src/app/dashboard/workforce/missing-rate-overview.test.tsx @@ -1,5 +1,5 @@ import { renderToStaticMarkup } from "react-dom/server"; -import { beforeEach, expect, it, vi } from "vitest"; +import { afterEach, beforeEach, expect, it, vi } from "vitest"; const mocks = vi.hoisted(() => ({ list: vi.fn(), scope: vi.fn() })); vi.mock("next/navigation", () => ({ useRouter: () => ({ refresh: vi.fn() }) })); vi.mock("@/lib/pb", () => ({ pbAdmin: async () => ({ collection: (name: string) => ({ getFullList: (options: unknown) => mocks.list(name, options) }) }) })); @@ -12,6 +12,10 @@ vi.mock("./workforce-charts", () => ({ WorkforceHoursChart: () => null, TopWorke import WorkforceLanding from "./page"; beforeEach(() => { + // Pinned so "this month" is a fixed window: the unpriced-hours warning + // is now scoped to the current month, not to all history. + vi.useFakeTimers(); + vi.setSystemTime(new Date("2026-10-04T10:00:00Z")); mocks.scope.mockResolvedValue({ active: [{ id: "site1" }], allowed: [{ id: "site1", name: "Head Office" }], mode: "single", unrestricted: false }); mocks.list.mockImplementation(async (name: string) => { if (name === "employees") return [ @@ -22,21 +26,30 @@ beforeEach(() => { { employee: "paid", date_logged: "2026-09-30", hours_worked: 8 }, { employee: "paid", date_logged: "2026-10-01", hours_worked: 8 }, { employee: "missing", date_logged: "2026-09-15", hours_worked: 5 }, + { employee: "missing", date_logged: "2026-10-02", hours_worked: 5 }, ]; if (name === "employee_rates") return [{ employee: "paid", effective_from: "2026-10-01", rate_per_hour: 60 }]; if (name === "sites") return [{ id: "site1", name: "Head Office" }]; return []; }); }); +afterEach(() => vi.useRealTimers()); it("shows a missing-rate action and an incomplete cost total using dated payroll", async () => { const html = renderToStaticMarkup(await WorkforceLanding({ searchParams: Promise.resolve({}) })); expect(html).toContain("Needs attention"); expect(html).toContain("Missing Worker"); expect(html).toContain("Add rate for Missing Worker"); - expect(html).toContain("5 hrs still need rates"); + // Only the 5 October hours are unpriced this month; the 5 September + // ones are history and no longer reported on the tile. + expect(html).toContain("5 hrs this month still need rates"); expect(html).toContain("Paid Worker: 880"); - expect(mocks.list).toHaveBeenCalledWith("employee_rates", { filter: 'employee.site="site1"' }); +}); + +it("does not filter rate history by site, so a worker priced elsewhere is still priced", async () => { + await WorkforceLanding({ searchParams: Promise.resolve({}) }); + const rateCall = mocks.list.mock.calls.find(([name]) => name === "employee_rates"); + expect(rateCall?.[1]).toBeUndefined(); }); it("hides inactive workers from rate alerts but preserves their recorded costs", async () => {