Open milesj opened 3 years ago
Here's a link to a repo that verifies the behavior you're describing. However, this is almost definitely working as intended. If you'd like to share a partial configuration between projects, I believe this is the preferred approach.
That said, it wouldn't be overly complicated to search for a base config in the worktree root and merge it with each project config. Maybe there's interest in having a flag for this behavior?
@Rossh87 seems strange that this is working as intended. I have a file that adds custom expect assertions that all projects are using. This means I have to copy over setupFilesAfterEnv
into each project. Sure it is easy enough to spread it out when using jest.config.js
however using package.json
can lead to typos and make it annoying to change. There has to be some easier way to set this up
The way I have it in my monorepos is using preset
to point to a jest.preset.js
in the root of the repo. Check out this example from Nx to see it in practise.
@manchuck I probably shouldn't have tried to speak to the authors' intentions, since I'm not a maintainer. What I mean is just that I can imagine root config-sharing by default leading to confusion or slowing down tests a lot for monorepos whose projects have very different external dependencies (databases, servers, etc.) that need to be setup for tests. For example, if I have a client module and a server module in a monorepo that represents all the code for some website, I might want to spin up a real database for integration testing the server, but probably won't need it for testing the client. If the database setup ended up in the root config, I'd be needlessly spinning up a database every time I ran the client test suite, which isn't ideal. Conceptually, my impression is that workspaces are intended to be autonomous, and sharing setup creates implicit dependency, which isn't ideal IMO. I do see your point, though, and it's perfectly reasonable to disagree.
Depending on your specific case, you could try extracting your custom assertion library into its own workspace, turning it into a discrete package that your other workspaces can explicitly depend on by importing it. That's what I would personally prefer for a custom assertion library, but YMMV.
Here is an example of my Jest config for two cases: unit tests and integration tests. Unfortunately it is not possible to share repeated configuration in top level as it just does not work. It would help a lot if properties from top level would be augmented to projects
with simple Object.assign
.
const DO_NOT_CHANGE = 100;
module.exports = {
collectCoverage: true,
coverageReporters: [
['text', { skipFull: true }],
'text-summary',
],
projects: [
{
cacheDirectory: '<rootDir>/.jestcache',
collectCoverageFrom: [
'packages/*/src/**/*.ts?(x)',
],
coveragePathIgnorePatterns: [
'/__template__/',
'/lib/',
'/node_modules/',
],
coverageThreshold: {
global: {
statements: DO_NOT_CHANGE,
branches: DO_NOT_CHANGE,
functions: DO_NOT_CHANGE,
lines: DO_NOT_CHANGE,
},
},
displayName: 'unit',
errorOnDeprecated: true,
globals: {
'ts-jest': {
tsconfig: '<rootDir>/tsconfig.common.json',
},
},
moduleNameMapper: {
'\\.css$': 'identity-obj-proxy',
'^!svg-url-loader!(.*)$': '$1',
'^!url-loader!(.*)$': '$1',
},
preset: 'ts-jest',
resetMocks: true,
setupFiles: [
'<rootDir>/__tests__/setupTestEnvironment.ts',
],
setupFilesAfterEnv: [
'<rootDir>/__tests__/setupTests.ts',
],
snapshotSerializers: [
'enzyme-to-json/serializer',
],
testMatch: [
'**/src/**/*.spec.ts?(x)',
],
transform: {
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': '<rootDir>/__mocks__/fileTransformer.js',
},
},
{
cacheDirectory: '<rootDir>/.jestcache',
collectCoverageFrom: [
'packages/*/src/?(*/)?(*/)*.ts?(x)', // Report only 2 levels deep.
'!**/*.spec.ts?(x)',
],
coveragePathIgnorePatterns: [
'/__template__/',
'/lib/',
'/node_modules/',
],
coverageThreshold: {
global: {
statements: DO_NOT_CHANGE,
branches: DO_NOT_CHANGE,
functions: DO_NOT_CHANGE,
lines: DO_NOT_CHANGE,
},
},
displayName: 'integration',
errorOnDeprecated: true,
globals: {
'ts-jest': {
tsconfig: '<rootDir>/tsconfig.common.json',
},
},
moduleNameMapper: {
'\\.css$': 'identity-obj-proxy',
'^!svg-url-loader!(.+)$': '$1',
'^!url-loader!(.*)$': '$1',
},
preset: 'ts-jest',
resetMocks: true,
setupFiles: [
'<rootDir>/__tests__/setupTestEnvironment.ts',
],
setupFilesAfterEnv: [
'<rootDir>/__tests__/setupTests.ts',
],
snapshotSerializers: [
'enzyme-to-json/serializer',
],
testMatch: [
'**/__tests__/**/*.test.ts?(x)',
],
transform: {
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': '<rootDir>/__mocks__/fileTransformer.js',
},
},
],
};
@igorpupkinable Would you mind to elaborate on how exactly it does not work when sharing common properties using Object.assign
?
const DO_NOT_CHANGE = 100;
const common = {
cacheDirectory: '<rootDir>/.jestcache',
coveragePathIgnorePatterns: [
'/__template__/',
'/lib/',
'/node_modules/',
],
coverageThreshold: {
global: {
statements: DO_NOT_CHANGE,
branches: DO_NOT_CHANGE,
functions: DO_NOT_CHANGE,
lines: DO_NOT_CHANGE,
},
},
errorOnDeprecated: true,
globals: {
'ts-jest': {
tsconfig: '<rootDir>/tsconfig.common.json',
},
},
moduleNameMapper: {
'\\.css$': 'identity-obj-proxy',
'^!svg-url-loader!(.*)$': '$1',
'^!url-loader!(.*)$': '$1',
},
preset: 'ts-jest',
resetMocks: true,
setupFiles: [
'<rootDir>/__tests__/setupTestEnvironment.ts',
],
setupFilesAfterEnv: [
'<rootDir>/__tests__/setupTests.ts',
],
snapshotSerializers: [
'enzyme-to-json/serializer',
],
transform: {
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': '<rootDir>/__mocks__/fileTransformer.js',
},
};
module.exports = {
collectCoverage: true,
coverageReporters: [
['text', { skipFull: true }],
'text-summary',
],
projects: [
Object.assign({}, common, {
displayName: 'unit',
collectCoverageFrom: [
'packages/*/src/**/*.ts?(x)',
],
testMatch: [
'**/src/**/*.spec.ts?(x)',
],
}),
Object.assign({}, common, {
displayName: 'integration',
collectCoverageFrom: [
'packages/*/src/?(*/)?(*/)*.ts?(x)', // Report only 2 levels deep.
'!**/*.spec.ts?(x)',
],
testMatch: [
'**/__tests__/**/*.test.ts?(x)',
],
}),
],
};
@hesalx you do augmentation manually in your example. I meant it should be done by Jest under the hood, e.g.
projects.forEach((project) => {
Object.assign(project, { ..config, projects: undefined });
});
It means root level jest config into a monorepo should only contain "projects" key? I assumed the same thought of @milesj in the beginning :/
A bit tricky in my opinion... as @manchuck said, it forces us to duplicate files with setup config.
An example: I'm using msw and I need to setup msw server using setupFilesAfterEnv
. But, in order to do it, I need to setup my env file before using dotenv into setupFiles
, so my server will be able to read my BASE_URL
.
In this case I need to duplicate my setup files into every project/package of my monorepo.
@gsevla I wound up just having the custom expectations in a file and mapping over all the projects ni the config, then appending the expectations. Hopefully that can help you keep the config DRY
The documentation seems to indicate the root configuration should be copied to each project.
https://jestjs.io/docs/configuration#projects-arraystring--projectconfig
Based on how it works in practice, I suppose the confusion comes from the usage of the term "root-level".
Here "root-level configuration" means "the configuration file in the project's root" and not "the root of the exported object in the configuration file".
This NOTE primarily clarifies the resolution of the template variables and not the inheritance of configurations per se.
In other words,
With the projects
option enabled, Jest will copy each individual item of the projects option of the root-level configuration to respective child configuration during the test run, however the values will be resolved in the child's context, i.e. <rootDir>
will point to the child's root directory and not to the root-level configuration directory.
Perhaps clarity can be found in the discussion from the PR which added this note: https://github.com/facebook/jest/pull/12871.
This issue is stale because it has been open for 1 year with no activity. Remove stale label or comment or this will be closed in 30 days.
Bump
π Bug Report
This isn't mentioned anywhere in the docs, so I'm not entirely sure how this is supposed to work. But I have a monorepo (yarn workspaces) and I want some packages to have expanded Jest configs, while all of them to inherit "common" configs. I assumed that any settings in the root
jest.config.js
would be inherited for all project configs, but this does not seem to be the case, and is honestly very tedious to work around.If I have this root config:
I would expect all projects to inherit the
setupFilesAfterEnv
setting, but they do not. According to--showConfig
, it's just an empty array. How else are we supposed to share config without having to create ajest.config.js
in each project?To Reproduce
Steps to reproduce the behavior:
Mentioned above.
Expected behavior
Projects would inherit the root/global config.
Link to repl or repo (highly encouraged)
envinfo