Ctrl K
ComunidadGuías…

jest/setupMockLegendList.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import type * as LegendListModule from '@legendapp/list/react-native';
import type React from 'react';
import type {ScrollViewProps, View as ReactNativeView} from 'react-native';

export default function setupMockLegendList() {
    jest.mock('@legendapp/list/react-native', () => {
        const ReactActual = jest.requireActual<typeof React>('react');
        const {ScrollView, View} = jest.requireActual<{ScrollView: React.ComponentType<ScrollViewProps>; View: typeof ReactNativeView}>('react-native');
        const LegendListActual = jest.requireActual<typeof LegendListModule>('@legendapp/list/react-native');

        type MockLegendListProps = LegendListModule.LegendListProps<unknown>;

        /**
         * LegendList relies on native layout measurements that Jest does not produce. Render every item in a
         * ScrollView so tests can exercise list content and callbacks without depending on another virtualized list.
         */
        const MockLegendList = ReactActual.forwardRef<unknown, MockLegendListProps>(
            (
                {
                    children,
                    data,
                    extraData,
                    getItemType,
                    ItemSeparatorComponent,
                    keyExtractor,
                    ListEmptyComponent,
                    ListFooterComponent,
                    ListFooterComponentStyle,
                    ListHeaderComponent,
                    ListHeaderComponentStyle,
                    maintainVisibleContentPosition: _maintainVisibleContentPosition,
                    onEndReached,
                    onEndReachedThreshold = 0.5,
                    onLoad,
                    onScroll,
                    onStartReached,
                    onStartReachedThreshold = 0.5,
                    recycleItems: _recycleItems,
                    renderItem,
                    ...scrollViewProps
                },
                ref,
            ) => {
                const onLoadRef = ReactActual.useRef(onLoad);
                const listMetricsRef = ReactActual.useRef<{contentLength: number; scroll: number; scrollLength: number} | undefined>(undefined);
                const reachedEdgesRef = ReactActual.useRef({end: false, start: false});
                onLoadRef.current = onLoad;

                ReactActual.useEffect(() => {
                    onLoadRef.current?.({elapsedTimeInMs: 0});
                }, []);

                ReactActual.useImperativeHandle(
                    ref,
                    () => ({
                        clearCaches: jest.fn(),
                        flashScrollIndicators: jest.fn(),
                        getAnimatableRef: () => null,
                        getNativeScrollRef: () => null,
                        getScrollableNode: () => null,
                        getScrollResponder: () => null,
                        getState: () => ({
                            data: data ?? [],
                            elementAtIndex: () => undefined,
                            endBuffered: (data?.length ?? 0) - 1,
                            startBuffered: 0,
                            ...listMetricsRef.current,
                        }),
                        reportContentInset: jest.fn(),
                        scrollIndexIntoView: jest.fn(() => Promise.resolve()),
                        scrollItemIntoView: jest.fn(() => Promise.resolve()),
                        scrollToEnd: jest.fn(() => Promise.resolve()),
                        scrollToIndex: jest.fn(() => Promise.resolve()),
                        scrollToItem: jest.fn(() => Promise.resolve()),
                        scrollToOffset: jest.fn(() => Promise.resolve()),
                        setItemSize: jest.fn(),
                        setScrollProcessingEnabled: jest.fn(),
                        setVisibleContentAnchorOffset: jest.fn(),
                    }),
                    [data],
                );

                const handleScroll: NonNullable<ScrollViewProps['onScroll']> = (event) => {
                    const {contentOffset, contentSize, layoutMeasurement} = event.nativeEvent;
                    const isHorizontal = scrollViewProps.horizontal === true;
                    const offset = isHorizontal ? contentOffset.x : contentOffset.y;
                    const contentLength = isHorizontal ? contentSize.width : contentSize.height;
                    const visibleLength = isHorizontal ? layoutMeasurement.width : layoutMeasurement.height;
                    const distanceFromEnd = contentLength - visibleLength - offset;
                    const isWithinEndThreshold = distanceFromEnd <= visibleLength * (onEndReachedThreshold ?? 0.5);
                    const isWithinStartThreshold = offset <= visibleLength * (onStartReachedThreshold ?? 0.5);
                    listMetricsRef.current = {
                        contentLength,
                        scroll: offset,
                        scrollLength: visibleLength,
                    };
                    onScroll?.(event);

                    if (!isWithinEndThreshold) {
                        reachedEdgesRef.current.end = false;
                    } else if (!reachedEdgesRef.current.end) {
                        reachedEdgesRef.current.end = true;
                        onEndReached?.({distanceFromEnd});
                    }
                    if (!isWithinStartThreshold) {
                        reachedEdgesRef.current.start = false;
                    } else if (!reachedEdgesRef.current.start) {
                        reachedEdgesRef.current.start = true;
                        onStartReached?.({distanceFromStart: offset});
                    }
                };

                const renderedItems = data?.flatMap((item, index) => {
                    if (!renderItem) {
                        return [];
                    }

                    const itemKey = keyExtractor?.(item, index) ?? String(index);
                    const safeExtraData: unknown = extraData;
                    const itemElement = ReactActual.createElement(View, {key: itemKey}, renderItem({data, extraData: safeExtraData, index, item, type: getItemType?.(item, index)}));
                    if (!ItemSeparatorComponent || index === data.length - 1) {
                        return [itemElement];
                    }

                    return [itemElement, ReactActual.createElement(ItemSeparatorComponent, {key: `${itemKey}-separator`, leadingItem: item})];
                });
                const header = ReactActual.isValidElement(ListHeaderComponent) ? ListHeaderComponent : ListHeaderComponent && ReactActual.createElement(ListHeaderComponent);
                const footer = ReactActual.isValidElement(ListFooterComponent) ? ListFooterComponent : ListFooterComponent && ReactActual.createElement(ListFooterComponent);
                const empty = ReactActual.isValidElement(ListEmptyComponent) ? ListEmptyComponent : ListEmptyComponent && ReactActual.createElement(ListEmptyComponent);
                let content = children;
                if (data) {
                    content = data.length > 0 ? renderedItems : empty;
                }

                return ReactActual.createElement(
                    ScrollView,
                    {...scrollViewProps, onScroll: handleScroll},
                    header && ReactActual.createElement(View, {style: ListHeaderComponentStyle}, header),
                    content,
                    footer && ReactActual.createElement(View, {style: ListFooterComponentStyle}, footer),
                );
            },
        );
        const LegendList = jest.fn((props: MockLegendListProps & {ref?: React.Ref<unknown>}) => ReactActual.createElement(MockLegendList, props));

        const useRecyclingState = <T>(valueOrInitializer: T | (() => T)) => ReactActual.useState(valueOrInitializer);

        return {
            ...LegendListActual,
            LegendList,
            useAdaptiveRender: () => 'normal',
            useAdaptiveRenderChange: jest.fn(),
            useIsLastItem: () => false,
            useListScrollSize: () => ({height: 0, width: 0}),
            useRecyclingEffect: jest.fn(),
            useRecyclingState,
            useSyncLayout: () => jest.fn(),
            useViewability: jest.fn(),
            useViewabilityAmount: jest.fn(),
        };
    });

    jest.mock('@legendapp/list/reanimated', () => ({
        AnimatedLegendList: jest.requireMock<typeof LegendListModule>('@legendapp/list/react-native').LegendList,
    }));
}