feat(frontend): Remittances + ActivityLog wire live tail

This commit is contained in:
Tyler
2026-06-20 17:14:37 -06:00
parent 365e64e25a
commit fa23eb182e
3 changed files with 115 additions and 13 deletions
+49
View File
@@ -10,6 +10,7 @@ import { describe, expect, it, vi, beforeEach } from "vitest";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { Remittances } from "./Remittances";
import { api } from "@/lib/api";
import { useTailStore } from "@/store/tail-store";
// Module-level mock: vitest hoists `vi.mock` calls above imports. We only
// stub the method this page touches via the `useRemittances` hook.
@@ -23,6 +24,18 @@ vi.mock("@/lib/api", () => ({
},
}));
// Mock the live-tail hook so the page renders the pill in the settled
// `live` state without driving the real streamTail parser. The hook's
// own test file (`useTailStream.test.ts`) covers the lifecycle in depth.
vi.mock("@/hooks/useTailStream", () => ({
useTailStream: vi.fn(() => ({
status: "live",
lastEventAt: null,
error: null,
forceReconnect: vi.fn(),
})),
}));
/**
* Minimal `render` helper using react-dom/client + act(). Mirrors the
* `renderIntoContainer` helper in `Reconciliation.test.tsx` — see that
@@ -78,6 +91,10 @@ async function waitForText(
describe("Remittances", () => {
beforeEach(() => {
vi.clearAllMocks();
// Singleton tail-store: clear the remittances slice between tests
// so a tail-arrival case (if added later) doesn't see rows from a
// previous test.
useTailStore.getState().reset("remittances");
});
it("renders a CAS adjustment label inside the expanded detail row", async () => {
@@ -146,4 +163,36 @@ describe("Remittances", () => {
expect(document.body.textContent).toContain("PR-1");
unmount();
});
// -------------------------------------------------------------------
// Live-tail status pill (sub-project 5, Phase 5 Task 23, optional
// page-level test). Cheap smoke check: the toolbar renders the pill
// with the `live` label whenever the mocked stream is settled. The
// hook's lifecycle is covered exhaustively in its own test file.
// -------------------------------------------------------------------
it("test_status_pill_shows_live_in_toolbar", async () => {
(
api.listRemittances as unknown as ReturnType<typeof vi.fn>
).mockResolvedValue({
items: [],
total: 0,
returned: 0,
has_more: false,
});
const { unmount } = renderIntoContainer(React.createElement(Remittances));
// Wait for the empty-state to mount — once the page body is rendered,
// the toolbar (and thus the pill) is in the DOM.
await waitForText("Remittances · awaiting first 835");
const pill = document.body.querySelector(
'[data-testid="tail-status-pill"]',
);
expect(pill).not.toBeNull();
expect(pill?.textContent).toContain("Live");
unmount();
});
});