react-native: Jest transpiling from "jest/preprocessor.js" broken

Environment

Run react-native info in your terminal and paste its contents here.

React Native Environment Info:
    System:
      OS: macOS High Sierra 10.13.6
      CPU: x64 Intel(R) Core(TM) i7-4770HQ CPU @ 2.20GHz
      Memory: 178.39 MB / 16.00 GB
      Shell: 3.2.57 - /bin/bash
    Binaries:
      Node: 10.9.0 - ~/.nvm/versions/node/v10.9.0/bin/node
      npm: 6.2.0 - ~/.nvm/versions/node/v10.9.0/bin/npm
      Watchman: 4.7.0 - /usr/local/bin/watchman
    SDKs:
      iOS SDK:
        Platforms: iOS 12.0, macOS 10.14, tvOS 12.0, watchOS 5.0
      Android SDK:
        Build Tools: 23.0.1, 25.0.0, 25.0.1, 25.0.2, 25.0.3, 26.0.1, 26.0.2, 27.0.3, 28.0.1, 28.0.3
        API Levels: 19, 23, 25, 26, 27, 28
    IDEs:
      Android Studio: 3.1 AI-173.4907809
      Xcode: 10.0/10A255 - /usr/bin/xcodebuild
    npmPackages:
      react: ^16.6.0-alpha.8af6728 => 16.6.0-alpha.8af6728
      react-native: ^0.57.4 => 0.57.4
    npmGlobalPackages:
      create-react-native-app: 1.0.0
      react-native-cli: 2.0.1

Description

We have recently upgraded from v0.55.4 to v0.57.4 using react-native-git-upgrade. As part of this upgrade the majority of our jest tests have broken due to transpiling errors.

Full jest config

  "jest": {
    "preset": "react-native",
    "setupTestFrameworkScriptFile": "<rootDir>/jestSetup.js",
    "moduleNameMapper": {
      "^image![a-zA-Z0-9$_-]+$": "GlobalImageStub",
      "^[@./a-zA-Z0-9$_-]+\\.(png|gif|jpg|ttf)$": "RelativeImageStub"
    },
    "transform": {
      "^.+\\.js$": "<rootDir>/node_modules/react-native/jest/preprocessor.js"
    },
    "testMatch": [
      "<rootDir>/src/**/*.test.js?(x)"
    ],
    "snapshotSerializers": [
      "enzyme-to-json/serializer"
    ],
    "collectCoverageFrom": [
      "**/src/**/*.js"
    ],
    "coveragePathIgnorePatterns": [
      "src/helpers/networkDebugger",
      "/node_modules/"
    ],
    "coverageThreshold": {
      "global": {
        "branches": 90,
        "functions": 90,
        "lines": 90,
        "statements": 90
      }
    }
  },

Jest setup file

import { configure } from 'enzyme'
import Adapter from 'enzyme-adapter-react-16'
import fetch from 'jest-fetch-mock'

global.fetch = fetch

const mockedDate = new Date('2018-01-01T12:13:14')
const _Date = Date
global.Date = jest.fn(() => mockedDate)
global.Date.UTC = _Date.UTC
global.Date.parse = _Date.parse
global.Date.now = _Date.now
global.WebSocket = jest.fn(() => ({ addEventListener: jest.fn(), send: jest.fn() }))
global.FileReader = jest.fn(() => ({ addEventListener: jest.fn(), readAsText: jest.fn() }))
global.requestAnimationFrame = jest.fn()

configure({ adapter: new Adapter() })

Some of the errors we are seeing are related to imports/exports and class arrow methods. e.g.

export class MyComponent extends Component {
  static propType = { optionalProp: PropTypes.bool },
  doSomething = () => { ... }
  render () { ... }
}

Fails with the error TypeError: Cannot read property 'default' of undefined. Once changed to

export class MyComponent extends Component {
  static propType = { optionalProp: PropTypes.bool },
  doSomething () { ... }
  render () { ... }
}

It is fine.

Reproducible Demo

Let us know how to reproduce the issue. Include a code sample, share a project, or share an app that reproduces the issue using https://snack.expo.io/. Please follow the guidelines for providing a MCVE: https://stackoverflow.com/help/mcve

I’ve setup a public repo which contains some example broken tests and how those tests can be fixed temporarily. However the underlying issue is the transpiling. https://github.com/lewnelson/react-native-jest-example

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 16
  • Comments: 35 (10 by maintainers)

Commits related to this issue

Most upvoted comments

We have also ran into this issue on 0.57.3. From an initial investigation I can make it work by disabling inlineRequires in the jest preprocessor https://github.com/facebook/react-native/blob/master/jest/preprocessor.js#L58

