wuliwong
wuliwong

Reputation: 4368

Resolving imports using webpack's worker-loader in Jest tests

I'm writing a Jest test and am having trouble resolving this import which uses webpack's worker-loader

import ImageInfoWorker from 'worker-loader?name=image_info!@WORKERS/image-info';

I have some other aliases which are resolving properly in my tests, such as:

import Store from '@SUPPORT/store';
import * as api from '@SUPPORT/api';

Here is the relevant snippet from package.json

  "jest": {
    "moduleFileExtensions": ["js", "jsx"],
    "moduleNameMapper": {
      "^@CSS/(.*)$": "<rootDir>/css/$1",
      "^@COMPONENTS/(.*)$": "<rootDir>/js/components/$1",
      "^@MODELS/(.*)$": "<rootDir>/js/models/$1",
      "^@STORES/(.*)$": "<rootDir>/js/stores/$1",
      "^@SUPPORT/(.*)$": "<rootDir>/js/support/$1",
      "^(.*?)@WORKERS/(.*)$": "$1<rootDir>/js/workers/$2"
  }
}

And here is the resolve section of my webpack config:

        resolve: {
          extensions: ['.js', '.jsx'],
          modules: [process.env.NODE_PATH, 'node_modules'],
          alias: {
            '@CSS':        path.join(projectRoot, 'css'),
            '@COMPONENTS': path.join(projectRoot, 'js', 'components'),
            '@MODELS':     path.join(projectRoot, 'js', 'models'),
            '@STORES':     path.join(projectRoot, 'js', 'stores'),
            '@SUPPORT':    path.join(projectRoot, 'js', 'support'),
            '@WORKERS':    path.join(projectRoot, 'js', 'workers')
        }
      },

Upvotes: 22

Views: 8256

Answers (5)

postolka
postolka

Reputation: 1

I had similar issue with html-loader. Simplest solution for me was to just mock the import with virtual one:

jest.mock('worker-loader?name=image_info!@WORKERS/image-info', () => ({
  default: {
    // ... mock worker here
  }
}), { virtual: true });

Upvotes: 0

n.sh
n.sh

Reputation: 384

This solution worked for me.

Create a workerMock.js file with mocking implementation you need in a folder called __mocks__(of course you can create it anywhere you like):

module.exports = Object.create(null);

Then in your jest config add configuration below:

"moduleNameMapper": {
  "\\.worker.js":"<rootDir>/__mocks__/workerMock.js"
}

Upvotes: 4

jczaplew
jczaplew

Reputation: 1753

If you don't need the path to be resolved, you can use "moduleNameMapper" to ignore the import.

First, create an empty module that contains export default ''.

Next, add the following to your package.json:

"jest": {
    "moduleNameMapper": {
        "^worker-plugin/loader.+$": "<rootDir>/EmptyModule"
    }
}

Upvotes: 6

Adam Jagosz
Adam Jagosz

Reputation: 1594

This approach worked for me with both inline-style and config-style worker imports.

Webpack-bundled WebWorkers are not suported by Jest yet (not that I know of), so you have to mock the worker. Simply extract the functionality of your worker to an external file, and in the worker file perform just the worker-y bits.

@WORKERS/imageInfo.js — the “meat” of your worker:

export default imageInfoFunction(data) {
    //...
}

@WORKERS/imageInfo.worker.js, the worker gravy:

import imageInfoFunction from "./imageInfo";

self.onmessage = async function (e) {
  self.postMessage(imageInfoFunction(e.data));
};

This way you can mock just the Worker part of your implementation, while testing the actual functionality:

mocks/imageInfo.worker.js

import imageInfoFunction from "@WORKERS/imageInfo";

export default class ImageInfoWorker {
  constructor() {
    // should be overwritten by the code using the worker
    this.onmessage = () => { };
  }

  // mock expects data: { } instead of e: { data: { } }
  postMessage(data) {
    // actual worker implementation wraps argument into { data: arg },
    // so the mock needs to fake it 
    this.onmessage({ data: imageInfoFunction (data) });
  }
}

Now in jest.config.js:

module.exports = {
  moduleNameMapper: {
    "@WORKERS/(.*\\.worker\\.js)$": "<rootDir>/mocks/$1",
    "@WORKERS/(.*)$": "<rootDir>/js/workers/$1",
  },
};

Note I didn't include the inline worker-loader config, but I skipped the ^(.*). This works because we don't need worker-loader anymore since we are mocking the worker. The first path is for .worker.js files that we want to mock and the other is for actual functionality that we want to test. The following would also work:

    "^(.*?)@WORKERS/(.*\\.worker\\.js)$": "<rootDir>/mocks/$2",
    "^(.*?)@WORKERS/(.*)$": "<rootDir>/js/workers/$2",

This solution could probably be generalized so that all workers are mocked at once, suggestions welcome.

Upvotes: 1

Pavlo D
Pavlo D

Reputation: 356

According to cpojer web-workers are not supported in Jest. You should use mocking, read more here

Upvotes: 2

Related Questions