cameras/test/dom.tsx
Michael Mainguy 870b4cd2af Move dashboard client onto React Query hooks
Adds src/app/camera-queries.ts: React Query hooks over our own routes,
with each camera's queries keyed ['camera', id]. The camera card,
snapshot polling, login form and network scan use these hooks instead
of hand-rolled fetch/useEffect state (vrek iss-2fm6x2y, iss-ksxmctm,
iss-m032zwq, iss-8hfq2y2).

- Snapshot polling pauses in hidden tabs, never overlaps a slow
  frame, and stops after a failure until Retry. Each frame's object
  URL is created and revoked in one effect, so none leak under Strict
  Mode.
- Saving or forgetting a login resets only that camera's queries.
- New "Forget saved login" action, shown for stored logins.
- Fix: a scan timeout typed below 1 s now clamps to 1 s instead of
  falling back to 5 s.

Adds jsdom component tests (test/dom.tsx helpers): 93 tests, line
coverage 54.3%. Refreshes the vrek export.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-19 09:01:34 -05:00

50 lines
1.7 KiB
TypeScript

import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { cleanup, render } from "@testing-library/react";
import type { ReactNode } from "react";
import { afterEach, vi } from "vitest";
afterEach(cleanup);
/** Same defaults as the app's Providers, but retries fire immediately. */
export function testQueryClient() {
return new QueryClient({
defaultOptions: {
queries: { staleTime: 0, retry: 1, retryDelay: 0 },
mutations: { retry: false },
},
});
}
export function renderWithQuery(ui: ReactNode, client = testQueryClient()) {
const wrapper = ({ children }: { children: ReactNode }) => (
<QueryClientProvider client={client}>{children}</QueryClientProvider>
);
return { client, ...render(ui, { wrapper }) };
}
type Handler = (init: RequestInit | undefined) => Response | Promise<Response>;
/**
* Stubs global fetch with handlers keyed by "METHOD /path". Unmatched requests fail the
* test loudly instead of hitting the network.
*/
export function stubFetch(routes: Record<string, Handler>) {
const fetchMock = vi.fn(async (input: RequestInfo | URL, init?: RequestInit) => {
const url = new URL(String(input), "http://localhost");
const key = `${init?.method ?? "GET"} ${url.pathname}`;
const handler = routes[key];
if (!handler) throw new Error(`Unexpected fetch: ${key}`);
return handler(init);
});
vi.stubGlobal("fetch", fetchMock);
return fetchMock;
}
export const json = (body: unknown, status = 200) => Response.json(body, { status });
export const calls = (fetchMock: ReturnType<typeof stubFetch>, key: string) =>
fetchMock.mock.calls.filter(
([input, init]) =>
`${init?.method ?? "GET"} ${new URL(String(input), "http://localhost").pathname}` === key,
);