react-native-reanimated-carousel icon indicating copy to clipboard operation
react-native-reanimated-carousel copied to clipboard

Jest TypeError: (0 , _computedWithAutoFillData.computedOffsetXValueWithAutoFillData) is not a function

Open andresouza-maple opened this issue 1 year ago • 12 comments
trafficstars

Describe the bug

My test is broken on version 4.0.0-alpha.12 but it works correctly on version 3.5.1. It is throwing the following error when running it:

 FAIL  shared/component-library/Carousel/Carousel.test.tsx
  Carousel
    ✕ renders properly (55 ms)

  ● Carousel › renders properly

    TypeError: (0 , _computedWithAutoFillData.computedOffsetXValueWithAutoFillData) is not a function

      19 |     render(<Carousel data={data} renderItem={renderItem} width={200} />);
      20 |
    > 21 |     jest.advanceTimersByTime(2000);
         |          ^
      22 |
      23 |     screen.debug();

My test:

import React from 'react';
import { Text } from 'react-native';
import Carousel, { CarouselRenderItem } from 'react-native-reanimated-carousel';
import { render, screen } from '@testing-library/react-native';

const data = ['Slide 1', 'Slide 2', 'Slide 3'];

const renderItem: CarouselRenderItem<string> = ({ item }) => (
  <Text>{item}</Text>
);

describe('Carousel', () => {
  beforeEach(() => {
    jest.useFakeTimers();
    jest.clearAllMocks();
  });

  it('renders properly', async () => {
    render(<Carousel data={data} renderItem={renderItem} width={200} />);

    jest.advanceTimersByTime(2000);

    screen.debug();

    const card1 = await screen.findByText(data[0]);

    expect(card1).toBeDefined();
  });
});

To Reproduce Steps to reproduce the behavior:

  1. Install the version 4.0.0-alpha.12;
  2. Run the test above;
  3. See error

Expected behavior The carousel should render the slides in the DOM and the test should pass. What I expect to see when I run screen.debug():

    <View
      style={
        {
          "flex": 1,
        }
      }
    >
      <View
        collapsable={false}
        forwardedRef={[Function anonymous]}
        handlerTag={1}
        handlerType="PanGestureHandler"
        jestAnimatedStyle={
          {
            "value": {},
          }
        }
        onGestureHandlerEvent={[Function anonymous]}
        onTouchEnd={[Function anonymous]}
        onTouchStart={[Function anonymous]}
        style={
          {
            "flexDirection": "row",
            "height": "100%",
            "overflow": "hidden",
            "width": 200,
          }
        }
      >
        <View
          collapsable={false}
          jestAnimatedStyle={
            {
              "value": {
                "transform": [
                  {
                    "translateX": 0,
                  },
                ],
              },
            }
          }
          style={
            {
              "height": "100%",
              "position": "absolute",
              "transform": [
                {
                  "translateX": 0,
                },
              ],
              "width": 200,
            }
          }
          testID="__CAROUSEL_ITEM_0_READY__"
        >
          <Text>
            Slide 1
          </Text>
        </View>
        <View
          collapsable={false}
          jestAnimatedStyle={
            {
              "value": {
                "transform": [
                  {
                    "translateX": 200,
                  },
                ],
              },
            }
          }
          style={
            {
              "height": "100%",
              "position": "absolute",
              "transform": [
                {
                  "translateX": 200,
                },
              ],
              "width": 200,
            }
          }
          testID="__CAROUSEL_ITEM_1_READY__"
        >
          <Text>
            Slide 2
          </Text>
        </View>
        <View
          collapsable={false}
          jestAnimatedStyle={
            {
              "value": {
                "transform": [
                  {
                    "translateX": -200,
                  },
                ],
              },
            }
          }
          style={
            {
              "height": "100%",
              "position": "absolute",
              "transform": [
                {
                  "translateX": -200,
                },
              ],
              "width": 200,
            }
          }
          testID="__CAROUSEL_ITEM_2_READY__"
        >
          <Text>
            Slide 3
          </Text>
        </View>
      </View>
    </View>

Versions:

  • react: v18.2.0
  • react-native: v0.71.14
  • react-native-reanimated: v3.14.0
  • react-native-reanimated-carousel: v4.0.0-alpha.12
  • react-native-gesture-handler: v2.16.0

Smartphone (please complete the following information):

  • Device: [e.g. iPhone6] N/A
  • OS: [e.g. iOS8.1] N/A
  • Browser [e.g. stock browser, safari] N/A
  • Version [e.g. 22] N/A
  • iTerm2: Build 3.5.3

Additional context I know version 4.0.0 is still in Alpha, but I thought you wanted to know about this issue before the release.

