chaijs / chai

BDD / TDD assertion framework for node.js and the browser that can be paired with any testing framework.
https://chaijs.github.io
MIT License
8.11k stars 694 forks source link

v5 - `Unknown file extension ".ts"` when running with mocha #1568

Closed GuyKh closed 7 months ago

GuyKh commented 8 months ago

In my tests, I only use import { expect } from "chai".

npm run test (mocha) returns:

TypeError: Unknown file extension ".ts" for /Users/me/git/myProj/test/app.test.ts
    at Object.getFileProtocolModuleFormat [as file:] (node:internal/modules/esm/get_format:160:9)
    at defaultGetFormat (node:internal/modules/esm/get_format:203:36)
    at defaultLoad (node:internal/modules/esm/load:143:22)
    at async ModuleLoader.load (node:internal/modules/esm/loader:409:7)
    at async ModuleLoader.moduleProvider (node:internal/modules/esm/loader:291:45)
    at async link (node:internal/modules/esm/module_job:76:21)

This wasn't happening before the change with "chai": "4.3.10"

Other files:

GuyKh commented 8 months ago

Failing PR https://github.com/GuyKh/10bis.Slackbot/pull/1021

MuTsunTsai commented 8 months ago

Having the same issue here. Need to stay in v4 for now.

43081j commented 8 months ago

this is likely because of esm vs commonjs.

your project builds as commonjs ("module": "commonjs" in tsconfig), while chai 5.x is pure ESM.

i would stick on chai 4.x until you can move your project to es modules instead (i.e. "module": "nodenext" in tsconfig, and "type": "module" in package.json).

GuyKh commented 8 months ago

@43081j - does this mean that chai 5.x will not run on anything but ESM? Also - a tutorial in terms of how to migrate to ESM?

43081j commented 8 months ago

@GuyKh i put a little more info in #1561 at the end

basically, chai 5.x ships as ESM, which means you can only use it if one or more of the following is true:

many projects will be simple to migrate to ESM, especially those used only in node. i don't have a tutorial but if i find a good example repo to migrate, i'd be happy to write up how i did it.

importantly too, it is fine to stick with chai 4.x for now

DarioMagniPLRM commented 8 months ago

my tsconfig.json: "target": "es2022" "module": "commonjs"

After some testing, I can confirm that

It would be useful to add type definitions to all plugins and update the documentation, which currently no longer conforms to the latest breaking changes (i.e.: https://www.chaijs.com/plugins/chai-json/).

Stwissel commented 8 months ago

The problem with Mocha runs a little deeper than changing your project to module. This part in the .mocharc.json points to the issue:

{
"require": ["ts-node/register", "chai/register-expect.js"],
}

Mocha seems to use require under the hood, which won't work with 5.0. The Mocha team might have advice?

43081j commented 8 months ago

mocha supports ESM just fine, you just need to do this instead:

{
  "loader": "ts-node/esm",
  "require": ["chai/register-expect.js"]
}

some more info on that here:

https://typestrong.org/ts-node/docs/recipes/mocha/

i'll try put a few examples together tonight if i get time, as this mostly seems like gaps in understanding than limitations

MuTsunTsai commented 8 months ago

After several days of playing around, I finally found a solution to use chai v5 with the rest of established toolchain (mocha + ts-node + istanbul).

The trick is NOT to import expect (or any other stuffs) from chai at all in any of the test/spec files. Instead, register the things you need as global members:

// mocha.env.mjs
import { Assertion, expect } from "chai";

globalThis.Assertion = Assertion;
globalThis.expect = expect;

// these are for ts-node
process.env.NODE_ENV = "test";
process.env.TS_NODE_PROJECT = "test/tsconfig.json";

(Note that chai/register-expect.js is doing similar thing and you can use that one also. I wrote my own because I also need Assertion static object to define custom chai methods.)

And then add it to your .mocharc.json, in my case it looks like this:

{
    "extension": [
        "ts"
    ],
    "spec": [
        "test/specs/**/*.ts"
    ],
    "timeout": "0",
    "require": [
        "./test/mocha.env.mjs",
        "ts-node/register",
        "tsconfig-paths/register"
    ]
}

Next you just remove all import { expect } from "chai" in the test files. Of course, you would also need to define the global functions to silence TypeScript errors:

// chai.d.ts
import type * as chai from "chai";

declare global {
    declare const expect: typeof chai.expect;
    declare const Assertion: typeof chai.Assertion;
}

And that's all you need. No need to change tsconfig.json or package.json. Yes, there are other solutions that make chai v5 works by modifying these two, but those solutions break other toolchains (especially istanbul, and I don't want to use c8).

43081j commented 7 months ago

you shouldn't need to do that, we use mocha 10, chai 5, and typescript with regular imports.

i also tried using ts-node with the ts-node/esm loader and it worked fine.

i suspect something must be missing if you need to avoid importing it like that

43081j commented 7 months ago

here's an example of mocha 10 + chai 5 + ts-node:

https://gist.github.com/43081j/78ce1392abb5043b02a29355006880a5