Gates / frontend (push) Successful in 1m33s
Gates / test (push) Successful in 1m48s
Gates / test-aarch64 (push) Successful in 7m10s
Gates / package (push) Successful in 5m31s
Gates / container (push) Successful in 15s
CI / gates (push) Successful in 14m51s
129 lines
3.8 KiB
TypeScript
129 lines
3.8 KiB
TypeScript
import { fireEvent, render, screen } from "@testing-library/react";
|
|
import { QueryClientProvider } from "@tanstack/react-query";
|
|
import { RouterProvider, createMemoryHistory } from "@tanstack/react-router";
|
|
import { AuthProvider, resetAuthProbeForTests } from "@/auth/store";
|
|
import { createQueryClient } from "@/lib/queryClient";
|
|
import { createAppRouter } from "@/routes";
|
|
|
|
const NAV_LABELS = [
|
|
"Dashboard",
|
|
"Query Log",
|
|
"Live",
|
|
"Clients",
|
|
"Groups",
|
|
"Blocklists",
|
|
"Rules",
|
|
"Local DNS",
|
|
"Upstreams",
|
|
"Lookup",
|
|
"Diagnostics",
|
|
"Settings",
|
|
];
|
|
|
|
const RESPONSES: Record<string, unknown> = {
|
|
"/api/stats?period=24h": {
|
|
period: "24h",
|
|
since: 0,
|
|
until: 86400,
|
|
queries: 0,
|
|
blocked: 0,
|
|
cached: 0,
|
|
clients: 0,
|
|
avg_response_time_us: null,
|
|
},
|
|
"/api/stats/timeseries?period=24h": { period: "24h", since: 0, until: 86400, bucket_seconds: 1800, buckets: [] },
|
|
"/api/health": {
|
|
status: "ok",
|
|
disk: { state: "ok", free_bytes: 0, db_bytes: 0, log_bytes: 0, sample_failures: 0 },
|
|
upstreams: { available: 1, total: 1 },
|
|
queries_dropped: 0,
|
|
writer_failed: false,
|
|
refreshes_gated: 0,
|
|
snapshot_generation: null,
|
|
diagnostics: { state: "recording", active_warnings: 0, active_errors: 0 },
|
|
},
|
|
"/api/upstream/health": { upstreams: [], available: 1, total: 1 },
|
|
"/api/version": { version: "0.0.0-test", git_commit: "0000000", zig_version: "0.16.0", uptime_seconds: 1 },
|
|
};
|
|
|
|
beforeEach(() => {
|
|
sessionStorage.clear();
|
|
resetAuthProbeForTests();
|
|
vi.stubGlobal(
|
|
"fetch",
|
|
vi.fn(async (input: RequestInfo | URL) => {
|
|
const url = String(input);
|
|
const payload = RESPONSES[url];
|
|
if (payload === undefined) return new Response(JSON.stringify({ error: "not stubbed" }), { status: 404 });
|
|
return new Response(JSON.stringify(payload), {
|
|
status: 200,
|
|
headers: { "content-type": "application/json" },
|
|
});
|
|
}),
|
|
);
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.unstubAllGlobals();
|
|
});
|
|
|
|
test("shell renders the dashboard route with all nav links", async () => {
|
|
const queryClient = createQueryClient();
|
|
const router = createAppRouter(createMemoryHistory({ initialEntries: ["/"] }), queryClient);
|
|
render(
|
|
<AuthProvider>
|
|
<QueryClientProvider client={queryClient}>
|
|
<RouterProvider router={router} />
|
|
</QueryClientProvider>
|
|
</AuthProvider>,
|
|
);
|
|
|
|
await screen.findByRole("heading", { name: "Dashboard" });
|
|
|
|
const nav = screen.getByRole("navigation", { name: "Main" });
|
|
expect(nav).toBeTruthy();
|
|
for (const label of NAV_LABELS) {
|
|
expect(screen.getByRole("link", { name: label })).toBeTruthy();
|
|
}
|
|
});
|
|
|
|
test("mount probe reveals the logout button and a failed logout surfaces inline", async () => {
|
|
vi.stubGlobal(
|
|
"fetch",
|
|
vi.fn(async (input: RequestInfo | URL) => {
|
|
const url = String(input);
|
|
if (url === "/api/auth/login")
|
|
return new Response(JSON.stringify({ error: "password required" }), {
|
|
status: 401,
|
|
headers: { "content-type": "application/json" },
|
|
});
|
|
if (url === "/api/auth/logout")
|
|
return new Response(JSON.stringify({ error: "rate limited" }), {
|
|
status: 429,
|
|
headers: { "content-type": "application/json", "retry-after": "7" },
|
|
});
|
|
const payload = RESPONSES[url];
|
|
if (payload === undefined) return new Response(JSON.stringify({ error: "not stubbed" }), { status: 404 });
|
|
return new Response(JSON.stringify(payload), {
|
|
status: 200,
|
|
headers: { "content-type": "application/json" },
|
|
});
|
|
}),
|
|
);
|
|
|
|
const queryClient = createQueryClient();
|
|
const router = createAppRouter(createMemoryHistory({ initialEntries: ["/"] }), queryClient);
|
|
render(
|
|
<AuthProvider>
|
|
<QueryClientProvider client={queryClient}>
|
|
<RouterProvider router={router} />
|
|
</QueryClientProvider>
|
|
</AuthProvider>,
|
|
);
|
|
|
|
fireEvent.click(await screen.findByRole("button", { name: "Log out" }));
|
|
|
|
await screen.findByText("Rate limited. Try again in 7s.");
|
|
expect(screen.getByRole("heading", { name: "Dashboard" })).toBeTruthy();
|
|
});
|