80 lines
3.1 KiB
TypeScript
80 lines
3.1 KiB
TypeScript
// @vitest-environment happy-dom
|
|
// PayerPeekContent uses useQuery internally via usePayerSummary. We mock
|
|
// that hook here so the component can be rendered without standing up a
|
|
// real QueryClient — same pattern as useClaimDetail.test.ts.
|
|
(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
|
|
|
|
import { afterEach, describe, it, expect, vi } from "vitest";
|
|
import { cleanup, render, screen } from "@testing-library/react";
|
|
import { MemoryRouter } from "react-router-dom";
|
|
import type { ReactNode } from "react";
|
|
import { PayerPeekContent } from "@/components/drill/PayerPeekContent";
|
|
|
|
// Mock the hook BEFORE the import above is resolved (vitest hoists
|
|
// `vi.mock` to the top of the file regardless of where it appears
|
|
// syntactically).
|
|
vi.mock("@/hooks/usePayerSummary", () => ({
|
|
usePayerSummary: vi.fn(),
|
|
}));
|
|
|
|
// Importing after vi.mock so we get the mocked reference.
|
|
import { usePayerSummary } from "@/hooks/usePayerSummary";
|
|
|
|
// happy-dom keeps `document.body` between tests; without cleanup,
|
|
// `screen.getByText(...)` would find nodes from earlier renders.
|
|
afterEach(() => cleanup());
|
|
|
|
// The component renders <Link>, which requires a router context. A bare
|
|
// MemoryRouter with no initialEntries is enough — the test asserts on the
|
|
// generated href, not on navigation.
|
|
function withRouter(node: ReactNode) {
|
|
return <MemoryRouter>{node}</MemoryRouter>;
|
|
}
|
|
|
|
const SAMPLE_PAYER = {
|
|
payer_id: "SKCO0",
|
|
name: "CO Medicaid",
|
|
claim_count: 1247,
|
|
billed_total: 548000,
|
|
received_total: 521000,
|
|
denial_rate: 0.042,
|
|
top_providers: [{ npi: "1881068062", count: 184 }],
|
|
} as const;
|
|
|
|
describe("PayerPeekContent", () => {
|
|
it("renders loading skeleton while fetching", () => {
|
|
// Idle / in-flight state — `data` undefined, `isLoading` true.
|
|
// The component renders <Skeleton variant="row" /> rows and no text,
|
|
// so the regex match for /claims/i must come back null (the
|
|
// "View all claims" link only renders once data is present).
|
|
(usePayerSummary as unknown as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
data: undefined,
|
|
isLoading: true,
|
|
});
|
|
|
|
render(withRouter(<PayerPeekContent payerId="SKCO0" />));
|
|
expect(screen.queryByText(/claims/i)).toBeNull();
|
|
});
|
|
|
|
it("renders summary stats when data loads", () => {
|
|
(usePayerSummary as unknown as ReturnType<typeof vi.fn>).mockReturnValue({
|
|
data: SAMPLE_PAYER,
|
|
isLoading: false,
|
|
});
|
|
|
|
render(withRouter(<PayerPeekContent payerId="SKCO0" />));
|
|
|
|
expect(screen.getByText("CO Medicaid")).toBeTruthy();
|
|
// fmt.num(1247) === "1,247" — the "184 claims" line also matches this
|
|
// regex, but getByText with a regex is fine because we only assert
|
|
// existence.
|
|
expect(screen.getByText(/1,247/)).toBeTruthy();
|
|
expect(screen.getByText("$548,000")).toBeTruthy();
|
|
// denial_rate is a fraction (0.042); fmt.pct(payer.denial_rate * 100)
|
|
// yields "4.2%".
|
|
expect(screen.getByText("4.2%")).toBeTruthy();
|
|
|
|
const link = screen.getByRole("link", { name: /view all claims/i });
|
|
expect(link.getAttribute("href")).toBe("/claims?payer=SKCO0");
|
|
});
|
|
}); |