feat(sp25): wire live-tail triplet into Acks page
The 999 register opens /api/acks/stream via useTailStream("acks"),
merges the snapshot + tail through useMergedTail("acks", ...) so
new rows appear without a manual refresh, and surfaces the
connection state via <TailStatusPill> in the hero. The TA1 section
gets the same triplet against /api/ta1-acks/stream.
Both pills sit in the page header so the operator can see at a
glance whether the live-tail connection is healthy, without having
to open the drawer or refresh the page. A stalled/error stream
shows a Reconnect button inline.
The Acks test mock adds useTailStream at the module level so the
page renders without opening a real fetch. New test asserts both
TailStatusPills mount in the page tree.
This commit is contained in:
@@ -21,6 +21,21 @@ vi.mock("@/lib/api", async (importOriginal) => {
|
||||
};
|
||||
});
|
||||
|
||||
// SP25: the Acks page now opens two live-tail streams (one per ack
|
||||
// flavor) via `useTailStream`. The hook has its own dedicated test
|
||||
// suite that covers the full lifecycle; here we just want the page to
|
||||
// render against a stubbed hook so the test never opens a real fetch.
|
||||
// The mock matches the production-default state (`live`) so the pill
|
||||
// renders the "Live" label without surfacing a misleading error.
|
||||
vi.mock("@/hooks/useTailStream", () => ({
|
||||
useTailStream: vi.fn(() => ({
|
||||
status: "live",
|
||||
lastEventAt: null,
|
||||
error: null,
|
||||
forceReconnect: vi.fn(),
|
||||
})),
|
||||
}));
|
||||
|
||||
function renderIntoContainer(element: React.ReactElement): {
|
||||
container: HTMLDivElement;
|
||||
unmount: () => void;
|
||||
@@ -567,4 +582,40 @@ describe("Acks", () => {
|
||||
|
||||
unmount();
|
||||
});
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// SP25: the page now mounts two live-tail streams (one per ack
|
||||
// flavor) via useTailStream. The mocked hook returns status: "live",
|
||||
// so the page should render TWO TailStatusPills — one in the 999
|
||||
// hero and one in the TA1 section — both showing the human label
|
||||
// "Live" (per `STATUS_LABEL` in `TailStatusPill.tsx`).
|
||||
// -------------------------------------------------------------------------
|
||||
it("test_renders_two_tail_status_pills_live", async () => {
|
||||
(api.listAcks as unknown as ReturnType<typeof vi.fn>).mockResolvedValue({
|
||||
items: [],
|
||||
total: 0,
|
||||
returned: 0,
|
||||
has_more: false,
|
||||
aggregates: { accepted_count: 0, rejected_count: 0, received_count: 0 },
|
||||
});
|
||||
|
||||
const { unmount } = renderIntoContainer(React.createElement(Acks));
|
||||
|
||||
// Wait for the page to settle — both pills need the query to
|
||||
// resolve (and even the TA1 section renders a tail pill even
|
||||
// when items is empty).
|
||||
await settle(
|
||||
() => document.body.textContent?.includes("Live") ?? false,
|
||||
);
|
||||
|
||||
// Two "Live" labels: one for the 999 stream, one for the TA1
|
||||
// stream. (Plus the description text on the page may say "live"
|
||||
// in another context — check via the surrounding pill markup.)
|
||||
const pills = Array.from(
|
||||
document.querySelectorAll('[data-testid="tail-status-pill"]'),
|
||||
);
|
||||
expect(pills.length).toBe(2);
|
||||
|
||||
unmount();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user