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

import-first loading of test files #4635

Merged
merged 6 commits into from
May 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 3 additions & 10 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -1065,7 +1065,6 @@ Require a module before loading the user interface or test files. This is useful

- Test harnesses
- Assertion libraries that augment built-ins or global scope (such as [should.js][npm-should.js])
- Instant ECMAScript modules via [esm][npm-esm]
- Compilers such as Babel via [@babel/register][npm-babel-register] or TypeScript via [ts-node][npm-ts-node] (using `--require ts-node/register`). See [Babel][example-babel] or [TypeScript][example-typescript] working examples.

Modules required in this manner are expected to do work synchronously; Mocha won't wait for async tasks in a required module to finish.
Expand Down Expand Up @@ -2034,20 +2033,15 @@ this means either ending the file with a `.mjs` extension, or, if you want to us
adding `"type": "module"` to your `package.json`.
More information can be found in the [Node.js documentation](https://nodejs.org/api/esm.html).

> Mocha supports ES modules only from Node.js v12.11.0 and above. To enable this in versions smaller than 13.2.0, you need to add `--experimental-modules` when running
> Mocha. From version 13.2.0 of Node.js, you can use ES modules without any flags.
> (Mocha _will_ load ESM even in Node v10, but this is not officially supported. Use at your own risk.)

### Current Limitations

Node.JS native ESM support still has status: **Stability: 1 - Experimental**

- [Watch mode](#-watch-w) does not support ES Module test files
- [Custom reporters](#third-party-reporters) and [custom interfaces](#interfaces)
can only be CommonJS files
- [Configuration file](#configuring-mocha-nodejs) can only be a CommonJS file (`.mocharc.js` or `.mocharc.cjs`)
- When using module-level mocks via libs like `proxyquire`, `rewiremock` or `rewire`, hold off on using ES modules for your test files
- Node.JS native ESM support does not work with [esm][npm-esm] module
- When using module-level mocks via libs like `proxyquire`, `rewiremock` or `rewire`,
hold off on using ES modules for your test files. You can switch to using `testdouble`,
which does support ESM.

## Running Mocha in the Browser

Expand Down Expand Up @@ -2426,7 +2420,6 @@ or the [source](https://github.com/mochajs/mocha/blob/master/lib/mocha.js).
[npm]: https://npmjs.org/
[npm-babel-register]: https://npm.im/@babel/register
[npm-chai-as-promised]: https://www.npmjs.com/package/chai-as-promised
[npm-esm]: https://npm.im/esm
[npm-glob]: https://www.npmjs.com/package/glob
[npm-growl]: https://npm.im/growl
[npm-mocha-lcov-reporter]: https://npm.im/mocha-lcov-reporter
Expand Down
62 changes: 48 additions & 14 deletions lib/esm-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,56 @@ const formattedImport = async file => {
return import(file);
};

exports.requireOrImport = async file => {
const hasStableEsmImplementation = (() => {
const [major, minor] = process.version.split('.');
// ESM is stable from v12.22.0 onward
// https://nodejs.org/api/esm.html#esm_modules_ecmascript_modules
return parseInt(major.slice(1), 10) > 12 || parseInt(minor, 10) >= 22;
})();

exports.requireOrImport = hasStableEsmImplementation
? async file => {
if (path.extname(file) === '.mjs') {
return formattedImport(file);
}
try {
return dealWithExports(await formattedImport(file));
} catch (err) {
if (
err.code === 'ERR_MODULE_NOT_FOUND' ||
err.code === 'ERR_UNKNOWN_FILE_EXTENSION'
) {
return require(file);
} else {
throw err;
}
}
}
: implementationOfRequireOrImportForUnstableEsm;

function dealWithExports(module) {
if (module.default) {
return module.default;
} else {
return {...module, default: undefined};
}
}

exports.loadFilesAsync = async (files, preLoadFunc, postLoadFunc) => {
for (const file of files) {
preLoadFunc(file);
const result = await exports.requireOrImport(path.resolve(file));
postLoadFunc(file, result);
}
};

/* istanbul ignore next */
async function implementationOfRequireOrImportForUnstableEsm(file) {
if (path.extname(file) === '.mjs') {
return formattedImport(file);
}
// This is currently the only known way of figuring out whether a file is CJS or ESM.
// If Node.js or the community establish a better procedure for that, we can fix this code.
// Another option here would be to always use `import()`, as this also supports CJS, but I would be
// wary of using it for _all_ existing test files, till ESM is fully stable.
// This is currently the only known way of figuring out whether a file is CJS or ESM in
// Node.js that doesn't necessitate calling `import` first.
try {
return require(file);
} catch (err) {
Expand All @@ -47,12 +89,4 @@ exports.requireOrImport = async file => {
throw err;
}
}
};

exports.loadFilesAsync = async (files, preLoadFunc, postLoadFunc) => {
for (const file of files) {
preLoadFunc(file);
const result = await exports.requireOrImport(path.resolve(file));
postLoadFunc(file, result);
}
};
}
2 changes: 1 addition & 1 deletion test/integration/fixtures/exit.fixture.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ var net = require('net');

it('should hang when --no-exit used', function (done) {
var server = net.createServer();
server.listen(55555, done);
server.listen(55554, done);
});