Files
homarr/packages/api/src/test/query-cache.spec.ts
T
Thomas Camlong 240397e7ac refactor: drop server-seeded widget clock
The WidgetTimeProvider plumbed a server timestamp and the container's
timezone down to useWidgetNow so clock widgets would paint a real time in
the SSR HTML instead of "--:--". That traded a cosmetic first-paint win for
a wrong clock: the seeded timezone is the server's, so anyone whose timezone
differs from the container's saw an incorrect time for the whole hydration
window. Fixing that properly needs a client-set timezone cookie read during
RSC render, which is far more plumbing than the problem warrants.

Revert the clock to release/v2 behaviour ("--:--" until hydration) and drop
the provider, the @homarr/widgets/time export and their tests. The e2e clock
assertions now wait on hydration explicitly instead of the seeded paint.

Also restore the widget.beszel.getSystemStats persistence exclusion. #6289
added it deliberately to keep those large time-series payloads out of the
persisted cache, and this branch had removed it with no stated rationale.

Query-cache persistence and the background-refresh indicator are unchanged.
2026-08-23 09:00:34 +02:00

114 lines
3.6 KiB
TypeScript

import { afterEach, describe, expect, test, vi } from "vitest";
import { QueryClient } from "@tanstack/react-query";
import {
dashboardSupportingQueryPolicies,
isWidgetDataQueryKey,
isWidgetDataTrpcPath,
queryCacheDefaultRefetchIntervalMs,
queryCacheDefaultStaleTimeMs,
queryCacheMetadataStaleTimeMs,
} from "@homarr/api/query-cache";
afterEach(() => {
vi.useRealTimers();
});
describe("isWidgetDataQueryKey", () => {
test.each([
[[["widget", "calendar", "findAllEvents"], { type: "query" }], true],
[[["app", "byId"], { type: "query" }], true],
[[["app", "byIds"], { type: "query" }], true],
[[["docker", "getContainers"], { type: "query" }], true],
[[["integration", "byIds"], { type: "query" }], true],
[[["widget", "app", "ping"], { type: "query", input: { id: "abc" } }], false],
[[["widget", "beszel", "getSystemStats"], { type: "query" }], false],
[[["widget", "beszel", "getSystems"], { type: "query" }], true],
[[["app", "selectable"], { type: "query" }], false],
[[["board", "getBoardByName"], { type: "query" }], false],
[["widget"], false],
])("matches widget data queries for %j", (queryKey, expected) => {
expect(isWidgetDataQueryKey(queryKey)).toBe(expected);
});
});
describe("isWidgetDataTrpcPath", () => {
test.each([
["widget.calendar.findAllEvents", true],
["app.byIds", true],
["integration.byIds", true],
["docker.getContainers", true],
["widget.app.ping", false],
["widget.beszel.getSystemStats", false],
["board.getBoardByName", false],
])("classifies dashboard data path %s", (path, expected) => {
expect(isWidgetDataTrpcPath(path)).toBe(expected);
});
});
describe("query cache stale handling", () => {
test("uses TanStack Query dataUpdatedAt and staleTime to decide when cached data refetches", async () => {
vi.useFakeTimers();
vi.setSystemTime(new Date("2026-01-01T00:10:00.000Z"));
const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: false,
staleTime: queryCacheDefaultStaleTimeMs,
},
},
});
const queryKey = [["widget", "example"], { input: { integrationIds: ["integration-1"] }, type: "query" }];
const queryFn = vi.fn().mockResolvedValue("fresh");
queryClient.setQueryData(queryKey, "cached", {
updatedAt: Date.now() - queryCacheDefaultStaleTimeMs - 1,
});
await expect(
queryClient.fetchQuery({
queryKey,
queryFn,
}),
).resolves.toBe("fresh");
expect(queryFn).toHaveBeenCalledOnce();
});
});
describe("dashboard supporting query policies", () => {
test("bounds metadata staleness without adding per-widget timers", () => {
expect(dashboardSupportingQueryPolicies).toEqual([
{
queryKey: [["app", "byId"]],
refetchInterval: false,
staleTime: queryCacheMetadataStaleTimeMs,
refetchOnWindowFocus: true,
refetchOnReconnect: true,
},
{
queryKey: [["app", "byIds"]],
refetchInterval: false,
staleTime: queryCacheMetadataStaleTimeMs,
refetchOnWindowFocus: true,
refetchOnReconnect: true,
},
{
queryKey: [["integration", "byIds"]],
refetchInterval: false,
staleTime: queryCacheMetadataStaleTimeMs,
refetchOnWindowFocus: true,
refetchOnReconnect: true,
},
{
queryKey: [["docker", "getContainers"]],
refetchInterval: queryCacheDefaultRefetchIntervalMs,
staleTime: queryCacheDefaultStaleTimeMs,
refetchOnWindowFocus: false,
refetchOnReconnect: false,
},
]);
});
});