Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Jest preset and Yarn Workspaces #17469

Closed
dingbat opened this issue Jan 6, 2018 · 24 comments
Closed

Jest preset and Yarn Workspaces #17469

dingbat opened this issue Jan 6, 2018 · 24 comments
Labels
Ran Commands One of our bots successfully processed a command. Stale There has been a lack of activity on this issue and it may be closed soon.

Comments

@dingbat
Copy link
Contributor

dingbat commented Jan 6, 2018

Is this a bug report?

Yes, or maybe incompatibility with a Yarn feature is more accurate

Have you read the Contributing Guidelines?

Yes

Environment

Environment:
OS: macOS Sierra 10.12.6
Node: 8.9.1
Yarn: 1.3.2
npm: 5.5.1
Watchman: 4.7.0
Xcode: Xcode 9.2 Build version 9C40b
Android Studio: 2.2 AI-145.3360264

Packages: (wanted => installed)
react: 16.0.0
react-native: 0.51.0

Target Platform: N/A

Steps to Reproduce

  1. react-native init rnproject
  2. rm -rf rnproject/node_modules
  3. Add package.json:
{
  "private": true,
  "workspaces": [
    "rnproject"
  ]
}
  1. yarn
  2. cd rnproject
  3. yarn test
● Validation Error:

  Module <rootDir>/node_modules/react-native/jest/assetFileTransformer.js in the transform option was not found.

  Configuration Documentation:
  https://facebook.github.io/jest/docs/configuration.html

error Command failed with exit code 1.

Expected Behavior

