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",
}
}
}
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?