Skip to content

Using testdouble esm loader with ts-node/esm loader on mocha leads to errors (Node 18.6.0) #496

@AlexZ-343

Description

@AlexZ-343

Description

I am trying to do esm mocking using testdouble with a mixed Typescript and Javascript project, mostly following this post

Node 18.6.0 now allows for chaining of loads, and I previously needed ts-node/esm in order to recognize my Typescript files and imports.

I realize that this new feature is less than a week old, but I would appreciate support with reconciling other loaders that I would need for my es6 project (or to find a way that only requires one loader). Due to project requirements, using commonjs on runtime is not an option.

Issue

When running the following mocha command:

NODE_OPTIONS='--experimental-specifier-resolution=node --loader=ts-node/esm --loader=testdouble' mocha test/.js test/.spec.ts

and any permutation thereof (removing experimental-specifier-resolution, changing the order of the --loader params)

I get the following exception

Exception in PromiseRejectCallback:
node:internal/modules/esm/loader:178
return output;
RangeError: Maximum call stack size exceeded
Exception in PromiseRejectCallback:
/Users/repos/moRepo/node_modules/ts-node/dist/esm.js:120
const { source: rawSource } = await defaultLoad(url, {
^
RangeError: Maximum call stack size exceeded
at validateArgs (node:internal/modules/esm/loader:578:26)

These 4 lines repeat indefinitely:

at addShortCircuitFlag (/Users/repos/myRepo/node_modules/ts-node/src/esm.ts:409:21)
at load (/Users/repos/myRepo/node_modules/ts-node/src/esm.ts:239:12)
at nextLoad (node:internal/modules/esm/loader:173:28)
at /Users/repos/myRepo/node_modules/ts-node/src/esm.ts:255:45

Environment

node v18.6.0
npm v.8.13.2
testdouble v.3.16.6

Example Repo

I don't have a repo handy, but I can try to create one and add it in the future.

Code-fenced Examples

service_one.ts


export function getAllInvalidObjects(connection: Connection) {
  
 const response: string[] = service_two.getData(someString);
 ... // some transformations of response
 return response;
}

service_two.ts

// this is the function I'd like to mock when testing service_one.ts
export function getData(someString) {
  axios.get('https://endpoint.com/path?string=' + someString).then((response: any) {
   resolve(response.json);
  }
}

test.ts :

import * as td from 'testdouble';

let mock_validation_rest: any;

beforeEach(async () => {
    mock_rest = await td.replaceEsm('../src/services/service_two.js');
});

afterEach(function () {
    sandbox.restore();
    td.reset();
});

it('successfully validates with mocking, async () => {
    td.when(mock_rest.prototype.getData(td.matchers.anything())).thenResolve(mockData);

    // as long as the mock above works then this test will pass
    return service_one.getAllInvalidObjects(connection).then((response: string[]) => {
      assert.equal(3, response.length);
    });
});

package.json:

    {
      "type": "module",
      "scripts": {
         "test": NODE_OPTIONS='--experimental-specifier-resolution=node --loader=ts-node/esm --loader=testdouble' mocha test/*.js test/*.spec.ts -r dotenv/config
      }
    }

tsconfig.json:

    {
      "compilerOptions": {
         "target": "es2016",
         "module": "es6,
         "moduleResolution": "node16"
         "allowJs": true,
         "esModuleInterop": true
      },
      "ts-node": {
         "esm": true
      }
      "include": [
         "./src/**/*",
         "test/**/*/.ts",
         "test/**/*.js"
      }
    }

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions