0

Issue on facebook/jest

Currently working on a project where I need to dynamically load other components in the main application system, I'm facing an issue with some error that throws a Jest Invariant constraint violation when trying to import dynamically a module.

An example is accessible at https://gitlab.com/matthieu88160/stryker-issue

In this project, I have a class used to load a component and another that will use the ComponentLoader and add some check and post-processing (this part of the code is not present in the accessible project). In the GitLab job, two test suites are executed and one fails. My problem is that the two test suites are exactly the same, obviously, I copy-pasted the first one to create. The second one and by the way demonstrate the issue.

I already tried some workaround found on the web without any success.

Here is the code of the component I try to test :

import {existsSync} from 'fs';
import {format} from 'util';

export default class ComponentLoader
{
    static load(file) {
        if(existsSync(file)) {
            return import(file);
        }

        throw new Error(
            format('Component file "%s" does not exist. Cannot load module', file)
        );
    }
}

And the test itself:

import {describe, expect, test} from '@jest/globals';
import {format} from 'util';
import {fileURLToPath} from 'url';
import {dirname} from 'path';
import mock from 'jest-mock';

describe(
    'ComponentLoader',
    () => {
        describe('load', () => {
            test('load method is able to load a component from a file', () => new Promise(
                resolve => {
                    Promise.all(
                        [import('../src/ComponentLoader.mjs'), import('./fixture/component/A1/component.fixture.mjs')]
                    ).then(modules => {
                        const file = format(
                            '%s/./fixture/component/A1/component.fixture.mjs',
                            dirname(fileURLToPath(import.meta.url))
                        );

                        modules[0].default.load(file).then(obj => {
                            expect(obj).toBe(modules[1]);
                            resolve();
                        });
                    });
                })
            );
        });
    }
);

And here the error report:

PASS test/ComponentLoaderA.test.mjs
FAIL test/ComponentLoaderB.test.mjs
  ● ComponentLoader › load › load method is able to load a component from a file
      15 |     static load(file) {
      16 |         if(existsSync(file)) {
    > 17 |             return import(file);
         |             ^
      18 |         }
      19 | 
      20 |         throw new Error(
      at invariant (node_modules/jest-runtime/build/index.js:2004:11)
      at Function.load (src/ComponentLoader.mjs:17:13)
      at test/ComponentLoaderB.test.mjs:21:44

The interesting element from my point of view is the fact the ComponentLoaderA.test.mjs and the ComponentLoaderB.test.mjs are exactly the same.

The full error trace I found is:

CComponentLoader load load method is able to load a component from a file
        Error:
    at invariant (importTest/.stryker-tmp/sandbox7042873/node_modules/jest-runtime/build/index.js:2004:11)
    at Runtime.loadEsmModule (importTest/.stryker-tmp/sandbox7042873/node_modules/jest-runtime/build/index.js:534:7)
    at Runtime.linkModules (importTest/.stryker-tmp/sandbox7042873/node_modules/jest-runtime/build/index.js:616:19)
    at importModuleDynamically (importTest/.stryker-tmp/sandbox7042873/node_modules/jest-runtime/build/index.js:555:16)
    at importModuleDynamicallyWrapper (internal/vm/module.js:443:21)
    at exports.importModuleDynamicallyCallback (internal/process/esm_loader.js:30:14)
    at Function.load (importTest/.stryker-tmp/sandbox7042873/src/ComponentLoader.mjs:89:11)
    at importTest/.stryker-tmp/sandbox7042873/test/ComponentLoaderB.test.mjs:22:37
    at new Promise (<anonymous>)
    at Object.<anonymous> (importTest/.stryker-tmp/sandbox7042873/test/ComponentLoaderB.test.mjs:14:79)

It seems the error does not have any message.

Further information from the jest-runtime investigation :

It seems that between the tests, the sandbox context is lost for a reason I cannot be able to manage to find at the moment.

In node_modules/jest-runtime/build/index.js:2004:11 : The condition is NULL, the error is then thrown without a message.

function invariant(condition, message) {
  if (!condition) {
    throw new Error(message);
  }
}

In node_modules/jest-runtime/build/index.js:534:7 : The context is NULL, and no message given, creating my empty error message.

const context = this._environment.getVmContext();

invariant(context);

The toString() of this._environment.getVmContext method as follow:

getVmContext() {
    return this.context;
}

The current _environment presents a null context :

NodeEnvironment {
  context: null,
  fakeTimers: null,
  [...]
}

The deeper point I can reach is this code where the context appear to be null :

const module = new (_vm().SourceTextModule)(transformedCode, {
  context,
  identifier: modulePath,
  importModuleDynamically: (specifier, referencingModule) => {
    return this.linkModules(
      specifier,
      referencingModule.identifier,
      referencingModule.context
    )},

  initializeImportMeta(meta) {
    meta.url = (0, _url().pathToFileURL)(modulePath).href;
  }
});

The context variable is not empty and _vm().SourceTextModule is a class extending Module. I can notice in the importModuleDynamically execution using console.log(this._environment) that the context is currently null.

  • 1
    Remember that your post is where all the details go: if you have code that's going, [it's on you](/help/how-to-ask) to reduce that code to a [mcve] so you can put that in your post. – Mike 'Pomax' Kamermans Dec 11 '20 at 23:45
  • Avoid the [`Promise` constructor antipattern](https://stackoverflow.com/q/23803743/1048572?What-is-the-promise-construction-antipattern-and-how-to-avoid-it)! Shouldn't affect the test though. – Bergi Dec 12 '20 at 09:53
  • What exactly is the error message from the violated invariant? – Bergi Dec 12 '20 at 09:53
  • Agree @Bergi, the Promise constructor here was a workaround I found and tried, currently present in the example code but is not the current project code in the test. Just edited the question with full error trace. – LeviathanCalumet Dec 12 '20 at 10:11
  • "*It seams the error does not have any message.*" - oh my, indeed. That means you'll need to study line 2004 of `node_modules/jest-runtime/build/index.js` to figure out what's happening there – Bergi Dec 12 '20 at 10:26
  • It was my fear :D – LeviathanCalumet Dec 12 '20 at 10:44
  • Hi @LeviathanCalumet, did you find a solution? I have the same error now. – Jan Bouchner Mar 03 '21 at 20:47

0 Answers0