feat(sp28): Acks page Claims badge column with orphan/single/many variants
This commit is contained in:
+160
-1
@@ -6,6 +6,7 @@ import React, { act } from "react";
|
||||
import { createRoot, type Root } from "react-dom/client";
|
||||
import { describe, expect, it, vi, beforeEach } from "vitest";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import { MemoryRouter } from "react-router-dom";
|
||||
import { Acks } from "./Acks";
|
||||
import { api } from "@/lib/api";
|
||||
|
||||
@@ -49,8 +50,20 @@ function renderIntoContainer(element: React.ReactElement): {
|
||||
const qc = new QueryClient({ defaultOptions: { queries: { retry: false } } });
|
||||
const root: Root = createRoot(container);
|
||||
act(() => {
|
||||
// SP28: the AckDrawer mounts the new <MatchedClaim /> panel
|
||||
// which calls `useNavigate()` for cross-page navigation to
|
||||
// /claims?claim=<id>. The hook requires a router context, so
|
||||
// the test harness wraps the page in <MemoryRouter>.
|
||||
root.render(
|
||||
React.createElement(QueryClientProvider, { client: qc }, element),
|
||||
React.createElement(
|
||||
QueryClientProvider,
|
||||
{ client: qc },
|
||||
React.createElement(
|
||||
MemoryRouter,
|
||||
null,
|
||||
element,
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
return {
|
||||
@@ -685,4 +698,150 @@ describe("Acks", () => {
|
||||
|
||||
unmount();
|
||||
});
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// SP28: the "Claims" column on the 999 register. The badge carries
|
||||
// the distinct-claim link count (per-AK2 granularity, D1). Empty
|
||||
// link count → "Orphan" amber pill. Single link → muted "1 claim"
|
||||
// text. Multi link → success "{count} claims" badge.
|
||||
// -------------------------------------------------------------------------
|
||||
it("test_claims_badge_shows_orphan_when_no_linked_claims", async () => {
|
||||
(api.listAcks as unknown as ReturnType<typeof vi.fn>).mockResolvedValue({
|
||||
items: [
|
||||
{
|
||||
id: 42,
|
||||
sourceBatchId: "b-orphan",
|
||||
acceptedCount: 1,
|
||||
rejectedCount: 0,
|
||||
receivedCount: 1,
|
||||
ackCode: "A",
|
||||
parsedAt: "2026-06-20T12:00:00Z",
|
||||
linkedClaimIds: [],
|
||||
},
|
||||
],
|
||||
total: 1,
|
||||
returned: 1,
|
||||
has_more: false,
|
||||
});
|
||||
|
||||
const { unmount } = renderIntoContainer(React.createElement(Acks));
|
||||
await waitForText("b-orphan");
|
||||
|
||||
// An orphan ack renders the warning "Orphan" badge — the
|
||||
// operator's cue that the auto-link didn't resolve and they
|
||||
// may need to drill in to run a manual match.
|
||||
const orphanBadge = document.body.querySelector(
|
||||
'[data-testid="claims-badge-orphan"]',
|
||||
);
|
||||
expect(orphanBadge).not.toBeNull();
|
||||
expect(orphanBadge?.textContent).toContain("Orphan");
|
||||
|
||||
unmount();
|
||||
});
|
||||
|
||||
it("test_claims_badge_shows_single_link_text_for_one_claim", async () => {
|
||||
(api.listAcks as unknown as ReturnType<typeof vi.fn>).mockResolvedValue({
|
||||
items: [
|
||||
{
|
||||
id: 43,
|
||||
sourceBatchId: "b-single",
|
||||
acceptedCount: 2,
|
||||
rejectedCount: 0,
|
||||
receivedCount: 2,
|
||||
ackCode: "A",
|
||||
parsedAt: "2026-06-20T12:00:00Z",
|
||||
linkedClaimIds: ["CLM-1"],
|
||||
},
|
||||
],
|
||||
total: 1,
|
||||
returned: 1,
|
||||
has_more: false,
|
||||
});
|
||||
|
||||
const { unmount } = renderIntoContainer(React.createElement(Acks));
|
||||
await waitForText("b-single");
|
||||
|
||||
// Single link → muted mono text "1 claim" (not a colored pill —
|
||||
// a single resolved link is the common case and doesn't warrant
|
||||
// visual emphasis).
|
||||
const singleBadge = document.body.querySelector(
|
||||
'[data-testid="claims-badge-single"]',
|
||||
);
|
||||
expect(singleBadge).not.toBeNull();
|
||||
expect(singleBadge?.textContent).toContain("1 claim");
|
||||
// Not the orphan variant.
|
||||
expect(
|
||||
document.body.querySelector('[data-testid="claims-badge-orphan"]'),
|
||||
).toBeNull();
|
||||
|
||||
unmount();
|
||||
});
|
||||
|
||||
it("test_claims_badge_shows_count_for_multiple_linked_claims", async () => {
|
||||
// A 999 with three AK2 set-responses, each linked to a different
|
||||
// claim batch, gets a multi-link badge.
|
||||
(api.listAcks as unknown as ReturnType<typeof vi.fn>).mockResolvedValue({
|
||||
items: [
|
||||
{
|
||||
id: 44,
|
||||
sourceBatchId: "b-multi",
|
||||
acceptedCount: 3,
|
||||
rejectedCount: 0,
|
||||
receivedCount: 3,
|
||||
ackCode: "A",
|
||||
parsedAt: "2026-06-20T12:00:00Z",
|
||||
linkedClaimIds: ["CLM-1", "CLM-2", "CLM-3"],
|
||||
},
|
||||
],
|
||||
total: 1,
|
||||
returned: 1,
|
||||
has_more: false,
|
||||
});
|
||||
|
||||
const { unmount } = renderIntoContainer(React.createElement(Acks));
|
||||
await waitForText("b-multi");
|
||||
|
||||
// Multi link → success "{N} claims" badge.
|
||||
const manyBadge = document.body.querySelector(
|
||||
'[data-testid="claims-badge-many"]',
|
||||
);
|
||||
expect(manyBadge).not.toBeNull();
|
||||
expect(manyBadge?.textContent).toContain("3 claims");
|
||||
expect(manyBadge?.getAttribute("data-claims-count")).toBe("3");
|
||||
|
||||
unmount();
|
||||
});
|
||||
|
||||
it("test_claims_badge_treats_undefined_linked_claim_ids_as_orphan", async () => {
|
||||
// Defensive: older ack rows (pre-SP28) might not carry
|
||||
// `linkedClaimIds` at all. The page must render them as
|
||||
// orphan (count == 0) rather than crashing on undefined.length.
|
||||
(api.listAcks as unknown as ReturnType<typeof vi.fn>).mockResolvedValue({
|
||||
items: [
|
||||
{
|
||||
id: 45,
|
||||
sourceBatchId: "b-legacy",
|
||||
acceptedCount: 1,
|
||||
rejectedCount: 0,
|
||||
receivedCount: 1,
|
||||
ackCode: "A",
|
||||
parsedAt: "2026-06-20T12:00:00Z",
|
||||
// linkedClaimIds intentionally omitted (legacy row)
|
||||
},
|
||||
],
|
||||
total: 1,
|
||||
returned: 1,
|
||||
has_more: false,
|
||||
});
|
||||
|
||||
const { unmount } = renderIntoContainer(React.createElement(Acks));
|
||||
await waitForText("b-legacy");
|
||||
|
||||
const orphanBadge = document.body.querySelector(
|
||||
'[data-testid="claims-badge-orphan"]',
|
||||
);
|
||||
expect(orphanBadge).not.toBeNull();
|
||||
|
||||
unmount();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user