import { render, screen } from "@testing-library/react"; import type { QueryRow } from "@/lib/types"; import type { ClientNames } from "@/features/clients/clientNames"; import { queryRow } from "@/features/provenance/provenanceFixture"; import { summarizeRow, type QuerySummary } from "@/features/provenance/querySummary"; import { ACTIVITY_COLUMNS, ActivityCells, ActivityTableHead, resultLabel, routeLabel } from "./cells"; const noNames: ClientNames = new Map(); function renderRow(overrides: Partial = {}): HTMLTableRowElement { const row: QuerySummary = summarizeRow(queryRow(1, overrides)); render( {children}} />
, ); return screen.getByTestId("row") as HTMLTableRowElement; } /** The cell under a header, read by its column name rather than its index. */ function cell(row: HTMLTableRowElement, column: (typeof ACTIVITY_COLUMNS)[number]): string { const index = ACTIVITY_COLUMNS.indexOf(column); return row.cells[index]?.textContent ?? ""; } test("the head names the seven columns in order", () => { render(
, ); const headers = screen.getAllByRole("columnheader").map((header) => header.textContent); expect(headers).toEqual(["Time", "Domain", "Client", "Type", "Result", "Route", "Duration"]); }); test("an allowed NOERROR row reads as the answer it got, with no badge", () => { const row = renderRow({ blocked: false, rcode: 0, route_kind: "upstream", response_time_us: 1234 }); expect(cell(row, "Result")).toBe("NOERROR"); expect(cell(row, "Route")).toBe("Upstream"); expect(cell(row, "Duration")).toBe("1.2 ms"); expect(cell(row, "Type")).toBe("A"); }); test("a blocked row reads Blocked even though the client got NOERROR", () => { const row = renderRow({ blocked: true, rcode: 0, route_kind: "blocked", policy_reason: "blocklist_domain" }); expect(cell(row, "Result")).toBe("Blocked"); expect(cell(row, "Route")).toBe("Blocked"); }); test("a SERVFAIL row names the code", () => { const row = renderRow({ blocked: false, rcode: 2, route_kind: "upstream", response_time_us: null }); expect(cell(row, "Result")).toBe("SERVFAIL"); expect(cell(row, "Duration")).toBe("—"); }); test("a cache hit names the cache as the route", () => { const row = renderRow({ blocked: false, rcode: 0, route_kind: "cache", cache_hit: true, upstream: "" }); expect(cell(row, "Route")).toBe("Cache"); expect(cell(row, "Result")).toBe("NOERROR"); }); test("an unassigned extended rcode keeps the numeric fallback, without the long form's parentheses", () => { const row = renderRow({ blocked: false, rcode: 3841 }); expect(cell(row, "Result")).toBe("RCODE 3841"); }); test("a persisted row links its domain to the detail the caller chose", () => { renderRow({ domain: "ads.example" }); expect(screen.getByRole("link", { name: "ads.example" }).getAttribute("href")).toBe("/activity/queries/1"); }); test("a streamed row reaches the renderer with a null id, and can render as plain text", () => { render( { expect(id).toBeNull(); return children; }} />
, ); expect(screen.queryByRole("link")).toBeNull(); expect(screen.getByTestId("row").textContent).toContain("example.com"); }); test("every route kind has a compact label", () => { expect(routeLabel("blocked")).toBe("Blocked"); expect(routeLabel("local")).toBe("Local"); expect(routeLabel("forward_zone")).toBe("Forward zone"); expect(routeLabel("upstream")).toBe("Upstream"); expect(routeLabel("cache")).toBe("Cache"); expect(routeLabel("rejected")).toBe("Rejected"); }); test("resultLabel is the pure form of the Result cell", () => { expect(resultLabel({ blocked: true, rcode: 2 })).toBe("Blocked"); expect(resultLabel({ blocked: false, rcode: 5 })).toBe("REFUSED"); });