ts-jest
ts-jest copied to clipboard
Tests fail when using TypeScript project references
š Bug Report
I'm in the process of migrating one of my projects to use Typescript project references, but after migration the tests that validated error types failed.
I've created a repo that has one directory with a very small project where tests succeed, and one directory with a project using typescript projects references where the tests fail. The projects are identical with the exception of project references. I'm not saying that ts-jest is to blame. It could as well be my understanding of project references or jest.
To Reproduce
git clone https://github.com/FantasticFiasco/jest-error-type-issue.git
cd jest-error-type-issue/does-not-work
yarn
yarn test
Expected behavior
I would expect the test to pass, because in the same git repository there is a directory called works with an identical project except the project references that has a test that passes.
Link to repo (highly encouraged)
https://github.com/FantasticFiasco/jest-error-type-issue
Debug log:
{"context":{"allowJs":false,"logLevel":20,"namespace":"jest-preset","package":"ts-jest","version":"25.5.1"},"message":"creating jest presets not handling JavaScript files","sequence":1,"time":"2020-05-15T16:59:54.105Z"}
{"context":{"logLevel":20,"namespace":"Importer","package":"ts-jest","version":"25.5.1"},"message":"creating Importer singleton","sequence":2,"time":"2020-05-15T16:59:55.006Z"}
{"context":{"allowJs":false,"logLevel":20,"namespace":"jest-preset","package":"ts-jest","version":"25.5.1"},"message":"creating jest presets not handling JavaScript files","sequence":3,"time":"2020-05-15T16:59:55.010Z"}
{"context":{"actualVersion":"25.5.4","expectedVersion":">=25 <26","logLevel":20,"namespace":"versions","package":"ts-jest","version":"25.5.1"},"message":"checking version of jest: OK","sequence":4,"time":"2020-05-15T16:59:55.011Z"}
{"context":{"baseOptions":{},"logLevel":20,"namespace":"jest-transformer","package":"ts-jest","transformerId":1,"version":"25.5.1"},"message":"created new transformer","sequence":5,"time":"2020-05-15T16:59:55.011Z"}
{"context":{"fileName":"/Users/mattiask/code/open-source/temp/jest-error-type-issue/does-not-work/test/foo.spec.ts","logLevel":20,"namespace":"jest-transformer","package":"ts-jest","transformOptions":{"config":{"automock":false,"browser":false,"cache":true,"cacheDirectory":"/private/var/folders/cl/yqb0znw56dj17l06y_45rzx40000gn/T/jest_dx","clearMocks":false,"coveragePathIgnorePatterns":["/node_modules/"],"cwd":"/Users/mattiask/code/open-source/temp/jest-error-type-issue/does-not-work","detectLeaks":false,"detectOpenHandles":false,"errorOnDeprecated":false,"extraGlobals":[],"forceCoverageMatch":[],"globals":{},"haste":{"computeSha1":false,"providesModuleNodeModules":[],"throwOnModuleCollision":false},"moduleDirectories":["node_modules"],"moduleFileExtensions":["js","json","jsx","ts","tsx","node"],"moduleNameMapper":[],"modulePathIgnorePatterns":[],"name":"065014f8040ebe756b2da8ebc80e3c0a","prettierPath":"prettier","resetMocks":false,"resetModules":false,"restoreMocks":false,"rootDir":"/Users/mattiask/code/open-source/temp/jest-error-type-issue/does-not-work","roots":["/Users/mattiask/code/open-source/temp/jest-error-type-issue/does-not-work"],"runner":"jest-runner","setupFiles":[],"setupFilesAfterEnv":[],"skipFilter":false,"snapshotSerializers":[],"testEnvironment":"/Users/mattiask/code/open-source/temp/jest-error-type-issue/does-not-work/node_modules/jest-environment-node/build/index.js","testEnvironmentOptions":{},"testLocationInResults":false,"testMatch":["**/__tests__/**/*.[jt]s?(x)","**/?(*.)+(spec|test).[tj]s?(x)"],"testPathIgnorePatterns":["/node_modules/"],"testRegex":[],"testRunner":"/Users/mattiask/code/open-source/temp/jest-error-type-issue/does-not-work/node_modules/jest-jasmine2/build/index.js","testURL":"http://localhost","timers":"real","transform":[["^.+\\.tsx?$","/Users/mattiask/code/open-source/temp/jest-error-type-issue/does-not-work/node_modules/ts-jest/dist/index.js",{}]],"transformIgnorePatterns":["/node_modules/"],"watchPathIgnorePatterns":[]},"instrument":false,"rootDir":"/Users/mattiask/code/open-source/temp/jest-error-type-issue/does-not-work","supportsDynamicImport":false,"supportsStaticESM":false},"transformerId":1,"version":"25.5.1"},"message":"computing cache key for /Users/mattiask/code/open-source/temp/jest-error-type-issue/does-not-work/test/foo.spec.ts","sequence":6,"time":"2020-05-15T16:59:55.012Z"}
{"context":{"logLevel":30,"namespace":"jest-transformer","package":"ts-jest","transformerId":1,"version":"25.5.1"},"message":"no matching config-set found, creating a new one","sequence":7,"time":"2020-05-15T16:59:55.012Z"}
{"context":{"config":{"automock":false,"browser":false,"cache":true,"cacheDirectory":"/private/var/folders/cl/yqb0znw56dj17l06y_45rzx40000gn/T/jest_dx","clearMocks":false,"coveragePathIgnorePatterns":["/node_modules/"],"cwd":"/Users/mattiask/code/open-source/temp/jest-error-type-issue/does-not-work","detectLeaks":false,"detectOpenHandles":false,"errorOnDeprecated":false,"extraGlobals":[],"forceCoverageMatch":[],"globals":{},"haste":{"computeSha1":false,"providesModuleNodeModules":[],"throwOnModuleCollision":false},"moduleDirectories":["node_modules"],"moduleFileExtensions":["js","json","jsx","ts","tsx","node"],"moduleNameMapper":[],"modulePathIgnorePatterns":[],"name":"065014f8040ebe756b2da8ebc80e3c0a","prettierPath":"prettier","resetMocks":false,"resetModules":false,"restoreMocks":false,"rootDir":"/Users/mattiask/code/open-source/temp/jest-error-type-issue/does-not-work","roots":["/Users/mattiask/code/open-source/temp/jest-error-type-issue/does-not-work"],"runner":"jest-runner","setupFiles":[],"setupFilesAfterEnv":[],"skipFilter":false,"snapshotSerializers":[],"testEnvironment":"/Users/mattiask/code/open-source/temp/jest-error-type-issue/does-not-work/node_modules/jest-environment-node/build/index.js","testEnvironmentOptions":{},"testLocationInResults":false,"testMatch":["**/__tests__/**/*.[jt]s?(x)","**/?(*.)+(spec|test).[tj]s?(x)"],"testPathIgnorePatterns":["/node_modules/"],"testRegex":[],"testRunner":"/Users/mattiask/code/open-source/temp/jest-error-type-issue/does-not-work/node_modules/jest-jasmine2/build/index.js","testURL":"http://localhost","timers":"real","transform":[["^.+\\.tsx?$","/Users/mattiask/code/open-source/temp/jest-error-type-issue/does-not-work/node_modules/ts-jest/dist/index.js",{}]],"transformIgnorePatterns":["/node_modules/"],"watchPathIgnorePatterns":[]},"logLevel":20,"namespace":"backports","package":"ts-jest","transformerId":1,"version":"25.5.1"},"message":"backporting config","sequence":8,"time":"2020-05-15T16:59:55.012Z"}
{"context":{"jestConfig":{"automock":false,"browser":false,"cache":true,"cacheDirectory":"/private/var/folders/cl/yqb0znw56dj17l06y_45rzx40000gn/T/jest_dx","clearMocks":false,"coveragePathIgnorePatterns":["/node_modules/"],"cwd":"/Users/mattiask/code/open-source/temp/jest-error-type-issue/does-not-work","detectLeaks":false,"detectOpenHandles":false,"errorOnDeprecated":false,"extraGlobals":[],"forceCoverageMatch":[],"globals":{"ts-jest":{}},"haste":{"computeSha1":false,"providesModuleNodeModules":[],"throwOnModuleCollision":false},"moduleDirectories":["node_modules"],"moduleFileExtensions":["js","json","jsx","ts","tsx","node"],"moduleNameMapper":[],"modulePathIgnorePatterns":[],"name":"065014f8040ebe756b2da8ebc80e3c0a","prettierPath":"prettier","resetMocks":false,"resetModules":false,"restoreMocks":false,"rootDir":"/Users/mattiask/code/open-source/temp/jest-error-type-issue/does-not-work","roots":["/Users/mattiask/code/open-source/temp/jest-error-type-issue/does-not-work"],"runner":"jest-runner","setupFiles":[],"setupFilesAfterEnv":[],"skipFilter":false,"snapshotSerializers":[],"testEnvironment":"/Users/mattiask/code/open-source/temp/jest-error-type-issue/does-not-work/node_modules/jest-environment-node/build/index.js","testEnvironmentOptions":{},"testLocationInResults":false,"testMatch":["**/__tests__/**/*.[jt]s?(x)","**/?(*.)+(spec|test).[tj]s?(x)"],"testPathIgnorePatterns":["/node_modules/"],"testRegex":[],"testRunner":"/Users/mattiask/code/open-source/temp/jest-error-type-issue/does-not-work/node_modules/jest-jasmine2/build/index.js","testURL":"http://localhost","timers":"real","transform":[["^.+\\.tsx?$","/Users/mattiask/code/open-source/temp/jest-error-type-issue/does-not-work/node_modules/ts-jest/dist/index.js",{}]],"transformIgnorePatterns":["/node_modules/"],"watchPathIgnorePatterns":[]},"logLevel":20,"namespace":"config","package":"ts-jest","transformerId":1,"version":"25.5.1"},"message":"normalized jest config","sequence":9,"time":"2020-05-15T16:59:55.013Z"}
{"context":{"logLevel":20,"namespace":"config","package":"ts-jest","transformerId":1,"tsJestConfig":{"compiler":"typescript","diagnostics":{"ignoreCodes":[6059,18002,18003],"pretty":true,"throws":true},"isolatedModules":false,"packageJson":{"kind":"file"},"transformers":[],"tsConfig":{"kind":"file"}},"version":"25.5.1"},"message":"normalized ts-jest config","sequence":10,"time":"2020-05-15T16:59:55.013Z"}
{"context":{"logLevel":20,"namespace":"config","package":"ts-jest","transformerId":1,"version":"25.5.1"},"message":"babel is disabled","sequence":11,"time":"2020-05-15T16:59:55.015Z"}
{"context":{"logLevel":20,"namespace":"Importer","package":"ts-jest","requireResult":{"exists":true,"given":"typescript","path":"/Users/mattiask/code/open-source/temp/jest-error-type-issue/does-not-work/node_modules/typescript/lib/typescript.js"},"version":"25.5.1"},"message":"loaded module typescript","sequence":12,"time":"2020-05-15T16:59:55.169Z"}
{"context":{"logLevel":20,"namespace":"Importer","package":"ts-jest","version":"25.5.1"},"message":"patching typescript","sequence":13,"time":"2020-05-15T16:59:55.169Z"}
{"context":{"actualVersion":"3.9.2","expectedVersion":">=3.4 <4","logLevel":20,"namespace":"versions","package":"ts-jest","version":"25.5.1"},"message":"checking version of typescript: OK","sequence":14,"time":"2020-05-15T16:59:55.169Z"}
{"context":{"logLevel":20,"namespace":"config","package":"ts-jest","transformerId":1,"tsConfigFileName":"/Users/mattiask/code/open-source/temp/jest-error-type-issue/does-not-work/tsconfig.json","version":"25.5.1"},"message":"readTsConfig(): reading /Users/mattiask/code/open-source/temp/jest-error-type-issue/does-not-work/tsconfig.json","sequence":15,"time":"2020-05-15T16:59:55.169Z"}
{"context":{"logLevel":20,"namespace":"config","package":"ts-jest","transformerId":1,"tsconfig":{"compileOnSave":false,"configFileSpecs":{"filesSpecs":[],"wildcardDirectories":{}},"errors":[],"fileNames":[],"options":{"configFilePath":"/Users/mattiask/code/open-source/temp/jest-error-type-issue/does-not-work/tsconfig.json","declaration":false,"inlineSourceMap":false,"inlineSources":true,"module":1,"noEmit":false,"removeComments":false,"sourceMap":true,"target":1},"projectReferences":[{"originalPath":"./src","path":"/Users/mattiask/code/open-source/temp/jest-error-type-issue/does-not-work/src"},{"originalPath":"./test","path":"/Users/mattiask/code/open-source/temp/jest-error-type-issue/does-not-work/test"}],"raw":{"compileOnSave":false,"compilerOptions":{},"files":[],"references":[{"path":"./src"},{"path":"./test"}]},"typeAcquisition":{"enable":false,"exclude":[],"include":[]},"wildcardDirectories":{}},"version":"25.5.1"},"message":"normalized typescript config","sequence":16,"time":"2020-05-15T16:59:55.175Z"}
{"context":{"fileName":"/Users/mattiask/code/open-source/temp/jest-error-type-issue/does-not-work/src/foo.ts","logLevel":20,"namespace":"jest-transformer","package":"ts-jest","transformOptions":{"config":{"automock":false,"browser":false,"cache":true,"cacheDirectory":"/private/var/folders/cl/yqb0znw56dj17l06y_45rzx40000gn/T/jest_dx","clearMocks":false,"coveragePathIgnorePatterns":["/node_modules/"],"cwd":"/Users/mattiask/code/open-source/temp/jest-error-type-issue/does-not-work","detectLeaks":false,"detectOpenHandles":false,"errorOnDeprecated":false,"extraGlobals":[],"forceCoverageMatch":[],"globals":{},"haste":{"computeSha1":false,"providesModuleNodeModules":[],"throwOnModuleCollision":false},"moduleDirectories":["node_modules"],"moduleFileExtensions":["js","json","jsx","ts","tsx","node"],"moduleNameMapper":[],"modulePathIgnorePatterns":[],"name":"065014f8040ebe756b2da8ebc80e3c0a","prettierPath":"prettier","resetMocks":false,"resetModules":false,"restoreMocks":false,"rootDir":"/Users/mattiask/code/open-source/temp/jest-error-type-issue/does-not-work","roots":["/Users/mattiask/code/open-source/temp/jest-error-type-issue/does-not-work"],"runner":"jest-runner","setupFiles":[],"setupFilesAfterEnv":[],"skipFilter":false,"snapshotSerializers":[],"testEnvironment":"/Users/mattiask/code/open-source/temp/jest-error-type-issue/does-not-work/node_modules/jest-environment-node/build/index.js","testEnvironmentOptions":{},"testLocationInResults":false,"testMatch":["**/__tests__/**/*.[jt]s?(x)","**/?(*.)+(spec|test).[tj]s?(x)"],"testPathIgnorePatterns":["/node_modules/"],"testRegex":[],"testRunner":"/Users/mattiask/code/open-source/temp/jest-error-type-issue/does-not-work/node_modules/jest-jasmine2/build/index.js","testURL":"http://localhost","timers":"real","transform":[["^.+\\.tsx?$","/Users/mattiask/code/open-source/temp/jest-error-type-issue/does-not-work/node_modules/ts-jest/dist/index.js",{}]],"transformIgnorePatterns":["/node_modules/"],"watchPathIgnorePatterns":[]},"instrument":false,"rootDir":"/Users/mattiask/code/open-source/temp/jest-error-type-issue/does-not-work","supportsDynamicImport":false,"supportsStaticESM":false},"transformerId":1,"version":"25.5.1"},"message":"computing cache key for /Users/mattiask/code/open-source/temp/jest-error-type-issue/does-not-work/src/foo.ts","sequence":17,"time":"2020-05-15T16:59:55.178Z"}
envinfo
System:
OS: macOS Catalina v10.15.4
Npm packages:
jest: v25.5.4
ts-jest: v25.5.1
typescript: v3.9.2
This one might be related to your case https://github.com/microsoft/TypeScript/pull/37239
In general, ts-jest uses typescript api to read tsconfig and resolves the config to fetch to typescript compiler. In your case, it cannot find enough configs to make tests pass.
Your case is similar to #766
Thanks, that did the trick!
@FantasticFiasco what did the trick? I couldn't see any actionables in the two links that @ahnpnl posted. I'm trying to leverage the new "Solution Style" config as introduced in https://github.com/microsoft/TypeScript/pull/37239, so I have a tsconfig.json file that looks like this:
{
"files": [],
"references": [
{ "path": "./tsconfig.foo.json" },
{ "path": "./tsconfig.bar.json" }
]
}
But when I run my tests they all fail because ts-jest doesn't seem to recognize that the compiler options for each test should be governed by one of tsconfig.foo.json or tsconfig.bar.json.
@ryami333 As I recall the problem was with tsconfig.json, I thought that all projects would pick up the configuration from the file in repo root, but that was not the case. Please see https://github.com/FantasticFiasco/axis-maintenance-js for a project that is using TypeScript and project references successfully.
@FantasticFiasco that repo uses a workaround in jest.config.js to get around the fact that ts-jest does not automatically inherit the correct TS config for any given file:
globals: {
"ts-jest": {
tsConfig: "tsconfig-base.json",
},
},
But that is a workaround which indicates an underlying bug, and not one that can be applied to my setup either. It's frustrating to see this and other similar issues (such as https://github.com/kulshekhar/ts-jest/issues/766) be closed when they're still reproducible, simply because some people have found a workaround which works for them.
Note that if you are using TypeScript 3.9 solution style, you might run into issue. By default when no tsconfig specified for ts-jest, root tsconfig.json will be picked up.
It is recommended to specify which tsconfig to be picked up via option tsConfig in jest configuration.
No that's my point exactly: the root tsconfig.json file is a valid config target. That's basically one of the motivations of "Solution Style" configs (per microsoft/TypeScript#37239) in the first place, so that tools like this (and IDE's linter plugins etc) which derive their entire project-config from a single root tsconfig.json file can have different settings for different files/directories.
Now, for example, I can have different configs for my foo and bar directories, and my IDE is informed of whether to use the config in tsconfig.foo.json or tsconfig.bar.json, not because I explicitly told it so, but because it can derive that information from a single root tsconfig.json file.
Otherwise I have to run jest + ts-jest once for my foo directory (with foo.config.json) and once more for my bar directory (with bar.config.json). Despite the fact that running tsc -b --project=tsconfig.json compiles correctly etc.
I was thinking about a new feature that is similar to https://github.com/angular/angular/pull/38003 which will show a warning message when encountering solution style, then automatically fallback to either tsconfig.test.json or tsconfig.spec.json (the most 2 common ways of naming tsconfig for test). If cannot find these 2, fallback to the current logic.
Running jest is a bit different from tsc so some behaviors of tsc are difficult to implement when combining with jest.
It's frustrating to see this and other similar issues (such as #766) be closed when they're still reproducible, simply because some people have found a workaround which works for them.
I'll try not to frustrate you next time š
You have a collaborator in the thread, I'm sure that given the correct argumentation the issue can be re-opened. Don't you think?
I think ts 3.9 has an API for solution style and project references. We can reopen and work on this
~@ahnpnl you're focussing on the use-case where someone has different config for their tests as they do for their source files, which is subtly different from the classic "Solution Style" case where the project has 2-or-more sub-projects (such as my foo and bar example above) with entirely different configuration requirements (and the tests may or may not have different config still). So if the plan is simply to publish a warning message when users have Solution configs, then does that mean that you simply do not support this valid Typescript setup pattern? In the case of Angular, a framework with an opinionated structure, I can see why this might be appropriate, but less so here.~
EDIT: I posted this right before seeing your message about the new API in 3.9 š„³
Sorry @FantasticFiasco, on reflection I can see that came across as abrasive.
Appreciate some ideas to tackle this. I took a quick look at the PR https://github.com/microsoft/TypeScript/pull/37239 and saw some discussions about a function but not quite sure.
I can help with a repro if nothing else, I'll look into that now.
There we go :)
https://github.com/ryami333/ts-jest-solution-repro
Correct me if Iām wrong. The expectation is test files should be run according to the project config of the project they belong to. For example:
- Project A has
tsconfig-a.json - Project B has
tsconfig-b.json
When running tests from root (which runs all tests for all projects), all tests in project A should be run with tsconfig-a.json and all tests in project B should be run with tsconfig-b.json ?
Quite struggling to find the right approach here .
Hi @orta can you help us a bit about this ?
If my understanding is correct, then yes. It seems that the tsc includes and files keys in tsconfig-a.json and tsconfig-b.json determine which configs apply to which files, which make sense. There are edge-cases though of course, such as files which match includes (or files) in multiple configs, and I suppose ts-jest should just try and match tsc's behaviour in these cases as much as possible.
I happen to know that the Sublime Text Editor's Typescript plugin already seems to be able to apply the correct config for any given file in a Solutions config context. I don't yet know whether this is a feature which they actively developed into the plugin, or whether they just defer to the typescript server's internal getConfigFileNameForFile method.
I am trying to figure out the best approach for this as well. I have two main strategies
- Single jest project with a tsconfig.test.json which includes source files for all projects and module mappings so it can resolve the project references. This side steps project references entirely and I am having better luck with this approach
- Jest projects with module mappings, with module paths it seems to work if I run tsc -b before to ensure the referenced projects are up to date. ie
modulePaths: ['<rootdir>/../src/shared']
Both strategies are valid I think but expect quite different behaviors from ts-jest. I think the mapper is an ok solution for option 1 because the intention is that jest references the source files across multiple TypeScript projects.
I think the experience here could be greatly improved if ts-jest resolves a solution tsconfig then assume approach 1 and just use a default tsconfig with sensible defaults for a new project created by TS 3.9 (if you are using a solution tsconfig it's a reasonably up to date project), this tsconfig should automatically have module mappings to be able to resolve the source files for each TypeScript project referenced by the solution tsconfig
Number 2 is where it gets hard.
Lets say there are 2 jest projects referenced by the root jest config and a shared library, when jest goes to run the tests it needs to ensure the shared library is compiled by typescript (does the TypeScript program handle this sort of concurrency?), once it is compiled then each jest project should be able to resolve the shared module using the main entry in package.json
I think solutions to both can be shipped in ts-jest, but these two approaches should be separated from a solution point of view.
@JakeGinnivan thanks a lot for the brainstorming help. I'd like to separate the 2 terms here: jest projects vs TypeScript project references to deal with them separately. I agree that in the end, ts-jest should support both.
Overall, these 2 work almost similar. However, the way ts-jest works based on how jest executes tests. Therefore, actually point 1 should be a bit more details besides your explanation above:
- Single jest project with a tsconfig.test.json which includes source files for all projects and module mappings so it can resolve the project references. This side steps project references entirely and I am having better luck with this approach Both strategies are valid I think but expect quite different behaviors from ts-jest. I think the mapper is an ok solution for option 1 because the intention is that jest references the source files across multiple TypeScript projects.
I think the experience here could be greatly improved if ts-jest resolves a solution tsconfig then assume approach 1 and just use a default tsconfig with sensible defaults for a new project created by TS 3.9 (if you are using a solution tsconfig it's a reasonably up to date project), this tsconfig should automatically have module mappings to be able to resolve the source files for each TypeScript project referenced by the solution tsconfig
ts-jest still needs to behave like tsc -b:
-
It needs to gather compiler options from each individual
tsconfig.jsonlocates in each referenced project (defined inprojectReferences). -
Detect the current processing file belongs to which referenced projects, then compile using the correct referenced project's compiler options.
-
moduleNameMapperis a sort of partially related to this topic. Once the compiling with correct referenced project's compiler options is solved,moduleNameMapperwill be easier to solve.
Number 2 is where it gets hard.
Lets say there are 2 jest projects referenced by the root jest config and a shared library, when jest goes to run the tests it needs to ensure the shared library is compiled by typescript (does the TypeScript program handle this sort of concurrency?), once it is compiled then each jest project should be able to resolve the shared module using the
mainentry in package.jsonI think solutions to both can be shipped in ts-jest, but these two approaches should be separated from a solution point of view.
In this scenario, jest will execute tests for each project using that project's jest config associated with ts-jest config.
I did a small test for this scenario. The project I have contains project-1, project-2 and share. project-1 and project-2 are jest projects and don't have projectReferences in each tsconfig.json. share is just a normal directory to contain shared codes. Here are how things execute:
-
Jest run tests for
project-1.ts-jestcompiles all the files needed inproject-1for test run, include file inshareas well. Thetsconfig.jsonofproject-1is used. -
Jest run tests for
project-2.ts-jestcompiles all the files needed inproject-2for test run, include file inshareas well. Thetsconfig.jsonofproject-2is also used.
This means when using jest config projects, jest will automatically create different instance of the ts-jest. So regardless using projects or not using projects, jest guarantee that tests in each project run in an sandbox environment. Therefore we only need to take care of resolving correct tsconfig.json.
If tsconfig.json from project-1/project-2 contains projectReferences to somewhere, ts-jest also needs to do the same like scenario 1, which also looks for tsconfig.json of those referenced projects and compile files with the correct compiler options.
Currently, jest transformer doesn't support async, neither TypeScript Program, everything is processed on demand and sequentially.
Could anybody check if I am dealing with the same issue here? I am not entirely sure if it's the same problem or if i have made some kind of configuration mistake (in the linked repo). I would really appreciate your help!
I am going through exactly what @ahnpnl described below. I am posting my use case here in case it is helpful.
ts-jest still needs to behave like tsc -b:
It needs to gather compiler options from each individual tsconfig.json locates in each referenced project (defined in projectReferences).
Detect the current processing file belongs to which referenced projects, then compile using the correct referenced project's compiler options.
moduleNameMapper is a sort of partially related to this topic. Once the compiling with correct referenced project's compiler options is solved, moduleNameMapper will be easier to solve.
I have one monorepo with a package called common and one called ui. The common package uses absolute imports inside of itself. The ui uses the common package, and has some tests inside that depends on common.
When I try to run the tests from ui package, it is unable to understand the absolute imports that the common package uses inside itself. If common's tsconfig was properly read, I guess it would work.
Same issue here..
This workaround https://github.com/FantasticFiasco/axis-maintenance-js doesn't work if you have namespace/interface augmentation..
Test passes but failed when collecting coverage report Failed to collect coverage
Not sure if this is the same use case as you all, but in my ts monorepo example in these test scripts you can see how I have to do for example:
"test": "ttsc -b tsconfig.test.json && jest",
instead of just "test": "jest"
This is because this project (webapi) depends on lib via ts project references, but whatever ts-jest is doing under the hood it does not seem to be behaving like the tsc -b counterpart thus it is not implicitly building the dependencies. Therefore, I must build these dependencies myself which is not ideal.
@rhyek same.
if i run yarn workspace subproject1 build, and then in the subproject1 folder's package.json i define build to do a tsc -b tsconfig.subproject1.json, where this tsconfig contains references to subproject2, which has its own package.json and tsconfig etc, then, everything compiles, all the dist folders are there, and my jest tests, located in subproject1, work just fine.
if i do not build myself, then it complains Cannot find module 'subproject1' or its corresponding type declarations., regardless of whether or not I attempt to use moduleNameMapper in subproject1's jest config.
So, bottom line is that it seems that ts-jest is not doing what tsc -b would do, so when in watch mode, i really suffer.
I have the exact same issue as @tommysullivan described. ts-jest should build like tsc- b
I want to make clear something: ts-jest cannot behave like TypeScript tsc -b. The reason ists-jest is a Jest transformer. We only transpile ts to js based on what jest-runtime passes in. This processing way is different from tsc -b where tsc controls the whole project.
We can still support project references but maybe only partially and definitely won't be exact the same like tsc -b does. In the meantime, you can try
- Combine with https://jestjs.io/docs/en/configuration#projects-arraystring--projectconfig (if you are using monorepo structure)
- If you are not using monorepo structure, I'd recommend to configure
moduleNameMapperto suit your needs.
I hope this makes clear to everyone the difference between jest + ts-jest vs TypeScript tsc and your expectations.
Hi @ahnpnl thanks for the clarification. The DX isn't great in that common scenario.
Hi,
So after spending several hours on this - I was able to make this work in my monorepo. This is my jest.config.ts, hopefully it helps others here:
import { defaults as tsjPreset } from 'ts-jest/presets'
import type { Config } from '@jest/types'
import type { InitialOptionsTsJest } from 'ts-jest/dist/types'
// there are some typing issues in the jest/types library we fix here
type JestConfig = Omit<InitialOptionsTsJest, 'projects'> & {
projects?: Partial<
| (Omit<Omit<Config.ProjectConfig, 'transfrom'>, 'moduleNameMapper'> & {
transform: any
moduleNameMapper: Record<any, any>
})
| Config.Glob
>[]
}
const config: JestConfig = {
preset: 'ts-jest',
collectCoverageFrom: ['packages/**/src/**/*.(ts|tsx)'],
coverageReporters: ['lcov', 'text'],
cacheDirectory: '.jest/cache',
projects: [
{
testMatch: ['<rootDir>/packages/frontend/tests/**/*.(spec|test).*'],
displayName: { name: 'Frontend', color: 'cyan' },
testEnvironment: 'jsdom',
transform: tsjPreset.transform,
globals: {
'ts-jest': {
tsconfig: {
noPropertyAccessFromIndexSignature: false,
},
},
},
moduleNameMapper: {
'\\.(css|less|scss|sass)$': 'identity-obj-proxy',
},
},
{
testMatch: ['<rootDir>/packages/backend/tests/**/*.(spec|test).*'],
displayName: { name: 'Backend', color: 'blue' },
testEnvironment: 'node',
transform: tsjPreset.transform,
},
],
}
export default config
Note - what did the trcik as specifying transform for each project, and not having a top level testRegex etc. Also, although I have tsconfig.json files in each sub-project that extend the baseline one at the monorepo's root, there doesn't appear to be a way to force ts-jest to actually pick these up correctly. Hence the need to override the globals.
I'm also running into this problem. All my tests are failing after migrating to TypeScript project references. It would be nice to have a fix for this.
You can use extends in tsconfig.json in your tests directory to resolve it, like this:
- /tests/tsconfig.json:
{
"extends": "../tsconfig.json"
}
depending on what underlying typescript api ts-jest is using, some flag is probably needed to be specified in the jest config, similar to what the tsc --build flag essentially enforces.
e.g. ts-loader for webpack provides a config setting for this: projectReferences: true
issue: https://github.com/TypeStrong/ts-loader/issues/1005
PR: https://github.com/TypeStrong/ts-loader/pull/935