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);
});
});
});