import React from "react"; import { render } from "@testing-library/react-native"; import { Text, View } from "react-native"; import { withAsyncRenderHOC } from "../withAsyncRender"; describe("withAsyncRenderHOC", () => { // Test component that requires onAsyncRender const TestComponent = ({ onAsyncRender, text, }: { onAsyncRender: () => void; text?: string; }) => { React.useEffect(() => { onAsyncRender(); }, [onAsyncRender]); return ( {text || "Test"} ); }; it("should wrap component and provide onAsyncRender callback", () => { const WrappedComponent = withAsyncRenderHOC(TestComponent); const wrapper = render(); expect(wrapper.toJSON()).not.toBeNull(); expect(wrapper.getByText("Hello")).toBeDefined(); }); it("should call emitAsyncElementRegistrate on mount", () => { const mockRegistrate = jest.fn(); const WrappedComponent = withAsyncRenderHOC(TestComponent); render(); expect(mockRegistrate).toHaveBeenCalledTimes(1); }); it("should call emitAsyncElementLayout when onAsyncRender is called", () => { const mockLayout = jest.fn(); const ComponentWithAsyncCall = ({ onAsyncRender, }: { onAsyncRender: () => void; }) => { return ( Async Component ); }; const WrappedComponent = withAsyncRenderHOC(ComponentWithAsyncCall); const wrapper = render( ); // Trigger onLayout const view = wrapper.UNSAFE_getByType(View); view.props.onLayout(); expect(mockLayout).toHaveBeenCalled(); }); it("should return unsubscribe function from emitAsyncElementRegistrate", () => { const mockUnsubscribe = jest.fn(); const mockRegistrate = jest.fn(() => mockUnsubscribe); const WrappedComponent = withAsyncRenderHOC(TestComponent); const { unmount } = render( ); expect(mockRegistrate).toHaveBeenCalledTimes(1); // Unmount should trigger cleanup unmount(); expect(mockUnsubscribe).toHaveBeenCalled(); }); it("should work without emitAsyncElementRegistrate prop", () => { const WrappedComponent = withAsyncRenderHOC(TestComponent); const wrapper = render(); expect(wrapper.toJSON()).not.toBeNull(); expect(wrapper.getByText("No Registration")).toBeDefined(); }); it("should work without emitAsyncElementLayout prop", () => { const ComponentWithLayout = ({ onAsyncRender, }: { onAsyncRender: () => void; }) => { return ( Test Layout ); }; const WrappedComponent = withAsyncRenderHOC(ComponentWithLayout); const wrapper = render(); // Should not throw when calling onLayout without emitAsyncElementLayout const view = wrapper.UNSAFE_getByType(View); expect(() => view.props.onLayout()).not.toThrow(); }); it("should pass through all other props to wrapped component", () => { const ComponentWithProps = ({ customProp, anotherProp, }: { onAsyncRender: () => void; customProp: string; anotherProp: number; }) => { return ( {customProp} {anotherProp} ); }; const WrappedComponent = withAsyncRenderHOC(ComponentWithProps); const wrapper = render( ); expect(wrapper.getByText("custom value")).toBeDefined(); expect(wrapper.getByText("42")).toBeDefined(); }); it("should call emitAsyncElementLayout multiple times if onAsyncRender is called multiple times", () => { const mockLayout = jest.fn(); const ComponentWithMultipleCalls = ({ onAsyncRender, }: { onAsyncRender: () => void; }) => { return ( First Second ); }; const WrappedComponent = withAsyncRenderHOC(ComponentWithMultipleCalls); const wrapper = render( ); // Trigger both onLayout callbacks const view1 = wrapper.getByTestId("view1"); const view2 = wrapper.getByTestId("view2"); view1.props.onLayout(); view2.props.onLayout(); expect(mockLayout).toHaveBeenCalledTimes(2); }); it("should handle components that use onAsyncRender in effects", () => { const mockLayout = jest.fn(); const ComponentWithEffect = ({ onAsyncRender, }: { onAsyncRender: () => void; }) => { React.useEffect(() => { // Simulate async operation completing const timer = setTimeout(() => { onAsyncRender(); }, 0); return () => clearTimeout(timer); }, [onAsyncRender]); return ( Effect Component ); }; const WrappedComponent = withAsyncRenderHOC(ComponentWithEffect); render(); // The effect should have been triggered return new Promise((resolve) => { setTimeout(() => { expect(mockLayout).toHaveBeenCalled(); resolve(true); }, 10); }); }); });