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

Created on 6 Nov 2018  ·  35Comments  ·  Source: facebook/react-native

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

Bug Author Provided Repro Mid-Pri Locked 🔩Test Infrastructure

Most helpful comment

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.

All 35 comments

I've just added another example to the repo that I came across, by adding an empty constructor arrow functions transpile correctly on class methods https://github.com/lewnelson/react-native-jest-example/blob/master/src/working/containers/Something.js.

I'm not sure if the issue lies within the jest preprocessor or within the babel plugins themselves. Possibly the ordering of the plugins in the preprocessor?

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.

@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.

This has worked for me too @tylergaw @letsgojuno. Thanks

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.

Everyone having this issue - go check out the link to find the solution. I didn't go and was not able to resolve it right away. Thanks!

@kelset This is an issue in the latest 0.58.3 release as well.

Steps to reproduce:

  1. init a new project
  2. add a class property to App.js
  3. add a transform in the jest config in package.json which uses jest/preprocessor.js.
  4. Run npm test

Would you be happy to accept a fix that changes the inlineRequires flag to false in jest/preprocessor.js? Atleast until the plugin is fixed or the underlying issue which surfaced this in recent versions is discovered.

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).

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

+1 here, we're seeing this in some object-spread syntax which we refactored outside of a reducer.

Disabling inlineRequires also resolves the issue.

    TypeError: Cannot read property 'default' of undefined

      58 |     } = this.calculateNextSection(nextIndex)
      59 |
    > 60 |     return {
         |                       ^
      61 |       ...formSections,

RN 0.57.8

jest and babel-jest 23.6.0 and 24.1.0 both have this.

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, having your state defined in a constructor is not a workaround, it is just not using the static class properties feature of ES7.

Since using this feature is ok in react-native, one would like jest tests to embrace it. And this is what this bug is about.

Current workaround that we have employed is to use patch-package and just fix it inlineRequires, so that the tests work. But I don't think it qualifies as a proper solution to the issue...

@lewnelson I create a demo repo to reproduce bag https://github.com/retyui/rn-22175

@kelset How to break tests:
https://github.com/retyui/rn-22175/commit/b8afa28c4a7bcdc4a11861057b72374acd1116d1

In our upgrade from React Native 0.58.6 → 0.59.0 – a narrow set of changes based on https://github.com/react-native-community/rn-diff-purge – this has become an issue.

Example test failure:

● NotificationBanner › shows the notification

    TypeError: Cannot read property 'default' of undefined

      25 | |}>
      26 | 
    > 27 | class NotificationBanner extends React.Component<Props> {
         |                                                                                                                                                    ^
      28 |   static defaultProps = {
      29 |     notification: undefined,
      30 |     bottom: undefined,

      at new NotificationBanner (src/components/NotificationBanner/index.js:27:447)
      at constructClassInstance (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:3435:22)
      at updateClassComponent (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:6606:9)
      at beginWork (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:7563:20)
      at performUnitOfWork (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:11234:16)
      at workLoop (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:11266:28)
      at renderRoot (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:11349:11)
      at performWorkOnRoot (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:12237:11)
      at performWork (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:12149:11)
      at performSyncWork (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:12123:7)

The constructor hack recommended in https://github.com/facebook/react-native/issues/22175#issuecomment-469272365 works.

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

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.)

@olegongit try to do something like that https://github.com/retyui/rn-22175/commit/b8afa28c4a7bcdc4a11861057b72374acd1116d1

And they can say:

So, this can be closed imho.

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

Can confirm this issue in 0.59.1 with

 "babel-jest": "^24.5.0",
  "jest": "^24.5.0",
  "react-test-renderer": "^16.8.4",

All tests with class components are failing with Cannot read property 'default' of undefined .
Also confirming the constructor "hack" works (altho with many snapshots it becomes tedious to fix)

@kelset , I am not sure but I think this is a separate issue from the original one (since it doesn't tie directly to the aforementioned class arrow methods).

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.

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

I think this is a bug in the preprocessor (more specifically in metro-react-native-babel-transformer). Either removing babel.config.js or make it just module.exports = {}; will also fix the error. metro-react-native-babel-transformer seems to also add metro-react-native-babel-preset though, so I don't think you need to add it to your own code?

You might consider making metro-react-native-babel-transformer throw if the metro preset is in user config?

Disclaimer - I don't know RN or its transpilation pipeline at all 😀

EDIT: This is talking about the reproduction provided in https://github.com/retyui/rn-22175. I haven't read through the whole issue

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

So is it possible to make it work for 0.58.X? What changes should we make in the config? I'm afraid I didn't understand the fix.

@danielmartinprieto try the following: in your package.json find the "jest" section and remove the "transform": { "^.+\\.js$": "<rootDir>/node_modules/react-native/jest/preprocessor.js" } line.

It might be needed for some specific babel setup/presets though... so might need some more investigation.

Thanks, @olegongit. We'll try that!

Just confirming that for 0.58.x, removing the transform doesn’t work.

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'),
  ]),
}

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

@Fausto95 thanks for the comment and for the reference to the PR where you did the upgrade! We couldn't make it work until I saw your changes, and I realised something: we were using since the beginning .babelrc instead of babel.config.js and TIL they're not interchangeable.

From https://babeljs.io/docs/en/configuration:

Configure Babel
Babel can be configured! Many other tools have similar configs: ESLint (.eslintrc), Prettier (.prettierrc).

All Babel API options are allowed. However, if the option requires JavaScript, you may want to use a JavaScript configuration file.

What's your use case?
You want to programmatically create the configuration?
You want to compile node_modules?
babel.config.js is for you!

You have a static configuration that only applies to your simple single package?
.babelrc is for you!

Have a nice weekend, y'all!

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.

@pro-nasa your solution is working for me, even though I did not fully understand what's going on over there. Are we waiting for a permanent solution from some specific 3rd party, or react-native itself?

Thank you!

I have a web project. After jest updating I has the same issue. I tried to find any inlineRequires, but I had not succeeded

@a-x- inline requires should be in: node_modules/react-native/jest/preprocessor.js

I juste do a ugly sed -i -e 's/inlineRequires: true,/inlineRequires: false,/g' node_modules/react-native/jest/preprocessor.js in all my CI script at the moment

I had the same error and none of the fixes worked for me:

  • adding constructor to node_modules/react-native-vector-icons/lib/create-icon-set.js
  • changing inlineRequires to false
  • I had no transform key specified in jest.config.js

It turned out I had a circular dependency in my components:
Component A was importing Component B via components/index (which in turn was exporting Component A)
Using relative import path fixed the error.

Was this page helpful?
0 / 5 - 0 ratings