import { renderHook, act } from "@testing-library/react-native"; import { useComponentScreenState } from "../useComponentScreenState"; import { useScreenContextV2 } from "@applicaster/zapp-react-native-utils/reactHooks/screen/useScreenContext"; import { create } from "zustand"; const createMockStore = () => create(() => ({})); // Mock the external dependencies jest.mock( "@applicaster/zapp-react-native-utils/reactHooks/screen/useScreenContext", () => ({ useScreenContextV2: jest.fn(), }) ); const mockUseScreenContextV2 = useScreenContextV2 as jest.MockedFunction< typeof useScreenContextV2 >; describe("useComponentScreenState", () => { beforeEach(() => { jest.clearAllMocks(); }); it("should return initial value when no state exists for componentId", () => { const mockStore = createMockStore(); mockUseScreenContextV2.mockReturnValue({ _componentStateStore: mockStore, } as any); const initialValue = "default-value"; const componentId = "test-component"; const { result } = renderHook(() => useComponentScreenState(componentId, initialValue) ); expect(result.current[0]).toBe(initialValue); }); it("should return stored value when state exists for componentId", () => { const mockStore = createMockStore(); mockStore.setState({ "existing-component": "stored-value" }); mockUseScreenContextV2.mockReturnValue({ _componentStateStore: mockStore, } as any); const { result } = renderHook(() => useComponentScreenState("existing-component", "default") ); expect(result.current[0]).toBe("stored-value"); }); it("should update state when setter is called", () => { const mockStore = createMockStore(); mockUseScreenContextV2.mockReturnValue({ _componentStateStore: mockStore, } as any); const { result } = renderHook(() => useComponentScreenState("update-test", "initial") ); // Verify initial value expect(result.current[0]).toBe("initial"); // Update the value act(() => { result.current[1]("updated-value"); }); // Verify updated value expect(result.current[0]).toBe("updated-value"); }); it("should handle different data types correctly", () => { const mockStore = createMockStore(); mockUseScreenContextV2.mockReturnValue({ _componentStateStore: mockStore, } as any); // Test with number const { result: numberResult } = renderHook(() => useComponentScreenState("number-test", 42) ); expect(numberResult.current[0]).toBe(42); // Test with boolean const { result: boolResult } = renderHook(() => useComponentScreenState("bool-test", true) ); expect(boolResult.current[0]).toBe(true); // Test with object const testObj = { key: "value" }; const { result: objResult } = renderHook(() => useComponentScreenState<{ key: string }>("obj-test", testObj) ); expect(objResult.current[0]).toEqual(testObj); }); it("should maintain separate states for different componentIds", () => { const mockStore = createMockStore(); mockUseScreenContextV2.mockReturnValue({ _componentStateStore: mockStore, } as any); const { result: firstResult } = renderHook(() => useComponentScreenState("first", "first-initial") ); const { result: secondResult } = renderHook(() => useComponentScreenState("second", "second-initial") ); // Verify initial values are separate expect(firstResult.current[0]).toBe("first-initial"); expect(secondResult.current[0]).toBe("second-initial"); // Update first component's state act(() => { firstResult.current[1]("first-updated"); }); // Verify only first component's state changed expect(firstResult.current[0]).toBe("first-updated"); expect(secondResult.current[0]).toBe("second-initial"); }); it("should return a memoized setter function", () => { const mockStore = createMockStore(); mockUseScreenContextV2.mockReturnValue({ _componentStateStore: mockStore, } as any); const { result, rerender } = renderHook( ({ id, initial }) => useComponentScreenState(id, initial), { initialProps: { id: "memo-test", initial: "initial" }, } ); const firstSetter = result.current[1]; // Rerender with same props rerender({ id: "memo-test", initial: "initial" }); const secondSetter = result.current[1]; // Setter should be the same instance due to useCallback expect(firstSetter).toBe(secondSetter); }); it("should update setter when componentId changes", () => { const mockStore = createMockStore(); mockUseScreenContextV2.mockReturnValue({ _componentStateStore: mockStore, } as any); const { result, rerender } = renderHook( ({ id, initial }) => useComponentScreenState(id, initial), { initialProps: { id: "old-id", initial: "initial" }, } ); const firstSetter = result.current[1]; // Rerender with different componentId rerender({ id: "new-id", initial: "initial" }); const secondSetter = result.current[1]; // Setter should be different because componentId changed expect(firstSetter).not.toBe(secondSetter); }); it("should call store.setState with correct parameters", () => { const mockStore = createMockStore(); const setStateSpy = jest.spyOn(mockStore, "setState"); mockUseScreenContextV2.mockReturnValue({ _componentStateStore: mockStore, } as any); const { result } = renderHook(() => useComponentScreenState("state-test", "initial") ); const newValue = "new-value"; act(() => { result.current[1](newValue); }); expect(setStateSpy).toHaveBeenCalledWith({ "state-test": newValue, }); }); it("should handle falsy initial values correctly", () => { const mockStore = createMockStore(); mockUseScreenContextV2.mockReturnValue({ _componentStateStore: mockStore, } as any); // Test with falsy values const { result: nullResult } = renderHook(() => useComponentScreenState("null-test", null) ); expect(nullResult.current[0]).toBeNull(); const { result: falseResult } = renderHook(() => useComponentScreenState("false-test", false) ); expect(falseResult.current[0]).toBe(false); const { result: zeroResult } = renderHook(() => useComponentScreenState("zero-test", 0) ); expect(zeroResult.current[0]).toBe(0); const { result: emptyStrResult } = renderHook(() => useComponentScreenState("empty-test", "") ); expect(emptyStrResult.current[0]).toBe(""); }); });