This is as far as I got. To get around the issue for now we are binding the methods in the constructor.

transform: {
  '^.+\\.js$': '<rootDir>/jest/preprocessor.js',
},
/**
 * Your own [temporary?] transform for React Native
 */
const generate = require('@babel/generator').default
const transformer = require('metro-react-native-babel-transformer')
const createCacheKeyFunction = require('fbjs-scripts/jest/createCacheKeyFunction')
const metroBabelRegister = require('metro-babel-register')

metroBabelRegister([])

module.exports = {
  process(src, file) {
    const { ast } = transformer.transform({
      filename: file,
      options: {
        ast: true,
        dev: true,
        enableBabelRuntime: false,
        experimentalImportSupport: false,
        hot: false,
        inlineRequires: false,
        minify: false,
        platform: '',
        projectRoot: '',
        retainLines: true,
        sourceType: 'unambiguous',
      },
      src,
      plugins: metroBabelRegister.config.plugins,
    })

    return generate(
      ast,
      {
        code: true,
        comments: false,
        compact: false,
        filename: file,
        retainLines: true,
        sourceFileName: file,
        sourceMaps: true,
      },
      src,
    ).code
  },

  getCacheKey: createCacheKeyFunction([
    __filename,
    require.resolve('metro-react-native-babel-transformer'),
    require.resolve('@babel/core/package.json'),
  ]),
}

Hm, it is true that adding "transform": { "^.+\\.js$": "<rootDir>/node_modules/react-native/jest/preprocessor.js" } to the jest config makes the tests to fail even on 0.59.

Question is if you need this transform in 0.59 of RN.

I can confirm, when upgrading to RN 0.59+ must remove the transform property from jest config file in order to avoid the error

@olegongit Removed and it works. Thank you !!! I guess it’s not needed anymore.

Quoting @cpojer’s answer in @karanjthakkar’s PR because it’s important:

Unfortunately I don’t think that’s the right fix. We do use this internally all over Facebook and do not run into a similar issue, and inline requires is probably just exposing a problem that exists elsewhere. While it may be a short-term hotfix that people can apply, we don’t want to disable this by default. I’m assuming the problem here is probably babel or some version of Jest 😦

One thing that I have noticed is that for Components, adding

constructor(props) {
    super(props)
}

solved the issue (found here).

Not sure if we should keep this open since the proposed workaround above (disabling the inline) is not the right solution, and I feel that probably it’s about code outside the main repo.

@kelset Just created a PR to start the conversation: #23326 🙂Thanks!

Great finding this thread, I just want to recap the solutions I’ve read so far.

I encountered this issue while trying to add a Jest test that uses react-test-renderer. I got the TypeError: Cannot read property 'default' of undefined error in react-native-vector-icons, at at new Icon (node_modules/react-native-vector-icons/lib/create-icon-set.js:42:389). This line defines a class with no constructor.

The first solution is adding a simple constructor to the above class, the error goes away.

Alternatively, to “solve” the issue I can remove the above constructor, but modify inlineRequires: true to inlineRequires: false in <rootDir>/node_modules/react-native/jest/preprocessor.js.

The third option is to go to my jest.config.js and remove the line '\\.js$': '<rootDir>/node_modules/react-native/jest/preprocessor.js', from the transform key.

I’m happy to fix this if somebody could make a minimal repo of a Jest+RN setup and share it here.

Would you be happy to accept a fix that changes the inlineRequires flag to false in jest/preprocessor.js?

Yeah I feel that by having a PR we can have a conversation with the FB team about it - because I think that the preprocessor is used around the internal codebase (but not sure atm).

@letsgojuno Oh wow, removing inlineRequires fixes it for me too. Been trying to find a fix for this for a few days. Thank you 🙏

I’ve been keeping a running list of issues/fixes when upgrading from RN 0.54 to 0.57. Added this one to the list https://github.com/tylergaw/RNUpgradePath#issue-4

@lewnelson I tried the empty constructor approach first, but didn’t have any luck with it. Got the same Cannot read property 'default' of undefined.

Yeah seems like this is only happening for configs that have this setting. It shouldn’t be needed when the preset option is used.

@retyui consider this a warning, your behaviour and aggressiveness won’t be tolerated any further. Behave and respect the other people in this community.

Good news guys, I have tried to create a simple example as @cpojer asked, but it works in 0.59. I have tried with a class property state and a class property function.

Moreover I have tested and it worked in 0.58. It failed in 0.57.8 though and probably earlier, so it was actually an issue.

(edit: At least it is the case with default RN jest setup, i.e. without adding a custom transfrom.)