APSL / react-native-keyboard-aware-scroll-view

A ScrollView component that handles keyboard appearance and automatically scrolls to focused TextInput.
MIT License
5.27k stars 646 forks source link

TypeError: Cannot read property 'major' of undefined - when upgrading 0.9.3 → 0.9.4 - using Jest, react-native-testing-library #493

Open louiechristie opened 3 years ago

louiechristie commented 3 years ago

When running upgrading react-native-keyboard-aware-scroll-view ^0.9.3 → ^0.9.4

by using

npx npm-check-updates --doctor -u

I get Jest with react-native-testing-library testing error:

    TypeError: Cannot read property 'major' of undefined

      at Object.extractNativeRef (node_modules/react-native-keyboard-aware-scroll-view/lib/KeyboardAwareHOC.js:135:93)
      at _this._handleRef (node_modules/react-native-keyboard-aware-scroll-view/lib/KeyboardAwareHOC.js:490:52)
      at commitAttachRef (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:11992:7)
      at commitLayoutEffects (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:14448:9)
      at Object.invokeGuardedCallbackProd (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:11308:10)
      at invokeGuardedCallback (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:11499:31)
      at commitRootImpl (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:14172:9)
      at unstable_runWithPriority (node_modules/react-test-renderer/node_modules/scheduler/cjs/scheduler.development.js:468:12)
      at runWithPriority (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:2486:10)
      at commitRoot (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:14011:3)

Workaround

Use the built in KeyboardAvoidingView, and ScrollView from React Native

// TypeScript example 😬
import React from 'react';
import { KeyboardAvoidingView, ScrollView, KeyboardAvoidingViewProps } from 'react-native';

type Props = KeyboardAvoidingViewProps & { children: React.ReactNode };

const KeyboardAwareScrollView = (props: Props): JSX.Element => {
  const { children } = props;

  return (
    <KeyboardAvoidingView style={{ flex: 1 }} {...props}>
      <ScrollView contentContainerStyle={{ flexGrow: 1 }}>{children}</ScrollView>
    </KeyboardAvoidingView>
  );
};

export default KeyboardAwareScrollView;

Inspired by this post by pepperedpopcorn on Reddit

salomaoluiz commented 3 years ago

I resolve this problem adding before the describe this: jest.mock('react-native-keyboard-aware-scroll-view');, but with this change I don't have access the childrens of component

salomaoluiz commented 3 years ago

You can mock with access to childrens with this way: image

scottschulte-bh commented 3 years ago

Another workaround I found below is similar but it mocks it with KeyboardAwareScrollView

https://stackoverflow.com/questions/56333004/nativebase-content-not-rendered-in-jest-with-react-native-testing-library

@salomaoluiz, do you think there might be pros and cons to using KeyboardAwareScrollView instead of mockImplementation?

williamgurzoni commented 3 years ago

FYI: I was having problems with the KeyboardAwareFlatList.

My workaround:

import { FlatList } from 'react-native';
jest.mock('react-native-keyboard-aware-scroll-view', () => {
  const KeyboardAwareScrollView = ({ children }) => children;
  const KeyboardAwareFlatList = FlatList;
  return { KeyboardAwareScrollView, KeyboardAwareFlatList };
});
lucipacurar commented 2 years ago

I solved this a little different:

jest.mock('react-native/Libraries/Utilities/Platform', () => {
    const platform = jest.requireActual('react-native/Libraries/Utilities/Platform');
    return {
        ...platform,
        constants: {
            ...platform.constants,
            reactNativeVersion: {
                major: 0,
                minor: 65,
                patch: 1,
            }
        },
    };
});
lailton-b commented 2 years ago

I solved this a little different:

jest.mock('react-native/Libraries/Utilities/Platform', () => {
  const platform = jest.requireActual('react-native/Libraries/Utilities/Platform');
  return {
      ...platform,
      constants: {
          ...platform.constants,
          reactNativeVersion: {
              major: 0,
              minor: 65,
              patch: 1,
          }
      },
  };
});

It works for me, thanks a lot!! How did you get to this solution?

hasansna commented 2 years ago

I am using expo and to get RN version I adapted @lucipacurar code.

jest.mock('react-native/Libraries/Utilities/Platform', () => {
  const fs = require('fs')
  const json = JSON.parse(fs.readFileSync('node_modules/react-native/package.json', 'utf8'))
  const platform = jest.requireActual('react-native/Libraries/Utilities/Platform');
  const version = json.version.toString().split('.')
  return {
    ...platform,
    constants: {
      ...platform.constants,
      reactNativeVersion: {
        major: version[0],
        minor: version[1],
        patch: version[2],
      }
    },
  };
});
owjsub commented 2 years ago

Here's a mock that swaps out KeyboardAwareScrollView with a ScrollView to avoid massive diffs in your jest snapshots compared to using ({ children }) => children

jest.mock('react-native-keyboard-aware-scroll-view', () => {
  const KeyboardAwareScrollView = require('react-native').ScrollView;
  return { KeyboardAwareScrollView };
});
Snopkowski commented 2 years ago
jest.mock("react-native-keyboard-aware-scroll-view", () => {
  return {
    KeyboardAwareScrollView: (props: { children: React.ReactNode }) =>
      props.children,
  };
});

Solution provided by @salomaoluiz worked too 🙌

rarenatoe commented 1 year ago

I solved this a little different:

jest.mock('react-native/Libraries/Utilities/Platform', () => {
  const platform = jest.requireActual('react-native/Libraries/Utilities/Platform');
  return {
      ...platform,
      constants: {
          ...platform.constants,
          reactNativeVersion: {
              major: 0,
              minor: 65,
              patch: 1,
          }
      },
  };
});

This solution conflicts with

import mockSafeAreaContext from 'react-native-safe-area-context/jest/mock'
jest.mock('react-native-safe-area-context', () => mockSafeAreaContext)

Thus I strongly discourage it.