andresouza-maple avatar Jul 25 '24 16:07 andresouza-maple

Hey @andresouza-maple! I'm here to help you with any bugs, questions, or contributions you have for the repository. Let me know how I can assist you!

The issue you're encountering with the test breaking on version 4.0.0-alpha.12 of react-native-reanimated-carousel but working correctly on version 3.5.1 is likely due to changes introduced in the newer version. Specifically, in 4.0.0-alpha.12, there was a fix for an issue where endWithSpring used outdated data from useSharedValue after onGestureEnd, causing incorrect carousel behavior on direction reversal [1].

To resolve the error TypeError: (0 , _computedWithAutoFillData.computedOffsetXValueWithAutoFillData) is not a function, ensure that the function computedOffsetXValueWithAutoFillData is properly defined and exported before it is used. Here is the relevant part of the code:

export function computedOffsetXValueWithAutoFillData(
  params: BaseParams<{
    rawDataLength: number
    value: number
    size: number
  }>,
) {
  "worklet";

  const { rawDataLength, value, size, loop, autoFillData } = params;

  if (isAutoFillData({ loop, autoFillData })) {
    switch (rawDataLength) {
      case SINGLE_ITEM:
        return value % size;
      case DOUBLE_ITEM:
        return value % (size * 2);
    }
  }

  return value;
}

Ensure that this function is not being referenced before its definition in your code. If the error persists, it might be due to a circular dependency or an import issue. Double-check your import statements and the order of your function definitions to ensure there are no circular dependencies [2].

Additionally, make sure you have the necessary Babel plugin configuration in your babel.config.js:

module.exports = {
  presets: [
    ...
  ],
  plugins: [
    ...
    'react-native-reanimated/plugin',
  ],
};

After editing your Babel plugin, try running npm start -- --reset-cache or yarn start --reset-cache [3].

If these steps do not resolve the issue, you may need to review and possibly update your test logic to ensure it aligns with the new behavior of the carousel component in version 4.0.0-alpha.12 [1].

To continue talking to Dosu, mention @dosu.

dosubot[bot] avatar Jul 25 '24 16:07 dosubot[bot]

Answering the bot above, I don't think the changes in the version 4.0.0-alpha.12 are causing it because I had the same issue with the version 4.0.0-alpha.0. I wonder if it is related to the new gesture handler API implemented in the v4.0.0-alpha.0

andresouza-maple avatar Jul 25 '24 21:07 andresouza-maple

The issue you're encountering is likely related to the new gesture handler API introduced in version 4.0.0-alpha.0 of react-native-reanimated-carousel. This version requires react-native-gesture-handler to be >=2.9.0 and replaces the usage of useAnimatedGestureHandler with the new gesture handler API [1].

To address this, ensure that all your dependencies are compatible with the new version of the carousel. Specifically, verify that react-native-gesture-handler is updated to >=2.9.0. Additionally, you might need to adjust your test setup to accommodate the changes in the internal implementation of the carousel component.

Here is an updated version of your test code that includes a check for the new gesture handler API:

import React from 'react';
import { Text } from 'react-native';
import Carousel, { CarouselRenderItem } from 'react-native-reanimated-carousel';
import { render, screen } from '@testing-library/react-native';

const data = ['Slide 1', 'Slide 2', 'Slide 3'];

const renderItem: CarouselRenderItem<string> = ({ item }) => (
  <Text>{item}</Text>
);

describe('Carousel', () => {
  beforeEach(() => {
    jest.useFakeTimers();
    jest.clearAllMocks();
  });

  it('renders properly', async () => {
    render(<Carousel data={data} renderItem={renderItem} width={200} />);

    jest.advanceTimersByTime(2000);

    screen.debug();

    const card1 = await screen.findByText(data[0]);

    expect(card1).toBeDefined();
  });
});

If the issue persists, you might need to investigate further into the specific changes introduced in the new version of the carousel and adjust your test setup accordingly.

To continue talking to Dosu, mention @dosu.

dosubot[bot] avatar Jul 25 '24 21:07 dosubot[bot]

Any news on this issue ? I have the same here

jiroscripts avatar Aug 14 '24 13:08 jiroscripts

I was able to fix it by adding this in my jest.setup.ts:

jest.mock('react-native-reanimated', () =>
  require('react-native-reanimated/mock'),
);

steviec avatar Aug 14 '24 15:08 steviec

I have the same issue without Jest. Did I miss something ?

jiroscripts avatar Aug 14 '24 16:08 jiroscripts

I was able to fix it by adding this in my jest.setup.ts:

jest.mock('react-native-reanimated', () =>
  require('react-native-reanimated/mock'),
);

