mirror of
https://github.com/MeshCore-Beacon/beacon-web.git
synced 2026-09-05 05:03:45 +00:00
useLiveOverview keyed its setQueryData on the whole RegionFilter object while the overview query keys on the regionKey string, so every WS bump silently missed the cache and the counters only moved on the 60s refetch. Also drop pending counts when the region changes so old-region increments don't bleed into the new KPIs.
69 lines
2.4 KiB
TypeScript
69 lines
2.4 KiB
TypeScript
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
import { renderHook } from "@testing-library/react";
|
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
import type { ReactNode } from "react";
|
|
import { useLiveOverview } from "../../../src/features/stats/useLiveStats";
|
|
import type { StatsOverview } from "../../../src/features/stats/types";
|
|
import type { WsManager } from "../../../src/api/ws-manager";
|
|
import type { WsPacketObservation } from "../../../src/types/ws";
|
|
|
|
vi.mock("../../../src/hooks/useRegion", () => ({
|
|
useRegion: () => ({ iatas: ["YOW"], regionKey: "YOW" }),
|
|
}));
|
|
|
|
const overview: StatsOverview = {
|
|
totalPackets: 100,
|
|
totalObservations: 200,
|
|
activeObservers: 5,
|
|
activeIatas: 2,
|
|
windowHours: 24,
|
|
};
|
|
|
|
// Captures the packet handler the hook registers and lets the test push events through it.
|
|
function fakeManager() {
|
|
const handlers: Array<(d: WsPacketObservation["data"]) => void> = [];
|
|
const manager = {
|
|
onPacketObservation: (h: (d: WsPacketObservation["data"]) => void) => {
|
|
handlers.push(h);
|
|
return () => {};
|
|
},
|
|
} as unknown as WsManager;
|
|
return { manager, emit: (d: WsPacketObservation["data"]) => handlers.forEach((h) => h(d)) };
|
|
}
|
|
|
|
describe("useLiveOverview", () => {
|
|
let rafCallbacks: FrameRequestCallback[];
|
|
|
|
beforeEach(() => {
|
|
rafCallbacks = [];
|
|
vi.stubGlobal("requestAnimationFrame", (cb: FrameRequestCallback) => {
|
|
rafCallbacks.push(cb);
|
|
return rafCallbacks.length;
|
|
});
|
|
vi.stubGlobal("cancelAnimationFrame", () => {});
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.unstubAllGlobals();
|
|
});
|
|
|
|
it("bumps the cached stats-overview counters on packet observations", () => {
|
|
const qc = new QueryClient();
|
|
qc.setQueryData<StatsOverview>(["stats-overview", "YOW"], overview);
|
|
const { manager, emit } = fakeManager();
|
|
|
|
const wrapper = ({ children }: { children: ReactNode }) => (
|
|
<QueryClientProvider client={qc}>{children}</QueryClientProvider>
|
|
);
|
|
renderHook(() => useLiveOverview(manager), { wrapper });
|
|
|
|
emit({ packet: { isFirstObservation: true } } as WsPacketObservation["data"]);
|
|
emit({ packet: { isFirstObservation: false } } as WsPacketObservation["data"]);
|
|
rafCallbacks.forEach((cb) => cb(0)); // flush the coalesced frame
|
|
|
|
const after = qc.getQueryData<StatsOverview>(["stats-overview", "YOW"]);
|
|
expect(after?.totalPackets).toBe(101);
|
|
expect(after?.totalObservations).toBe(202);
|
|
});
|
|
});
|