software-mansion / react-native-reanimated

React Native's Animated library reimplemented
https://docs.swmansion.com/react-native-reanimated/
MIT License
8.83k stars 1.29k forks source link

JEST - reanimatedv2 TypeError: Cannot read properties of undefined (reading 'overshootClamping') when using Layout #3284

Open WillyRamirez opened 2 years ago

WillyRamirez commented 2 years ago

Description

Following the docs results in errors when applied to a custom component of mine. When I remove the layout prop from my <Animated.View> the test works fine.

Expected behavior

Jest is able to create a snapshot when using the layout prop on an Animated.View

Actual behavior & steps to reproduce

error when running yarn test

TypeError: Cannot read properties of undefined (reading 'overshootClamping')

  26 |       <Animated.View
  27 |         style={styles.appBarContainer}
> 28 |         layout={Layout.overshootClamping(25).duration(100)}
     |                        ^
  29 |       >
  30 |         <SomeComponent />`

Snack or minimal code example

MyScreen.tsx (simplified)

const MyScreen: FC<Props> = ({ route }) => {

  return (
    <View>
      <Animated.View
        layout={Layout.overshootClamping(25).duration(100)}
      >
        <SomeComponent />
      </Animated.View>
    </View>
  );
};

MyScreenTest.tsx

import React from 'react';
import { create, act } from 'react-test-renderer';
import { MyScreen } from '../MyScreen';

describe('ChargeScreen', () => {

  it('renders correctly', () => {

    // render the component
    let root;
    act(() => {
      root = create(<MyScreen />);
    });

    expect(root.toJSON()).toMatchSnapshot();

  });
});

setup-tests.ts

import 'react-native-gesture-handler/jestSetup';
import 'react-native';

require('react-native-reanimated/lib/reanimated2/jestUtils').setUpTests();

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

jest.config.js

module.exports = {
  preset: 'react-native',
  collectCoverageFrom: ['**/*.{ts,tsx}'],
  globalSetup: './global-setup.js',
  testPathIgnorePatterns: ['/node_modules/'],
  transformIgnorePatterns: ['node_modules/(?!victory-native)/'],
  reporters: ['default', 'jest-junit'],
  moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
  setupFilesAfterEnv: [
    '<rootDir>setup-tests.ts',
  ],
  moduleNameMapper: {
    '\\.svg': '<rootDir>/__mocks__/svgMock.ts',
  },
};

Package versions

name version
react-native 0.68.0
react-native-reanimated 2.8.0
NodeJS v17.8.0
Xcode
Java
Gradle
expo

Affected platforms

WillyRamirez commented 2 years ago

It seems Layout has not been mocked. Solved it by copying the contents of the file react-native-reanimated/mock

and adding the following code to the object called Reanimated

  Layout: {
    overshootClamping: () => {
      return Reanimated.Layout;
    },
    duration: () => {
      return Reanimated.Layout;
    },
    springify: () => {
      return Reanimated.Layout;
    },
  },
Norfeldt commented 1 year ago

I had a similar issue

  ● TextField › input field › can show a loading indicator

    TypeError: Cannot read properties of undefined (reading 'delay')

      39 |
      40 |     return (
    > 41 |       <Reanimated.default.View entering={Reanimated.FadeIn.delay(fadeInDelay)} style={[rotationStyle]}>

fixed it by patch-package by going into the mock and add FadeIn

node_modules/react-native-reanimated/src/reanimated2/mock.ts

/* eslint-disable node/no-callback-literal */
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-nocheck
const NOOP = () => {
  // noop
};
const ID = (t) => t;
const IMMEDIATE_CB_INVOCATION = (cb: () => unknown) => cb();

const ReanimatedV2 = {
  useSharedValue: (v) => ({ value: v }),
  useDerivedValue: (a) => ({ value: a() }),
  useAnimatedScrollHandler: () => NOOP,
  useAnimatedGestureHandler: () => NOOP,
  useAnimatedStyle: IMMEDIATE_CB_INVOCATION,
  useAnimatedRef: () => ({ current: null }),
  useAnimatedReaction: NOOP,
  useAnimatedProps: IMMEDIATE_CB_INVOCATION,

  withTiming: (toValue, _, cb) => {
    cb && cb(true);
    return toValue;
  },
  withSpring: (toValue, _, cb) => {
    cb && cb(true);
    return toValue;
  },
  withDecay: (_, cb) => {
    cb && cb(true);
    return 0;
  },
  withDelay: (_, animationValue) => {
    return animationValue;
  },
  withSequence: (..._animations) => {
    return 0;
  },
  withRepeat: (animation) => {
    return animation;
  },
  cancelAnimation: NOOP,
  measure: () => ({
    x: 0,
    y: 0,
    width: 0,
    height: 0,
    pageX: 0,
    pageY: 0,
  }),
  Easing: {
    linear: ID,
    ease: ID,
    quad: ID,
    cubic: ID,
    poly: ID,
    sin: ID,
    circle: ID,
    exp: ID,
    elastic: ID,
    back: ID,
    bounce: ID,
    bezier: () => ({ factory: ID }),
    bezierFn: ID,
    in: ID,
    out: ID,
    inOut: ID,
  },
  Extrapolation: {
    EXTEND: 'extend',
    CLAMP: 'clamp',
    IDENTITY: 'identity',
  },

  // Pathing reanimated2
  FadeIn: {
    default: NOOP,
    delay: NOOP,
  },

  runOnJS: (fn) => fn,
  runOnUI: (fn) => fn,
};

module.exports = {
  ...ReanimatedV2,
};

running

$ yarn patch-package react-native-reanimated

patches/react-native-reanimated+2.9.1.patch

diff --git a/node_modules/react-native-reanimated/src/reanimated2/mock.ts b/node_modules/react-native-reanimated/src/reanimated2/mock.ts
index 59f9119..3d5a0e8 100644
--- a/node_modules/react-native-reanimated/src/reanimated2/mock.ts
+++ b/node_modules/react-native-reanimated/src/reanimated2/mock.ts
@@ -71,6 +71,12 @@ const ReanimatedV2 = {
     IDENTITY: 'identity',
   },

+  // // Pathing reanimated2
+  FadeIn: {
+    default: NOOP,
+    delay: NOOP,
+  },
+
   runOnJS: (fn) => fn,
   runOnUI: (fn) => fn,
 };
github-actions[bot] commented 1 year ago

Hey! 👋

The issue doesn't seem to contain a minimal reproduction.

Could you provide a snack or a link to a GitHub repository under your username that reproduces the problem?

github-actions[bot] commented 1 year ago

Hey! 👋

It looks like you've omitted a few important sections from the issue template.

Please complete Steps to reproduce, Snack or a link to a repository, Reanimated version, React Native version and Platforms sections.