Question: Jest + Typescript + threads.js how can it work together ?
I have implemented threads.js in my "vue + typescript + electron" project and it works, but my test now fails with the error:
pinguSync\src\backend\controller\main-list-manager\worker\main-list-search-id-worker.ts:1
import { expose } from 'threads/worker';
^^^^^^
SyntaxError: Cannot use import statement outside a moduleJest
"worker.ts":
import { expose } from 'threads/worker';
import Series from '../../objects/series';
const worker = function findSeriesById(buffer: Series[], id: string): Series | null {
return buffer.find(x => x.id === id) ?? null;
};
export type MainListSearchIdWorker = typeof worker;
expose(worker);
"main":
public static async findSeriesById(id: string): Promise<Series | null> {
const worker = await spawn(new Worker('./worker/main-list-search-id-worker.ts'));
return worker(MainListManager.getMainList(), id);
}
"test":
describe('test fn: findSeriesById', () => {
test('should find Series by Id', async () => {
const series = new Series();
MainListManager['mainList'].push(series);
const result = await MainListSearcher.findSeriesById(series.id);
expect(result?.id).toBe(series.id);
});
test('should return on no result', async () => {
const result = await MainListSearcher.findSeriesById('id');
expect(result).toBeNull();
});
});
(You can turn Series to a object with just a id for reproducing):
{id:''}
JestConfig:
'use strict';
module.exports = {
'roots': [
'<rootDir>'
],
'testMatch': [
'**/test/**/*-(test|tests).+(ts|tsx|js)',
],
'transform': {
'\\.(gql|graphql)$': '@jagi/jest-transform-graphql',
'^.+\\.(ts|tsx)$': 'ts-jest'
},
'reporters': [
'default',
['jest-html-reporters', {
'publicPath': './html-report',
'filename': 'report.html',
'expand': true
}]
],
'testTimeout': 3500,
'verbose': false,
'globals': {
'ts-jest': {
babelConfig: true,
}
},
'testEnvironment': 'node',
'setupFilesAfterEnv': ['<rootDir>/test/test-helper.ts'],
'maxWorkers': 6
};
Hey @DaniGTA!
That's a pretty specific use case and I am not sure I am able to answer that, esp. since I don't do Vue. Your detailed description is a good starting point, though.
Have you tried this yet?
Sorry, I forgot to mention that my project is in vue and electron but it is separate from the tests. (Jest works without any electron or vue dependency (and threads.js works with electron+vue (thanks to webpack) but it cant get compiled in jest))
Jest only tests the parts of my program that are only related to typescript, everything else is getting mocked.
So puppeter will not help me in this case.
When i run the jest tests its like running plain typescript. (ts-node)
But i dont know how threads.js works without beeing compiled to js.
But i dont know how threads.js works without beeing compiled to js.
You would just use ts-node to run your program, the way you always do. Now when you try to spawn a TypeScript worker in node.js, threads.js will automatically set up the ts-node/register hook in the worker if ts-node is available.
I guess either that mechanic doesn't work in your case for some reason or maybe your TypeScript config (might be a different one for Jest than when running without Jest?) has compilerOptions.module set to es6 or esnext?
Or seen from a different angle: Do you use ES modules in node.js? (I just assumed you would use CommonJS modules)
has compilerOptions.module set to es6 or esnext?
esnext
Or seen from a different angle: Do you use ES modules in node.js?
CommonJS modules
Jest and Electron uses the same tsconfig:
tsconfig.ts:
{
"compilerOptions": {
"target": "es2019",
"module": "esnext",
"strict": true,
"jsx": "preserve",
"importHelpers": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"experimentalDecorators": true,
"emitDecoratorMetadata":true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"skipLibCheck": true,
"sourceMap": true,
"outDir": "out",
"types": [
"webpack-env",
"jest",
"node"
],
"lib": [
"esnext",
"es6",
"es7",
"dom",
"dom.iterable",
"scripthost"
]
},
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
"tests/**/*.ts",
"tests/**/*.tsx"
]
}
Then that's why it doesn't work: You live in a CommonJS world, but tell the TypeScript compiler to compile to ES modules. Don't know if Jest does some magic on top of that, so that your master thread code runs in the first place, but it cannot work like this.
There is one thing that needs to be fixed in threads.js, though: If you are using ES modules in node.js, you should be able to spawn a TypeScript worker ES module. I think this will break right now in the way that you showed as soon as ES modules and automatic ts-node/register come together.
Is there a solution to this? I am on the same problem right now
I'm having exactly the same issue with jest + ts-jest + ts-config... I can't really configure it so that it can import ts file for webworker... It'd be nice if the docs included an example repository with typescript + threads.js + jest configured
Just ran into the same issue, typescript workers don't play well with jest.
Same problem for me, is documentation available?
Sorry, got no solution for Jest ready yet. If anyone got an idea, please share!
I got it to work eventually quite a while ago and the thing that did the trick was to add this to tsconfig.json:
"ts-node": {
"compilerOptions": {
"module": "commonjs"
}
}
For anyone having this issue, I haven't found a true solution but I was able to get my other tests running by conditionally importing any modules using threads at runtime based on an environment variable. This let me exclude it in the tests.
I also found that setting verbose: false seemed to cause jest to log the error but continue running tests anyway. This did result in a large memory leak though (~500MB per test suite).
I faced same "SyntaxError: Cannot use import statement outside a modul" when running on Jest, but I was able solve it with
yarn add ts-node --dev
following the:
You would just use ts-node to run your program, the way you always do. Now when you try to spawn a TypeScript worker in node.js, threads.js will automatically set up the ts-node/register hook in the worker if ts-node is available.
Jest uses ts-jest for transpile while thread.js wants ts-node, so you need to install that in addition (ts-jest won't transpile files for thread.js, only ts-node suppored as mentioned above ) - maybe that's what is causing the confusion.
Using ts-node slows down running my tests to 3x. So is there another way?