test(workforce): update the missing-rate overview for this-month figures

The overview test asserted the previous all-time sub-line wording and
that employee_rates was read with a site filter, both of which the cost
workforce commits changed on purpose.

Pins the clock so "this month" is a fixed window, and gives the unpriced
worker an October entry so the new month-scoped warning is actually
exercised rather than skipped. Added a case asserting rate history is
read unfiltered, which is what lets a worker who moved sites keep the
rate he was paid at.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
DEls 2026-10-10 00:14:41 +02:00
parent 373ff2c1d3
commit 0987dcc33a

View file

@ -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 () => {