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>
50 lines
1.7 KiB
TypeScript
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,
|
|
);
|