import { act } from "@testing-library/react"; import { Socket } from "@hyper-fetch/sockets"; import { createWebsocketMockingServer } from "@hyper-fetch/testing"; import { renderUseSocketState } from "../../../utils/use-socket-state.utils"; describe("useSocketState [ Base ]", () => { const spy = vi.fn(); const { url, startServer, stopServer } = createWebsocketMockingServer(); const socket = new Socket({ url }); let view = renderUseSocketState(socket); beforeEach(async () => { view = renderUseSocketState(socket); vi.resetModules(); vi.resetAllMocks(); startServer(); }); afterEach(() => { stopServer(); }); describe("when using actions", () => { it("should set state with data", async () => { const value = 1; const [state, actions] = view.result.current; act(() => { actions.setData(value); }); expect(state.data).toBe(value); }); it("should set state with timestamp", async () => { const value = 1; const [state, actions] = view.result.current; act(() => { actions.setTimestamp(value); }); expect(state.timestamp).toBe(value); }); it("should set state with connected value", async () => { const value = true; const [state, actions] = view.result.current; act(() => { actions.setConnected(value); }); expect(state.connected).toBe(value); }); it("should set state with connecting value", async () => { const value = true; const [state, actions] = view.result.current; act(() => { actions.setConnecting(value); }); expect(state.connecting).toBe(value); }); }); describe("when using callbacks", () => { it("should call onConnected callback", async () => { const [, , callbacks] = view.result.current; act(() => { socket.events.emitConnected(); callbacks.onConnected(spy); socket.events.emitConnected(); }); expect(spy).toHaveBeenCalledTimes(1); }); it("should call onDisconnected callback", async () => { const [, , callbacks] = view.result.current; act(() => { socket.events.emitDisconnected(); callbacks.onDisconnected(spy); socket.events.emitDisconnected(); }); expect(spy).toHaveBeenCalledTimes(1); }); it("should call onError callback", async () => { const value = { error: new Error("Test error"), }; const [, , callbacks] = view.result.current; act(() => { socket.events.emitError(value); callbacks.onError(spy); socket.events.emitError(value); }); expect(spy).toHaveBeenCalledTimes(1); expect(spy).toHaveBeenCalledWith(value); }); it("should call onConnecting callback", async () => { const [, , callbacks] = view.result.current; act(() => { socket.events.emitConnecting({ connecting: true }); callbacks.onConnecting(spy); socket.events.emitConnecting({ connecting: true }); }); expect(spy).toHaveBeenCalledTimes(1); }); it("should call onReconnecting callback", async () => { const value = 2; const [, , callbacks] = view.result.current; act(() => { socket.events.emitReconnecting({ attempts: value }); callbacks.onReconnecting(spy); socket.events.emitReconnecting({ attempts: value }); }); expect(spy).toHaveBeenCalledTimes(1); expect(spy).toHaveBeenCalledWith({ attempts: value }); }); it("should call onReconnectingFailed callback", async () => { const value = 2; const [, , callbacks] = view.result.current; act(() => { socket.events.emitReconnectingFailed({ attempts: value }); callbacks.onReconnectingFailed(spy); socket.events.emitReconnectingFailed({ attempts: value }); }); expect(spy).toHaveBeenCalledTimes(1); expect(spy).toHaveBeenCalledWith({ attempts: value }); }); }); describe("when using dependency tracking", () => { it("should not trigger re-render when key is not tracked", async () => { const renderSpy = vi.fn(); view = renderUseSocketState(socket, { onRender: renderSpy }); const [, actions] = view.result.current; renderSpy.mockClear(); // Clear initial render count act(() => { actions.setData(123); }); expect(renderSpy).not.toHaveBeenCalled(); }); it("should trigger re-render when key is tracked", async () => { const renderSpy = vi.fn(); view = renderUseSocketState(socket, { onRender: renderSpy }); const [, actions, , { setRenderKey }] = view.result.current; act(() => { setRenderKey("data"); }); renderSpy.mockClear(); // Clear initial render count act(() => { actions.setData(123); }); expect(renderSpy).toHaveBeenCalledTimes(1); }); it("should not add duplicate render keys", async () => { const renderSpy = vi.fn(); view = renderUseSocketState(socket, { onRender: renderSpy }); const [, , , { setRenderKey }] = view.result.current; act(() => { setRenderKey("data"); setRenderKey("data"); // Adding same key twice }); renderSpy.mockClear(); // Clear initial render count act(() => { view.result.current[1].setData(123); }); expect(renderSpy).toHaveBeenCalledTimes(1); }); }); describe("when using clearState", () => { it("should reset all state to initial values", async () => { view = renderUseSocketState(socket); const [, actions, , { setRenderKey }] = view.result.current; act(() => { setRenderKey("data"); setRenderKey("extra"); setRenderKey("connected"); setRenderKey("connecting"); setRenderKey("timestamp"); }); act(() => { actions.setData("some-data"); actions.setExtra({ test: true } as any); actions.setConnected(true); actions.setConnecting(true); actions.setTimestamp(12345); }); expect(view.result.current[0].data).toBe("some-data"); expect(view.result.current[0].connected).toBe(true); expect(view.result.current[0].connecting).toBe(true); expect(view.result.current[0].timestamp).toBe(12345); act(() => { view.result.current[1].clearState(); }); expect(view.result.current[0].data).toBe(null); expect(view.result.current[0].extra).toBe(null); expect(view.result.current[0].connected).toBe(false); expect(view.result.current[0].connecting).toBe(false); expect(view.result.current[0].timestamp).toBe(null); }); }); });