Files
nanobot/webui/src/tests/useSidebarState.test.tsx
T

70 lines
2.5 KiB
TypeScript

import { act, renderHook, waitFor } from "@testing-library/react";
import type { ReactNode } from "react";
import { describe, expect, it, vi } from "vitest";
import { useSidebarState } from "@/hooks/useSidebarState";
import type { NanobotClient } from "@/lib/nanobot-client";
import type { SidebarStatePayload } from "@/lib/types";
import { ClientProvider } from "@/providers/ClientProvider";
describe("useSidebarState", () => {
it("serializes full-state writes so an older request cannot overwrite a newer update", async () => {
let resolveFirstWrite: (() => void) | null = null;
let sidebarStateUpdateHandler: ((state: SidebarStatePayload) => void) | null = null;
const setSidebarState = vi.fn()
.mockImplementationOnce((state: SidebarStatePayload) => new Promise<SidebarStatePayload>(
(resolve) => {
resolveFirstWrite = () => resolve(state);
},
))
.mockImplementation(async (state: SidebarStatePayload) => state);
const client = {
status: "open" as const,
onStatus: () => () => {},
onSidebarStateUpdate: (handler: (state: SidebarStatePayload) => void) => {
sidebarStateUpdateHandler = handler;
return () => {
sidebarStateUpdateHandler = null;
};
},
setSidebarState,
} as unknown as NanobotClient;
vi.stubGlobal("fetch", vi.fn().mockResolvedValue({
ok: true,
status: 200,
json: async () => ({}),
}));
const wrapper = ({ children }: { children: ReactNode }) => (
<ClientProvider client={client} token="token">
{children}
</ClientProvider>
);
const { result } = renderHook(() => useSidebarState([], false), { wrapper });
await waitFor(() => expect(result.current.loading).toBe(false));
act(() => {
void result.current.update((current) => ({
...current,
title_overrides: { "websocket:a": "First" },
}));
void result.current.update((current) => ({
...current,
title_overrides: { "websocket:a": "Second" },
}));
});
expect(setSidebarState).toHaveBeenCalledTimes(1);
act(() => {
sidebarStateUpdateHandler?.(setSidebarState.mock.calls[0]?.[0]);
});
expect(result.current.state.title_overrides).toEqual({
"websocket:a": "Second",
});
act(() => resolveFirstWrite?.());
await waitFor(() => expect(setSidebarState).toHaveBeenCalledTimes(2));
expect(setSidebarState.mock.calls[1]?.[0]).toEqual(expect.objectContaining({
title_overrides: { "websocket:a": "Second" },
}));
});
});