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

Add docs for using testdouble when we have to use another loader (ie ts-node/esm) #514

Open
3 of 13 tasks
codan84 opened this issue May 4, 2023 · 8 comments
Open
3 of 13 tasks

Comments

@codan84
Copy link

codan84 commented May 4, 2023

Description

I am already running my tests with --loader=ts-node/esm to handle TypeScript to ESM compilation and writing my tests in TypeScript.
How can I now plug in Testdouble to be able to call replaceEsm in my tests? An example of this would be very useful.

From what I can see we can specify multiple loaders, but I have failed to find the right way to do it:
https://nodejs.org/docs/latest-v16.x/api/esm.html#loaders

(I am, in particular, using Ava)

Issue

It is not clear how to use testdouble with --loader=ts-node/esm

Environment

  • node -v output: v16.13.2
  • npm -v (or yarn --version) output:
  • npm ls testdouble (or yarn list testdouble) version: 3.17.2

Failing Test

  • Fork the repo
  • Add a failing test (probably to the `/regression/src' directory)
  • Submit a pull request for the failing test or link to your branch here

Example Repo

Runkit Notebook

  • Create a Runkit notebook
  • Invoke var td = require('testdouble') at the top
  • Verify the behavior your issue is concerned with by clicking "Run"
  • Link to the Runkit here

Code-fenced Examples

var td = require('testdouble')

// Your steps here.
@searls
Copy link
Member

searls commented May 5, 2023

cc/ @giltayar, our resident ESM czar. I know Node 20 causes problems if you happen to be on it

@giltayar
Copy link
Contributor

giltayar commented May 5, 2023

Try node --loader ts-node/esm --loader testdouble ...

I'm not sure when multiple loaders started getting supported, so maybe 16 does not support multiple, so if that doesn't work, try v18.

And as @searls DON'T use v20, as there are serious bugs there around loaders.

@codan84
Copy link
Author

codan84 commented May 5, 2023

@giltayar node documentation for v16 shows clearly support for multiple loaders. However - the docs are for v16.20.0 and I had v16.13.2.
I tried something similar to your suggestion (NODE_OPTIONS="--loader=testdouble --loader=ts-node/esm" npm run test) with node 18 and indeed I am not getting errors, but I also do not see mocking working (the original implementation gets called). You can see it here:
https://github.com/codan84/jest-ts-experiments/tree/ava-testdouble (mind the ava-testdouble branch)

The order of loaders is important and the loader passed last gets invoked first, thus I believe the order should be --loader=testdouble --loader=ts-node/esm.

Unsure why mocks do not work with the solution in my repo.

@giltayar
Copy link
Contributor

giltayar commented May 5, 2023

Thanks for the excellent reproduction! It really helped me figure things out.

First, how to make it work, and then why.

  1. Reverse the loaders. First testdouble then ts-node/esm
  2. In td.replaceEsm('../src/want-to-be-mocked.js'), use the .ts extension, i.e. td.replaceEsm('../src/want-to-be-mocked.ts')!

Now for the why:

  1. When chaining loaders, a loader can "short circuit" loaders after it , meaning: "ignore their result, use mine!". Unfortunately, ts-node/esm is always short circuiting, and not behaving like a good citizen (it even says so in the code). This is why I had to switch the order, to not allow it short circuit testdouble!

  2. Now for the interesting part: the ts-node/esm replaces .js extensions with the .ts extensions (when appropriate), so that what the testdouble loader "sees" is .ts extensions. Switching the replaceEsm to .ts makes this go away.

There is a LOT of confusion around .js/.ts in the TypeScript world, and this is a part of it.

@codan84
Copy link
Author

codan84 commented May 5, 2023

Thanks for a great explanation @giltayar .
I can confirm that with node v18.16.0 and running it like NODE_OPTIONS="--loader=ts-node/esm --loader=testdouble" npm run test mocks are working.
I will do a little bit more testing (ie can we add multiple loaders into ava config in package.json, and which version of node 16 things work with) and add it all up in here.
If you are ok with this I can also make a PR with docs/working example of this.

@codan84
Copy link
Author

codan84 commented May 5, 2023

I can confirm that adding ava config to package.json:

"ava": {
    "extensions": {
      "ts": "module"
    },
    "nodeArguments": [
      "--loader=ts-node/esm",
      "--loader=testdouble"
    ]
  }

and running tests like npm run test or yarn test works too (again, order of loaders in the array is important!).

@cspotcode
Copy link

On the linked issue, I have posted a brief explanation of why ts-node does not always short-circuit despite appearing like it does, and why its resolver needs to come last in the chain. (first in the args, last in the chain)
TypeStrong/ts-node#2008 (comment)

@codan84
Copy link
Author

codan84 commented Mar 13, 2024

by the way, for node 20 you have to switch ts-node/esm loader for tsimp/import:

export default {
  cache: false,
  failWithoutAssertions: false,
  extensions: {
    ts: 'module'
  },
  nodeArguments: [
    '--import=tsimp/import',
    '--import=testdouble'
  ]
}

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

No branches or pull requests

4 participants