Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Question: Jest + Typescript + threads.js how can it work together ? #269

Open
DaniGTA opened this issue Jun 25, 2020 · 15 comments
Open

Question: Jest + Typescript + threads.js how can it work together ? #269

DaniGTA opened this issue Jun 25, 2020 · 15 comments
Labels

Comments

@DaniGTA
Copy link

DaniGTA commented Jun 25, 2020

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
};

@andywer
Copy link
Owner

andywer commented Jun 25, 2020

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?

@DaniGTA
Copy link
Author

DaniGTA commented Jun 25, 2020

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.

@andywer
Copy link
Owner

andywer commented Jun 26, 2020

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)

@DaniGTA
Copy link
Author

DaniGTA commented Jun 26, 2020

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"
  ]
}

@andywer
Copy link
Owner

andywer commented Jun 26, 2020

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.

@crubier
Copy link

crubier commented Sep 19, 2020

Is there a solution to this? I am on the same problem right now

@TeoTN
Copy link

TeoTN commented Sep 29, 2020

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

@TheoXD
Copy link

TheoXD commented Nov 28, 2020

Just ran into the same issue, typescript workers don't play well with jest.

@nassissme
Copy link

Same problem for me, is documentation available?

@andywer
Copy link
Owner

andywer commented Dec 23, 2020

Sorry, got no solution for Jest ready yet. If anyone got an idea, please share!

@TheoXD
Copy link

TheoXD commented Dec 24, 2020

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

@MattGson
Copy link

MattGson commented May 28, 2021

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).

@mfrener
Copy link

mfrener commented Jan 5, 2022

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.

@standbyoneself
Copy link

Using ts-node slows down running my tests to 3x. So is there another way?

@solorhyme0109
Copy link

It seems that ts-jest can not recognize the script imported by worker. So, just write the script as normal commonjs module and it works.

const { expose } = require("threads/worker");
const json_parse = require("./parse");
expose(json_parse({ storeAsString: true }));

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

10 participants