Files
BanGUI/frontend/src/hooks/__tests__/usePolledData.test.ts
Lukas 4a649e7347 chore: bump to v0.9.19-rc.1 and add local OpenAPI build support
- Add release candidate (rc) support to release.sh with latestRC tagging
- Bump VERSION, backend pyproject.toml, and frontend package.json to 0.9.19-rc.1
- Add local frontend/openapi.json so build no longer needs running backend
- Update generate:types and validate-types.sh to use local openapi.json
- Fix frontend tests: remove unused imports/variables and update mock data
2026-05-24 22:05:34 +02:00

179 lines
4.4 KiB
TypeScript

import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { renderHook, act } from "@testing-library/react";
import { usePolledData } from "../usePolledData";
// Mock usePageVisibility to always return true (page visible)
vi.mock("../usePageVisibility", () => ({
usePageVisibility: () => true,
}));
describe("usePolledData", () => {
beforeEach(() => {
vi.useFakeTimers();
});
afterEach(() => {
vi.runOnlyPendingTimers();
vi.useRealTimers();
});
it("stops polling when pollInterval is undefined", async () => {
const fetcher = vi.fn().mockResolvedValue({ value: "test" });
const selector = vi.fn((response: { value: string }) => response.value);
renderHook(() =>
usePolledData({
fetcher,
selector,
errorMessage: "Failed to load",
})
);
// Initial fetch should happen in useFetchData
await act(async () => {
vi.runAllTimersAsync();
});
// Reset timer and advance to ensure no more polls
vi.clearAllTimers();
fetcher.mockClear();
await act(async () => {
vi.advanceTimersByTime(10000);
});
// Should not poll since pollInterval is undefined
expect(fetcher).not.toHaveBeenCalled();
});
it("implements drift correction: next poll is scheduled based on elapsed time", async () => {
const fetcher = vi.fn().mockResolvedValue({ value: "test" });
const selector = vi.fn((response: { value: string }) => response.value);
renderHook(() =>
usePolledData({
fetcher,
selector,
errorMessage: "Failed to load",
pollInterval: 5000,
})
);
// Wait for initial setup
await act(async () => {
vi.advanceTimersByTime(100);
});
// Clear for clean test
fetcher.mockClear();
// First poll completes after ~0ms, next poll should be in ~5000ms
// Advance 4000ms
await act(async () => {
vi.advanceTimersByTime(4000);
});
expect(fetcher).not.toHaveBeenCalled();
// Advance to 5500ms total, poll should have happened
await act(async () => {
vi.advanceTimersByTime(1500);
});
expect(fetcher).toHaveBeenCalled();
});
it("cleans up timers on unmount", async () => {
const fetcher = vi.fn().mockResolvedValue({ value: "test" });
const selector = vi.fn((response: { value: string }) => response.value);
const { unmount } = renderHook(() =>
usePolledData({
fetcher,
selector,
errorMessage: "Failed to load",
pollInterval: 5000,
})
);
await act(async () => {
vi.advanceTimersByTime(1000);
});
const callCount = fetcher.mock.calls.length;
// Unmount
unmount();
// Advance time and verify no new fetches
await act(async () => {
vi.advanceTimersByTime(10000);
});
// Should not increase beyond initial calls
expect(fetcher.mock.calls.length).toBe(callCount);
});
it("calls refresh callback to trigger immediate fetch", async () => {
const fetcher = vi.fn().mockResolvedValue({ value: "test" });
const selector = vi.fn((response: { value: string }) => response.value);
const { result } = renderHook(() =>
usePolledData({
fetcher,
selector,
errorMessage: "Failed to load",
pollInterval: 5000,
})
);
await act(async () => {
vi.advanceTimersByTime(100);
});
fetcher.mockClear();
// Call refresh
await act(async () => {
result.current.refresh();
vi.advanceTimersByTime(100);
});
expect(fetcher).toHaveBeenCalled();
});
it("returns initial data if provided", () => {
const fetcher = vi.fn().mockResolvedValue({ value: "updated" });
const selector = vi.fn();
const { result } = renderHook(() =>
usePolledData({
fetcher,
selector,
errorMessage: "Failed to load",
initialData: "initial",
pollInterval: 5000,
})
);
expect(result.current.data).toBe("initial");
expect(result.current.loading).toBe(true);
});
it("returns null when data is undefined and no initialData", () => {
const fetcher = vi.fn().mockResolvedValue({ value: "test" });
const selector = vi.fn();
const { result } = renderHook(() =>
usePolledData({
fetcher,
selector,
errorMessage: "Failed to load",
pollInterval: 5000,
})
);
expect(result.current.data).toBeNull();
});
});