fix: stabilize terminal dashboard states

This commit is contained in:
golem
2026-08-21 03:44:42 -06:00
parent 10507e769d
commit d1a86ad162
5 changed files with 82 additions and 9 deletions
+56 -4
View File
@@ -1,8 +1,8 @@
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { focusManager, onlineManager, QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { act, renderHook, waitFor } from "@testing-library/react";
import type { ReactNode } from "react";
import type { Address } from "viem";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import type { DashboardSnapshot, DeploymentManifest } from "../types/dashboard";
import { useBankDashboard } from "./useBankDashboard";
@@ -55,8 +55,7 @@ function deferred<T>() {
return { promise, resolve, reject };
}
function createWrapper() {
const client = new QueryClient({ defaultOptions: { queries: { retry: false, gcTime: 0 } } });
function createWrapper(client = new QueryClient({ defaultOptions: { queries: { retry: false, gcTime: 0 } } })) {
return function Wrapper({ children }: { children: ReactNode }) {
return <QueryClientProvider client={client}>{children}</QueryClientProvider>;
};
@@ -65,6 +64,13 @@ function createWrapper() {
describe("useBankDashboard", () => {
beforeEach(() => {
notifyBlock = undefined;
focusManager.setFocused(true);
onlineManager.setOnline(true);
});
afterEach(() => {
focusManager.setFocused(undefined);
onlineManager.setOnline(true);
});
it("starts in loading while the manifest and first reconciled snapshot are pending", () => {
@@ -151,6 +157,52 @@ describe("useBankDashboard", () => {
expect(loader).toHaveBeenCalledTimes(1);
});
it("does not retry a terminal manifest-fetch failure on focus, reconnect, or remount", async () => {
const loadManifest = vi.fn().mockRejectedValue(new Error("manifest request failed"));
const client = new QueryClient({ defaultOptions: { queries: { retry: false, gcTime: Number.POSITIVE_INFINITY } } });
const wrapper = createWrapper(client);
const options = { loadManifest, loader: vi.fn() };
const first = renderHook(() => useBankDashboard(options), { wrapper });
await waitFor(() => expect(first.result.current.status).toBe("invalid-manifest"));
await act(async () => {
focusManager.setFocused(false);
focusManager.setFocused(true);
onlineManager.setOnline(false);
onlineManager.setOnline(true);
await new Promise((resolve) => setTimeout(resolve, 0));
});
first.unmount();
const second = renderHook(() => useBankDashboard(options), { wrapper });
await waitFor(() => expect(second.result.current.status).toBe("invalid-manifest"));
expect(loadManifest).toHaveBeenCalledTimes(1);
});
it("does not retry a terminal chain mismatch on focus, reconnect, or remount", async () => {
const loader = vi.fn().mockRejectedValue(new Error(
"endpoint chain ID 84532 does not match manifest chain ID 31337",
));
const client = new QueryClient({ defaultOptions: { queries: { retry: false, gcTime: Number.POSITIVE_INFINITY } } });
const wrapper = createWrapper(client);
const options = { loadManifest: async () => manifestJson, loader };
const first = renderHook(() => useBankDashboard(options), { wrapper });
await waitFor(() => expect(first.result.current.status).toBe("chain-mismatch"));
await act(async () => {
focusManager.setFocused(false);
focusManager.setFocused(true);
onlineManager.setOnline(false);
onlineManager.setOnline(true);
await new Promise((resolve) => setTimeout(resolve, 0));
});
first.unmount();
const second = renderHook(() => useBankDashboard(options), { wrapper });
await waitFor(() => expect(second.result.current.status).toBe("chain-mismatch"));
expect(loader).toHaveBeenCalledTimes(1);
});
it("reconciles exactly once when a new watched block arrives", async () => {
const nextSnapshot = { ...snapshot, blockNumber: 13n, synchronizedAt: new Date("2026-08-21T10:01:00.000Z") };
const loader = vi.fn().mockResolvedValueOnce(snapshot).mockResolvedValueOnce(nextSnapshot);