jestjscreate-react-appyarnpkg-v2vscode-jsconfig

CRA + Yarn 2 + jsconfig.json = Can't run unit tests


I have the following configuration in my jsconfig.json file:

{
  "compilerOptions": {
    "baseUrl": "./src"
  },
  "include": ["src"]
}

Which lets me do this:

import { App } from 'components'
import * as actions from 'actions/app.actions'

Instead of this:

import { App } from '../components'
import * as actions from '../actions/app.actions'

To get started with unit testing, I've created a simple App.test.jsx in src/components/tests

import { render } from '@testing-library/react'
import { App } from 'components'

it('renders without crashing', () => {
  render(<App />)
})

However, when I run yarn test (which is sugar for react-scripts test), it throws with this ugly error:

 FAIL  src/components/tests/App.test.jsx
  ● Test suite failed to run

    Your application tried to access components, but it isn't declared in your dependencies; 
this makes the require call ambiguous and unsound.

    Required package: components (via "components")
    Required by: C:\Users\Summer\Code\sandbox\src\components\tests\

      23714 |     enumerable: false
      23715 |   };
    > 23716 |   return Object.defineProperties(new Error(message), {
            |                                  ^
      23717 |     code: { ...propertySpec,
      23718 |       value: code
      23719 |     },

      at internalTools_makeError (.pnp.js:23716:34)
      at resolveToUnqualified (.pnp.js:24670:23)
      at resolveRequest (.pnp.js:24768:29)
      at Object.resolveRequest (.pnp.js:24846:26)

It seems like Jest (or Yarn?) thinks components is a node package, because it's not aware of the absolute imports setting in my jsconfig.json. Is there a way to make it aware? Or do I have to choose between 0% coverage and relative imports?

I've tried entering "moduleNameMapper" under "jest" in my package.json like the documentation explains, but it didn't help. I got the same error + one more after it.

I've also tried changing components in the test file to ../components but then it complains about actions/app.actions which is inside the <App /> component.

Module name mapper config:

/* package.json */
{
  /* ... */
  "jest": {
    "moduleNameMapper": {
      "actions(.*)$": "<rootDir>/src/actions$1",
      "assets(.*)$": "<rootDir>/src/assets$1",
      "components(.*)$": "<rootDir>/src/components$1",
      "mocks(.*)$": "<rootDir>/src/mocks$1",
      "pages(.*)$": "<rootDir>/src/pages$1",
      "reducers(.*)$": "<rootDir>/src/reducers$1",
      "scss(.*)$": "<rootDir>/src/scss$1",
      "store(.*)$": "<rootDir>/src/store$1",
      "themes(.*)$": "<rootDir>/src/themes$1",
      "api": "<rootDir>/src/api.js",
    }
  }
}

Solution

  • This is because Yarn takes control of the resolution pipeline, and thus isn't aware of resolution directives coming from third-party configuration (like moduleNameMapper).

    This isn't to say you have no options, though - specifically, the fix here is to avoid moduleNameMapper, and instead leverage the builtin link: dependency protocol. This has other advantages, such as being compatible with all tools (TS, Jest, ESLint, ...) without need to port your aliases to each configuration format.

    See also: Why is the link: protocol recommended over aliases for path mapping?