81 lines
2.6 KiB
TypeScript
81 lines
2.6 KiB
TypeScript
import { act, fireEvent, render, screen } from "@testing-library/react";
|
|
import type { LiveQueryEvent } from "@/lib/types";
|
|
import { FakeEventSource } from "./fakeEventSource";
|
|
import LiveLogPage from "./LiveLogPage";
|
|
|
|
function frame(ts: number, domain: string, overrides: Partial<LiveQueryEvent> = {}): { data: string } {
|
|
const payload: LiveQueryEvent = {
|
|
ts,
|
|
domain,
|
|
client_ip: "192.0.2.10",
|
|
qtype: 1,
|
|
blocked: false,
|
|
block_reason: "",
|
|
response_time_us: 500,
|
|
cache_hit: true,
|
|
upstream: "",
|
|
...overrides,
|
|
};
|
|
return { data: JSON.stringify(payload) };
|
|
}
|
|
|
|
function renderPage() {
|
|
const sources: FakeEventSource[] = [];
|
|
const createEventSource = (url: string) => {
|
|
const es = new FakeEventSource(url);
|
|
sources.push(es);
|
|
return es;
|
|
};
|
|
render(<LiveLogPage createEventSource={createEventSource} />);
|
|
return sources;
|
|
}
|
|
|
|
test("streams rows, flags blocked ones, and freezes the display", () => {
|
|
const sources = renderPage();
|
|
expect(screen.getByText("Connecting…")).toBeTruthy();
|
|
|
|
act(() => sources[0]!.emit("open"));
|
|
expect(screen.getByRole("status", { name: "Live" })).toBeTruthy();
|
|
expect(screen.getByText("Waiting for queries…")).toBeTruthy();
|
|
|
|
act(() => {
|
|
sources[0]!.emit("query", frame(1000, "ok.example"));
|
|
sources[0]!.emit(
|
|
"query",
|
|
frame(1001, "ads.example", { blocked: true, block_reason: "blocklist:stevenblack", qtype: 28 }),
|
|
);
|
|
});
|
|
|
|
expect(screen.getByText("ok.example")).toBeTruthy();
|
|
expect(screen.getByText("Blocked")).toBeTruthy();
|
|
expect(screen.getByText("blocklist:stevenblack")).toBeTruthy();
|
|
expect(screen.getByText("AAAA")).toBeTruthy();
|
|
const blockedRow = screen.getByText("ads.example").closest("tr");
|
|
expect(blockedRow?.className).toContain("bg-red-50");
|
|
|
|
const freeze = screen.getByRole("button", { name: "Freeze" });
|
|
fireEvent.click(freeze);
|
|
expect(freeze.getAttribute("aria-pressed")).toBe("true");
|
|
|
|
act(() => sources[0]!.emit("query", frame(1002, "later.example")));
|
|
expect(screen.queryByText("later.example")).toBeNull();
|
|
expect(screen.getByText(/3 in buffer/)).toBeTruthy();
|
|
|
|
fireEvent.click(screen.getByRole("button", { name: "Resume" }));
|
|
expect(screen.getByText("later.example")).toBeTruthy();
|
|
});
|
|
|
|
test("repeated connection failures show the viewer-cap state with a retry button", () => {
|
|
const sources = renderPage();
|
|
act(() => {
|
|
sources[0]!.emit("error");
|
|
sources[0]!.emit("error");
|
|
sources[0]!.emit("error");
|
|
});
|
|
expect(screen.getByRole("alert").textContent).toContain("too many live viewers");
|
|
|
|
fireEvent.click(screen.getByRole("button", { name: "Retry" }));
|
|
expect(sources).toHaveLength(2);
|
|
expect(screen.getByText("Connecting…")).toBeTruthy();
|
|
});
|