I tried this, and the error is gone, but I still don't get the Expected Behaviour. When I call screen.debug(), I don't see the slides in the DOM, just an empty View that I guess is the Carousel container:

    <View
      style={
        {
          "flex": 1,
        }
      }
    >
      <View
        collapsable={false}
        onTouchEnd={[Function anonymous]}
        onTouchStart={[Function anonymous]}
        style={
          [
            {
              "overflow": "hidden",
            },
            {
              "height": "100%",
              "width": 200,
            },
            {},
            {
              "flexDirection": "row",
            },
          ]
        }
      />
    </View>

andresouza-maple avatar Aug 20 '24 20:08 andresouza-maple

@andresouza-maple You're right. My fix just makes the error go away, but it doesn't make the carousel component behave correctly.

steviec avatar Aug 26 '24 15:08 steviec

I finally resolved the bug on my side with NX 19 and RN 0.73.8

I removed some lines in metro.config.js

  resolver: {
       ...
  -      resolverMainFields: ['sbmodern', 'browser', 'main'],
  -      blockList: exclusionList([/^(?!.*node_modules).*\/dist\/.*/]),
  }

I added some plugns in babel.config.js

    plugins: [
        '@babel/plugin-transform-flow-strip-types',
        '@babel/plugin-transform-class-properties',
        '@babel/plugin-transform-private-methods',
        '@babel/plugin-transform-private-property-in-object',
        'react-native-reanimated/plugin',
    ],

My package.json for plugins versions:

        "@babel/plugin-transform-class-properties": "^7.21.0",
        "@babel/plugin-transform-flow-strip-types": "^7.25.2",
        "@babel/plugin-transform-private-methods": "^7.21.0",
        "@babel/plugin-transform-private-property-in-object": "^7.21.0",

Hope it helps :)

jiroscripts avatar Aug 30 '24 07:08 jiroscripts

Hey, i use this mock. It doesn't reproduce complex behavior, but it renders all items correctly. You just have to create a react-native-reanimated-carousel.tsx file in the __mocks__ folder. Hope this helps.

import React, { forwardRef, useEffect, useImperativeHandle } from 'react'
import { FlatList, ListRenderItem } from 'react-native'
import { TCarouselProps } from 'react-native-reanimated-carousel'

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export default forwardRef<
  any,
  Omit<TCarouselProps, 'onProgressChange'> & {
    onProgressChange: (offsetProgress: number, absoluteProgress: number) => void
  }
>(function Carousel(
  { renderItem, data, testID, width, height, onProgressChange, defaultIndex = 0 },
  ref
) {
  useImperativeHandle(ref, () => ({
    next: jest.fn(),
    prev: jest.fn(),
    scrollTo: ({ index }: { index: number }) => {
      onProgressChange(0, index)
    },
    getCurrentIndex: jest.fn(),
  }))

  useEffect(() => {
    if (defaultIndex > 0) {
      onProgressChange(0, defaultIndex)
    }
  }, [defaultIndex, onProgressChange])

  return (
    <FlatList
      ref={ref}
      testID={testID}
      renderItem={renderItem as unknown as ListRenderItem<unknown>}
      data={data}
      keyExtractor={(_, index) => index.toString()}
      style={{ width, height }}
    />
  )
})

mmeissonnier avatar Sep 11 '24 01:09 mmeissonnier

@mmeissonnier thank you, your mock worked for me.

@dohooo is there any chance you can put mock into library, which can be maintained?

professorkolik avatar Oct 01 '24 19:10 professorkolik

@mmeissonnier the mock worked for me as well. Thanks! I guess this will do the work for now, until we have this fixed in the v4.0.0

andresouza-maple avatar Oct 03 '24 19:10 andresouza-maple

@mmeissonnier Legend !! thank you

woo-industries avatar Nov 20 '24 16:11 woo-industries

Hi, @andresouza-maple. I'm Dosu, and I'm helping the react-native-reanimated-carousel team manage their backlog. I'm marking this issue as stale.

Issue Summary:

  • You reported a TypeError in Jest tests on version 4.0.0-alpha.12 of the library, which was not present in 3.5.1.
  • The error was related to the function computedOffsetXValueWithAutoFillData.
  • The issue was suspected to be linked to the new gesture handler API.
  • Workarounds were suggested by other users, but the carousel did not behave correctly.
  • The issue was resolved using a mock shared by @mmeissonnier, confirmed to work by you and others.

Next Steps:

  • Please confirm if this issue is still relevant to the latest version of the react-native-reanimated-carousel. If it is, feel free to comment to keep the discussion open.
  • If there are no further updates, this issue will be automatically closed in 7 days.

Thank you for your understanding and contribution!

dosubot[bot] avatar Feb 20 '25 16:02 dosubot[bot]