React Native's jest preset should work with projects using yarn workspaces, where modules may not be in the project's node_modules directory (they may be hoisted in the parent dir's node_modules.)

Actual Behavior

The jest preset provided by React Native specifies <rootDir>/node_modules/... in a couple places, which is not always the location of certain modules (i.e. when using Yarn workspaces):

"modulePathIgnorePatterns": [
    "<rootDir>/node_modules/react-native/Libraries/react-native/"
  ],
  "transform": {
    "^...$": "<rootDir>/node_modules/react-native/jest/assetFileTransformer.js"
  },
  "setupFiles": [
    "<rootDir>/node_modules/react-native/jest/setup.js"
  ],

Thus, Jest produces an error saying that it can't find the files in the right spots.

The Yarn documentation on workspaces is opinionated on making assumptions about the package layout, I tend to agree:

The package layout will be different between your workspace and what your users will get (the workspace dependencies will be hoisted higher into the filesystem hierarchy). Making assumptions about this layout was already hazardous since the hoisting process is not standardized, so theoretically nothing new here.

But I'm also not sure how to achieve a layout-agnostic kind of a setup.

Reproducible Demo

https://github.com/dingbat/ws-rn-bug-example

Some fixes I tried

  • When running jest, specifying the root dir as the workspace root (--rootDir ..). This caused a bunch more errors and didn't seem like a good direction, because we should be able to run tests isolated to a subdir anyway.
  • Copy and paste the RN config in with the paths modified for the hoisted node_modules. Tried this with and without "preset": "react-native", neither one worked
    • Using the preset still tried loading in a setup file from the RN preset (I think because it specifies an array) Module <rootDir>/node_modules/react-native/jest/setup.js in the setupFiles option was not found.
    • Not using the preset probably will never work... Cannot find module 'setupDevtools' from 'setup.js'
  • Symlink rnproject/node_modules/react-native to node_modules/react-native (not a real solve since this wouldn't survive Git and would have to be some sort of build step). No luck, for some reason Jest still can't find the asset transformer:
    ● Validation Error:
    
      Module <rootDir>/node_modules/react-native/jest/assetFileTransformer.js in the transform option was not found.
    
      Configuration Documentation:
      https://facebook.github.io/jest/docs/configuration.html
    

Note

My intuition tells me there's no easy fix in this repo, and there's much better luck of addressing it in the Jest repo. But since the broken jest-preset configuration is provided in this repo, I thought it would make more sense to open an issue here.

@stevenleeg
Copy link

I've been running into this issue as well. Any updates here?

@monicatie
Copy link

Recently jumped onto react-native and jest and ran into this recently as well. Curious what the plan is.

@mgenov
Copy link

mgenov commented Jan 29, 2018

I'm encountering the same issue. Does anyone knows if there is a tracking tracking issue in the jest repository ?

@egid
Copy link

egid commented Feb 2, 2018

Looks like someone created a ticket, then closed it out with their own symlink-heavy workaround (?): jestjs/jest#5325

I think this is maybe worth reopening?

@Smtih
Copy link

Smtih commented Feb 8, 2018

I've been hacking away at this, seems I can get things working by adding these to my jest config in my package.json
"rootDir": "../..", <--- points to root where node modules folder is "roots": [ "<rootDir>/packages/mobile", <----- points back to the folder I actually want to search for tests "<rootDir>/node_modules" <------ need to also have the node modules folder otherwise errors ],

Babelrc file also needs to be in root or you'll probably have transpiling related errors.

@kcjonson
Copy link

kcjonson commented Feb 20, 2018

Full reproduction in isolation in Lerna with steps here:
https://github.com/kcjonson/jest-hoisting-bug-demo/blob/master/README.md

Minimal setup that I could muster

mkdir test
cd test
lerna init
cd packages
react-native init Foo
react-native init Bar

Readme has steps to see tests broken after deps were hoisted by lerna (or yarn workspaces, but I didn't document that)

@kcjonson
Copy link

I proceeded trying to manually map all the things that it wasn't finding by hand, hoping I could find some pattern that I was missing. This list is the point at which I ran out of beer and gave up.

"moduleNameMapper": {
  "^React$": "<rootDir>/../../node_modules/react",
  "^ReactNative$": "<rootDir>/../../node_modules/react-native",
  "setupDevtools": "<rootDir>/../../node_modules/react-native/Libraries/Core/Devtools/setupDevtools.js",
  "InitializeCore": "<rootDir>/../../node_modules/react-native/Libraries/Core/InitializeCore.js",
  "Image": "<rootDir>/../../node_modules/react-native/Libraries/Image/Image.ios.js",
  "Text": "<rootDir>/../../node_modules/react-native/Libraries/Text/Text.js",
  "Modal": "<rootDir>/../../node_modules/react-native/Libraries/Modal/Modal.js",
  "View": "<rootDir>/../../node_modules/react-native/lib/View.js",
  "RefreshControl": "<rootDir>/../../node_modules/react-native/Libraries/Components/RefreshControl/RefreshControl.js",
  "ActivityIndicator": "<rootDir>/../../node_modules/react-native/Libraries/Components/ActivityIndicator/ActivityIndicator.js",
  "AnimatedImplementation": "<rootDir>/../../node_modules/react-native/Libraries/Animated/src/AnimatedImplementation.js",
  "ensureComponentIsNative": "<rootDir>/../../node_modules/react-native/Libraries/Components/Touchable/ensureComponentIsNative.js",
  "ErrorUtils": "<rootDir>/../../node_modules/react-native/Libraries/vendor/core/ErrorUtils.js",
  "NativeModules": "<rootDir>/../../node_modules/react-native/Libraries/BatchedBridge/NativeModules.js",
  "ReactNativePropRegistry": "<rootDir>/../../node_modules/react-native/Libraries/Renderer/shims/ReactNativePropRegistry.js",
  "requireNativeComponent": "<rootDir>/../../node_modules/react-native/Libraries/ReactNative/requireNativeComponent.js"
},

What it does seem to say is that react-native is having a lot of problems finding itself. In setup.js you can clearly see :

jest
  .mock('InitializeCore', () => {})
  .mock('Image', () => mockComponent('Image'))
  .mock('Text', () => mockComponent('Text'))
  .mock('TextInput', () => mockComponent('TextInput'))
  .mock('Modal', () => mockComponent('Modal'))
  .mock('View', () => mockComponent('View'))
  .mock('RefreshControl', () => require.requireMock('RefreshControlMock'))
  .mock('ScrollView', () => require.requireMock('ScrollViewMock'))

is a bunch of things that its going to mock, which seems totally reasonable, but its trying to resolve those locations first. Where I'm getting lost here is that all of those things such as Text are rather obviously unmapped modules, and for the life of me I can't find any bits of code that would be doing the mapping of them! How on earth are those being resolved in a non-hoisted environment?

@egid
Copy link

egid commented Feb 22, 2018

I tried out @Smtih's jest config example, replacing packages/mobile with my project's location in the monorepo, and it errors out (differently but still errors). It's now able to resolve assetFileTransformer, but Cannot find module 'react/lib/ReactComponentTreeHook' from 'ReactDebugTool.js'.

Like @kcjonson says, it seems like React Native is having trouble locating dependencies, not Jest.

@react-native-bot
Copy link
Collaborator

Thanks for posting this! It looks like you may not be using the latest version of React Native, v0.53.0, released on January 2018. Can you make sure this issue can still be reproduced in the latest version?

I am going to close this, but please feel free to open a new issue if you are able to confirm that this is still a problem in v0.53.0 or newer.

How to ContributeWhat to Expect from Maintainers

@react-native-bot react-native-bot added Ran Commands One of our bots successfully processed a command. Stale There has been a lack of activity on this issue and it may be closed soon. labels Feb 24, 2018
@salmanwaheed
Copy link

salmanwaheed commented Feb 24, 2018 via email

@robhanlon22
Copy link

Still occurring.

@HeberLZ
Copy link

HeberLZ commented Mar 27, 2018

For people still struggling with this i got a yarn workflow friendly fix, not pretty but it'll help us until the root of the issue is fixed. Instead of defining <rootDir>/node_modules/react-native/jest/assetFileTransformer.js on the package.json, point to a proxy file so that the module resolution is managed by node instead of jest, solving the issue at hand which is that the module is actually installed on a parent folder. Example:

// old package.json
<rootDir>/node_modules/react-native/jest/assetFileTransformer.js
// new package.json
<rootDir>/proxy/assetFileTransformer.js
// and on the proxy/assetFileTransformer.js file
module.exports = require('react-native/jest/assetFileTransformer.js')

Same thing would apply to babel-jest

// old package.json
<rootDir>/node_modules/babel-jest
// new package.json
<rootDir>/proxy/babel-jest.js
// and on the proxy/babel-jest.js file
module.exports = require('babel-jest')

Another option would probably be to ignore the rootDir which should have the same effect, you'd have to make sure that the dependency is correct and that is installed on somewhere otherwise i'll end up using the global module if installed. In this case it could be simplified to:

// old package.json
<rootDir>/node_modules/babel-jest
// new package.json
babel-jest

@FezVrasta
Copy link

Yarn now supports a nohoist option that may come in handy.

https://yarnpkg.com/blog/2018/02/15/nohoist/

@robhanlon22
Copy link

robhanlon22 commented Mar 29, 2018

Quick and dirty fix for this:

{
  "workspaces": {
    "packages": ["packages/*"],
    "nohoist": "**/*"
  }
}

I tried being conservative on what I wasn't hoisting, but for the take of productivity, I just disabled hoisting altogether.

@FezVrasta
Copy link

@ohwillie what's the point of doing this? Don't you lose all the advantages of workspaces?

@robhanlon22
Copy link

You still get the single yarn.lock and single yarn install command, which is definitely a win.

@robhanlon22
Copy link

If you'd like to be more granular, you can start by nohoisting **/react-native and then playing "follow the error"

@chucksellick
Copy link

This is a bit of a nightmare, if Yarn supported per-project nohoist then there would be an easy option, but instead I had to blanket nohoist both react-native and jest and all their dependencies (which as you can imagine is an awful lot of dependencies) to get around this. Latest version of react-native (0.55.1). Not sure why this issue is closed as it is definitely an incompatibility with a widely used feature of Yarn?

@robhanlon22
Copy link

robhanlon22 commented Apr 8, 2018 via email

@chucksellick
Copy link

It only supports per-package on private packages for some reason. Trying to add it into the package.json of a public package will throw an error. I'm not sure why the distinction is made for this feature.

@robhanlon22
Copy link

robhanlon22 commented Apr 8, 2018 via email

@pronebird
Copy link

Is there any plan to fix this in react-native?

@Pranit-Harekar
Copy link

This worked for me: justinsisley/Jest-CSS-Modules#13 (comment)

@KSR001
Copy link

KSR001 commented Oct 17, 2018

Thank you @HeberLZ for giving a workaround which resolves the problem, without having to compromise on yarn workspace advantages (which you lose when using nohoist).

@facebook facebook locked as resolved and limited conversation to collaborators Feb 24, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Ran Commands One of our bots successfully processed a command. Stale There has been a lack of activity on this issue and it may be closed soon.
Projects
None yet
Development

No branches or pull requests