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

Support ES6 style tests without transpiler usage #3006

Open
SGD1953 opened this issue Sep 18, 2017 · 60 comments

Comments

@SGD1953
Copy link

@SGD1953 SGD1953 commented Sep 18, 2017

Prerequisites

  • Checked that your issue isn't already filed by cross referencing issues with the common mistake label
  • Checked next-gen ES issues and syntax problems by using the same environment and/or transpiler configuration without Mocha to ensure it isn't just a feature that actually isn't supported in the environment in question or a bug in your code.
  • 'Smoke tested' the code to be tested by running it outside the real test suite to get a better sense of whether the problem is in the code under test, your usage of Mocha, or Mocha itself
  • Ensured that there is no discrepancy between the locally and globally installed versions of Mocha. You can find them with:
    node node_modules/.bin/mocha --version(Local) and mocha --version(Global). We recommend avoiding the use of globally installed Mocha.

Description

Before I start, there are already some closed issues regarding this topic but as the prerequisites have changed I would like to start a new attempt.

Now that node supports running EMCAScript modules (yes, I know it is experimental) it would be great to see mocha to work in conjunction with mjs test definitions.

Steps to Reproduce

I have a very simple test

describe('Test', function () {
});

Which i have saved as test.js and test.mjs

Expected behavior: I would like both tests to show

- test/test.js 
  0 passing (1ms)
(node:70422) ExperimentalWarning: The ESM module loader is experimental.

Actual behavior: While the js test works, the mjs test gives me

- test/test.mjs 
module.js:658
    throw new errors.Error('ERR_REQUIRE_ESM', filename);
    ^

Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: /Users/dgehl/Repositories/LOreal/code/ecom-lora/test/frontend/components/global/test.mjs

Reproduces how often: 100%

Versions

node --version - v8.5.0
mocha --version - 3.5.3

Additional Information

I think that this might be that mocha's runner is using commonjs and nodejs' current implementation disallows to use ECMAScript modules from a commonjs context.

Please don't reply with "use a transpiler", I want to explicitly not use one.

Edit: in an earlier version I accidentally used jsx instead of mjs.

@ScottFreeCode

This comment has been minimized.

Copy link
Contributor

@ScottFreeCode ScottFreeCode commented Sep 18, 2017

Initial thoughts off the top of my head, before doing any further research:

  • I seem to recall the Node people specifically got the standard changed to allow backwards compatibility with CommonJS modules. If that's (still?) true, then I'd expect that eventually it would be supported without needing to do anything with/to Mocha. (And by "eventually" I mean "possibly even faster than Mocha changes", given the rate of Node's release cycle; see the emphasized, penultimate below for a more detailed explanation of this.)
  • Can I assume that this was run like node --experimental-modules node_modules/mocha/bin/_mocha?
  • How exactly is .jsx involved? I see that the error example shown refers to .mjs; it's unclear, from what has been posted, where the jsx is.
  • I also vaguely recall hearing of Node's initial implementation requiring the .mjs file extension; if that's (still?) true, and if you're trying to use import/export with a .jsx file (either importing a .jsx file in .mjs file or loading a .jsx file that contains import/export), could that be the issue rather than Mocha?
  • We would need to come up with a way to ensure that changes to the experimental feature don't require changes to Mocha that would have to wait for a semver major -- otherwise you could end up right back where we are now except waiting even longer (since Mocha's major release cycle is not locked to twice a year like Node's).
  • Speaking purely from my own opinion and not on behalf of the team, if the new module format cannot interoperate with the old module format without modification to existing libraries written in the old format, then the new module format is not ready yet.
@SGD1953

This comment has been minimized.

Copy link
Author

@SGD1953 SGD1953 commented Sep 18, 2017

I seem to recall the Node people specifically got the standard changed to allow backwards compatibility with CommonJS modules. If that's (still?) true, then I'd expect that eventually it would be supported without needing to do anything with/to Mocha. (And by "eventually" I mean "possibly even faster than Mocha changes", given the rate of Node's release cycle; see the emphasized, penultimate below for a more detailed explanation of this.)

From my (little) research it seems that at least for now it is allowed to use require from a ECMAScript module but not import from a commonjs module.

Can I assume that this was run like node --experimental-modules node_modules/mocha/bin/_mocha?
How exactly is .jsx involved? I see that the error example shown refers to .mjs; it's unclear, from what has been posted, where the jsx is.

Yes, it was run with --experimental-modules. jsx is a typo, I meant mjs, will update the initial post.

I also vaguely recall hearing of Node's initial implementation requiring the .mjs file extension; if that's (still?) true, and if you're trying to use import/export with a .jsx file (either importing a .jsx file in .mjs file or loading a .jsx file that contains import/export), could that be the issue rather than Mocha?

The issue seems to be, and I might be missing something here, that mocha uses require to load the test (that's at least my current assumption as I'm not a mocha expert, more of a user) which then includes other modules via import. This in conjunction with the first point would explain the error.

We would need to come up with a way to ensure that changes to the experimental feature don't require changes to Mocha that would have to wait for a semver major -- otherwise you could end up right back where we are now except waiting even longer (since Mocha's major release cycle is not locked to twice a year like Node's).
Speaking purely from my own opinion and not on behalf of the team, if the new module format cannot interoperate with the old module format without modification to existing libraries written in the old format, then the new module format is not ready yet.

I was afraid that this would be the answer and I understand that this is not a top priority. If my assumption of the cause of the error above is correct, soemthing like #956 could help as the entry point of the test could be a mjs module rather than commonjs which is probably hard to achieve otherwise. It seems to be on the roadmap of the nodejs team to support import from current modules, not clear about timelines however.

@ScottFreeCode

This comment has been minimized.

Copy link
Contributor

@ScottFreeCode ScottFreeCode commented Sep 18, 2017

From my (little) research it seems that at least for now it is allowed to use require from a ECMAScript module but not import from a commonjs module.

It seems to be on the roadmap of the nodejs team to support import from current modules, not clear about timelines however.

To clarify: given that in "older" (in some cases current non-experimental) environments import is a syntax error, which can't be avoided with branching logic or anything like that, what Mocha needs isn't to be able to use import itself but rather to be able to use require to load modules that use (or that use modules that use) the new format.

jsx is a typo, I meant mjs , will update the initial post.

Thanks, that eliminates one possible angle!

If my assumption of the cause of the error above is correct, soemthing like #956 could help as the entry point of the test could be a mjs module rather than commonjs which is probably hard to achieve otherwise.

Making Mocha not create global variables is unfortunately not possible without extensive rewriting of some of the more arcane internals (I tried and couldn't figure it out myself 😿 ); however, using your own JS entry point is possible now through the "programmatic" API (which might not be documented outside of an old wiki page and the JSDoc comments in the source files, but is officially supported):

// test.mjs
var Mocha = require('mocha'),
var mocha = new Mocha();

// your mission: create a file `example.mjs`
// in the `test` folder and have it `import` stuff
// as well as using `describe` and `it` to make tests
mocha.addFile("./test/example.mjs");

mocha.run(function(failures){
  process.on('exit', function () {
    process.exit(failures ? 1 : 0);
  });
});
node --experimental-modules test.mjs

I haven't actually tried that to see if it makes a difference (need to grab the latest version of Node first), but let me know if it works for you...

@SGD1953

This comment has been minimized.

Copy link
Author

@SGD1953 SGD1953 commented Sep 19, 2017

First of all thank you for your support on this!

I tried this

// runner.mjs
import Mocha from 'mocha';
var mocha = new Mocha();

// your mission: create a file `example.mjs`
// in the `test` folder and have it `import` stuff
// as well as using `describe` and `it` to make tests
mocha.addFile('./test/frontend/components/global/test.mjs');

mocha.run(function (failures) {
    process.on('exit', function () {
        process.exit(failures ? 1 : 0);
    });
});

So basically made the node entry point a ECMAScript module.

I run it via node --experimental-modules --harmony ./runner.mjs

I get

(node:88620) ExperimentalWarning: The ESM module loader is experimental.
{ Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: /.../test/frontend/components/global/test.mjs
    at Object.Module._extensions..mjs (module.js:658:11)
    at Module.load (module.js:545:32)
    at tryModuleLoad (module.js:508:12)
    at Function.Module._load (module.js:500:3)

what Mocha needs isn't to be able to use import itself but rather to be able to use require to load modules that use (or that use modules that use) the new format.

That I'm afraid is currently not possible in node, you can only use require in modules you imported via import. Is there a way to avoid mocha.addFile('./test/frontend/components/global/test.mjs'); and instead import the test and add the imported script like this

import test from './test';
mocha.addTest(test);

?

@ScottFreeCode

This comment has been minimized.

Copy link
Contributor

@ScottFreeCode ScottFreeCode commented Sep 20, 2017

There's no function like that in Mocha at the moment, but you can do something along those lines. addFile just appends the file to a list that is later required by the run function. The run function calls loadFiles to require them:

mocha/lib/mocha.js

Lines 220 to 235 in 1cc0fc0

/**
* Load registered files.
*
* @api private
*/
Mocha.prototype.loadFiles = function (fn) {
var self = this;
var suite = this.suite;
this.files.forEach(function (file) {
file = path.resolve(file);
suite.emit('pre-require', global, file, self);
suite.emit('require', require(file), file, self);
suite.emit('post-require', global, file, self);
});
fn && fn();
};

What you'd want to do is for any files that need to be imported instead of required don't call addFile (so Mocha won't try to require it on run) and instead before calling run call some code that's like what's in loadFiles but using import instead of require. I don't recall off the top of my head whether there are any restrictions on use of import that would prevent this, but if it's possible at all then I imagine it would look pretty close to:

modules.forEach(function (file) {
  file = path.resolve(file);
  mocha.suite.emit('pre-require', global, file, mocha);
  import fileExport from file; // fileExport is used by the exports interface, not sure if anything else; most interfaces act as a side effect of running the file
  mocha.suite.emit('require', fileExport, file, mocha);
  mocha.suite.emit('post-require', global, file, mocha);
});

You can also look at how https://github.com/mochajs/mocha/blob/master/bin/_mocha uses Mocha's programmatic API to get a sense of how to supply other options and how to use things like Mocha's file lookup functionality. It's not very well organized but everything the commandline interface does is in there (either directly or because in there is a call to the functions in Mocha's programmatic API).

@SGD1953

This comment has been minimized.

Copy link
Author

@SGD1953 SGD1953 commented Sep 20, 2017

I can come one step further but the imported test now complains it does not know about describe (ReferenceError: describe is not defined). What is the proper way to inject it? If I do

import Mocha from 'mocha';
const describe = Mocha.describe;

it complains TypeError: describe is not a function

@ScottFreeCode ScottFreeCode self-assigned this Sep 22, 2017
@ScottFreeCode

This comment has been minimized.

Copy link
Contributor

@ScottFreeCode ScottFreeCode commented Sep 25, 2017

So, my distro finally got NodeJS 8.5, and I've had a chance to play with this and confirm a couple of hunches I had but didn't want to state till I'd been able to check:

  1. I can't find any way to load an ES module without hardcoding its name in a script/module file. That means that Mocha can't load them through the commandline interface no matter what we do and regardless of any other ES vs. CommonJS semantics. If and when that changes, we'll want to know, I guess. (If it changes such that the ES module can be loaded through a variable require we probably won't need to change anything, but I don't think we can say anything about how modules work for sure until it happens.)
  2. Whereas Mocha sets up the interface in the pre-require event emission (not when the module is first loaded; the chosen interface is specific to the new Mocha instance), the new module system actually parses the dependency tree and loads dependencies before the modules that depend upon them, so the reason describe is not defined is that Mocha isn't setting it up until after the test modules are loaded. That means that it's going to be convoluted to get this to happen at all (again, unless and until require(file) is allowed and loads the dependency at that specific line, preferably synchronously so we don't have to change anything else).

However, I did discover I can make it work by exploiting the fact that imported modules are loaded in the order of the import calls. (It would get a whole lot more redundant if you use the Exports interface or a reporter that needs the name of each individual file, as described in the code I'm about to link, but in principle it works.) If that fact were to change without any of the other above facts changing, then even this would not be possible at all. But for now I think this is what you'd have to do.

https://gist.github.com/anonymous/caba0883254a2349c5615df8e9286665

node --experimental-modules ./run.mjs

Unfortunately, I'm fairly sure that's the best we can do given the way ES modules work and what Node allows at the present time.

@ScottFreeCode ScottFreeCode removed their assignment Sep 25, 2017
@boneskull

This comment has been minimized.

Copy link
Member

@boneskull boneskull commented Oct 4, 2017

Think of it another way:

  • import is syntax.
  • require is a function.

You cannot dynamically import anything, just as you cannot dynamically run code without the use of, for example, eval().

There is this stage 3 proposal which would allow this behavior, but I'm not sure if any runtimes are shipping it yet.

As such, there's no way for Mocha to import an .mjs file when running via mocha without adding perhaps @std/esm and using its require implementation for files with the .mjs extension. That may be a viable solution and something we could consider supporting, but a discussion (and such a PR) would likely need to come from the community, at least until this behavior isn't behind a flag.

import describe from 'mocha' is pretty low on the priority list, unfortunately, due to the inherent difficulty around this sort of thing (#956). Best to run with node and stick to consuming the globals.

@boneskull

This comment has been minimized.

Copy link
Member

@boneskull boneskull commented Oct 4, 2017

Actually, it occurs to me that we could load the tests and leverage vm.runInContext, assuming such a thing supports modules. Because Node's loading behavior is tied to the .mjs extension, and vm.runInContext expects a string, don't see how it could--and there's nothing mentioned about this in the docs. Maybe an issue somewhere?

@boneskull

This comment has been minimized.

Copy link
Member

@boneskull boneskull commented Oct 4, 2017

(then again, this may be exactly what @std/esm does under the hood!)

@vitalets

This comment has been minimized.

Copy link

@vitalets vitalets commented Dec 12, 2017

I've got mocha tests working without transpiler in a browser. Maybe it helps for this issue.

@boneskull

This comment has been minimized.

Copy link
Member

@boneskull boneskull commented Dec 13, 2017

that’s unrelated as you’re not pulling mocha in as a module, but rather a script...

@boneskull

This comment has been minimized.

Copy link
Member

@boneskull boneskull commented Dec 13, 2017

sorry confused myself. it’s different in a browser.

@robogeek

This comment has been minimized.

Copy link

@robogeek robogeek commented Feb 10, 2018

I want to weigh in with a vote of support for doing something to allow Mocha to run tests located in an ES Module. I landed here after trying to write such a test and getting a weirdo error from the Node.js module loader. I'm using Node.js 9.5, which natively supports ES6 modules.

As it currently stands, Node.js 9.5 does not allow a CommonJS module to require() an ES6 module. Maybe they're working in the direction of allowing that, I don't know.

I wrote the test as a ES6 module with the .mjs extension and tried to run it. Got the error from the loader -- I assume the mocha command results in using require() and that's why it failed.

Redid the test with the .js extension and tried to use require() to load the module that was to be tested. That also got the error from the loader.

I'm of the opinion that the Node.js world needs to consider how they'll move to and support ES6 modules. Since Mocha is a very popular tool in this world, it would be best for the Mocha team to consider how to support ES6 modules.

@robogeek

This comment has been minimized.

Copy link

@robogeek robogeek commented Feb 10, 2018

To follow up ... After some pondering and searching I was able to get this sequence to work as a workaround.

Name the test script with .js extension (making it a CommonJS script)

Then add this in the test script:

require = require("@std/esm")(module,{"esm":"js"});

Then I can require() an ES module as so:

const model = require('../models/notes');
@sorgloomer

This comment has been minimized.

Copy link

@sorgloomer sorgloomer commented Feb 26, 2018

@robogeek Or it might be even better to use the @std/esm preloader from commandline, so you don't have to clutter your spec files with workarounds, and can have .mjs extensions.

mocha -r @std/esm spec.mjs
@harrysarson

This comment has been minimized.

Copy link
Contributor

@harrysarson harrysarson commented Feb 26, 2018

Dynamic import ships with node v9.6 behind the --harmony-dynamic-import flag. Dynamic imports allow mocha to load tests contained in es6 modules without needing a transpiler.

@sorgloomer

This comment has been minimized.

Copy link

@sorgloomer sorgloomer commented Feb 26, 2018

@harrysarson It is not going to work out of the box. Mocha uses cjs modules and require, you would have to write the test files using cjs, with some additional glue code to handle the async nature of import. Or am I missing something?

@stale

This comment has been minimized.

Copy link

@stale stale bot commented Jun 26, 2018

I am a bot that watches issues for inactivity.
This issue hasn't had any recent activity, and I'm labeling it stale. In 14 days, if there are no further comments or activity, I will close this issue.
Thanks for contributing to Mocha!

@stale stale bot added the stale label Jun 26, 2018
@demurgos

This comment has been minimized.

Copy link
Contributor

@demurgos demurgos commented Jun 26, 2018

The issue is still relevant but relies on native support for ESM. Browsers have it, Node not yet.

@stale stale bot removed the stale label Jun 26, 2018
@stefanpenner

This comment has been minimized.

Copy link

@stefanpenner stefanpenner commented Jun 27, 2018

I was just playing around, getting familiar with ESM/.mjs and decided I needed tests for my toy. Realizing mocha is not yet officially supporting .mjs files, I through together a quick interim module (until someone has time to add full support to mocha):

https://www.npmjs.com/package/mocha-esm
PRs/Issues welcome: https://github.com/stefanpenner/mocha-esm

There might be something better out there, but it was fun to through together. so \o/

@boneskull

This comment has been minimized.

Copy link
Member

@boneskull boneskull commented Dec 5, 2018

fwiw I recommend esm over --experimental-modules until Node.js has its story straight. that will be a considerable wait.

@demurgos

This comment has been minimized.

Copy link
Contributor

@demurgos demurgos commented Dec 5, 2018

@boneskull
Haha, thanks. I'm already working around c8 since July (I opened a bunch of PRs and issues on this repo). There are also some design decision where we disagree so we try to share most of the dependencies (I wrote the merge algorithm for example) and decided that I'll publish another tool: c88. I got it to work this week-end and I'm now testing it on my libraries. I'm able to use it with native ESM and mocha in CI. I still need some time to document and fix it but it should be ready in January).

@jrgleason
I wrote the code above of the top of my head. It seems that the issue here is that globSync returns relative paths that do not start with ./ or ../. You may want to prepend it with ./: it should work with simple relative paths.
You should also note that dynamic imports use relative URLs: #, ? and other special characters may be handled differently. If you want a rock solid solution, you should resolve the absolute path of the module and then convert it to a file URL. As part of my work on coverage, I wrote a lib to convert between absolute paths and URLs: you may want to use fromSysPath from furi. Conversions should handle any kind of path (even the Windows namespaces and UNC paths...).

Here is what a complete example may look like:

const {fromSysPath} = require("furi");
const {sync: globSync} = require("glob");
const {resolve} = require("path");

(async () => {
    const matches = globSync("**/*.spec.mjs");
    for (const match of matches) {
        await import(fromSysPath(resolve(match)).href);
    }
    run();
})();
@boneskull

This comment has been minimized.

Copy link
Member

@boneskull boneskull commented Dec 5, 2018

I mean, doesn't mocha --require esm just work? Do we really need automatic detection? That sounds difficult and adds overhead...

@demurgos

This comment has been minimized.

Copy link
Contributor

@demurgos demurgos commented Dec 5, 2018

@demurgos to cut a minor release, we'd need to cherry pick all non-breaking changes since v5.2.0 into a branch and compile them into the CHANGELOG. if you or someone else is willing to do that work, then we can cut the release.

Thanks for the proposition. It's still possible to get --experimental-modules with NODE_OPTIONS so it's not a high priority (and it may complicate the git tree to cherry-pick commits). If I manage to close the issues I have with other dependencies, I'll see if I can spend some time on this. In the mean time, I just keep an eye on the v6 milestone.

fwiw I recommend esm over --experimental-modules until Node.js has its story straight. that will be a considerable wait.
I mean, doesn't mocha --require esm just work?

I definitely agree that it is the best solution right now: it's the easiest solution to set up and has been out for some time: it works. In my case, I am maintaining my own build tools and handle native ESM as an alternative to classic CJS builds. Despite being really eager for native ESM, I still recommend to not use it as the only way to run your code: it's experimental after all 😛.

Most of my recent messages are about sharing what can be done using native ESM. This is mostly experimental work and I expect to have to change it when Node's ESM gets stable. Long term there are benefits to have a solution that does not require the esm package. Here are my reasons:

  • It reduces the amount of tools needed (lower complexity, less configuration to understand)
  • There may be a few differences between real ESM and esm around edge cases (evaluation errors, cycles, load errors, async/dynamic modules, wasm, etc.). When writing isomorphic code, it may be safer to reduce any possible source of behavior divergence. This is also kinda related to native coverage: with esm, V8 sees the transpiled output so you have to deal with source maps (not yet supported by c8, but I'm preparing a PR, they work on c88). Other differences may also appear when debugging.
  • It avoids to have to dynamically transpile the code and helps improving performance.

Do we really need automatic detection? That sounds difficult and adds overhead...

I am not sure which automatic detection you are referring to. Is it related to the PR that was sent earlier this year?


Edit: I'm also on the Node tooling Slack (mostly active on the #c8 channel) if you want to discuss.

@boneskull

This comment has been minimized.

Copy link
Member

@boneskull boneskull commented Dec 5, 2018

@demurgos I think I was a bit confused about what people wanted here. Anyway...

If NODE_OPTIONS=--experimental-modules works until --experimental-modules is supported in Mocha v6.0.0, then is there any other work to be done for this issue? That's what I'm missing.

@demurgos

This comment has been minimized.

Copy link
Contributor

@demurgos demurgos commented Dec 5, 2018

I expect that this issue should remain open until native ESM ("ES6 style tests without transpiler usage") works out of the box / as easily as CJS works currently.

The solution I posted with --delay and NODE_OPTIONS=--experimental-modules is more a workaround than proper support. I'd consider this issue fixed once you'll be able to run mocha **/*.spec.mjs and get a report.

Unfortunately, for the moment I feel that we still have to wait for Node to figure out ESM support. I'd have to check, but I think that the PR did not use automatic detection but simply imported every module (CJS or ESM) using dynamic imports. The implementation will depend on the interop story for modules.


Edit: I am referring to #3253. It allows to load all modules as ESM (no automatic detection).

sgilroy pushed a commit to TwineHealth/mocha that referenced this issue Feb 27, 2019
This commit adds `--experimental-modules` to the list of Node flag recognized by Mocha. It allows to use `mocha --experimental-modules foo.spec.js` instead of requiring the workaround `NODE_options="--experimental-modules" mocha foo.spec.js`.

See <mochajs#3006 (comment)>
@stale

This comment has been minimized.

Copy link

@stale stale bot commented Apr 5, 2019

I am a bot that watches issues for inactivity.
This issue hasn't had any recent activity, and I'm labeling it stale. In 14 days, if there are no further comments or activity, I will close this issue.
Thanks for contributing to Mocha!

@stale stale bot added the stale label Apr 5, 2019
@demurgos

This comment has been minimized.

Copy link
Contributor

@demurgos demurgos commented Apr 5, 2019

Node 12 should include the new ESM implementation. It will be the occasion to check how to support ES modules in Mocha.

@stale stale bot removed the stale label Apr 5, 2019
@demurgos

This comment has been minimized.

Copy link
Contributor

@demurgos demurgos commented Apr 30, 2019

I hit GC bug when using Mocha an ESM, but it is reported and confirmed so it should be fixed: nodejs/node#27492.

Beside this bug, the strategy described in my comment above still works to use Mocha with ESM.

@tadd

This comment has been minimized.

Copy link

@tadd tadd commented May 1, 2019

Hi Mocha people, thank you for creating and maintaining nice tool!

This is just FYI comment. For the last few months I have been working on Mocha and * -test.mjs , using the patches like below. There is almost no problem to run mocha test/*.mjs (without transpiler or esm npm module).
https://gist.github.com/tadd/756d21bad38933c179f10e59bddee6b4

Of course this patch is not "production-ready" for Mocha committers; this is just a hack for Mocha users who want to use ESM in test codes as soon as possible.
I've been used Node.js v11 with --experimental-modules option. Currently I'm with v12 and also works.

It is another story, but Node v12 introduced .cjs extension and "type" field in package.json. It seems that these also need to be considered.

@maxnordlund

This comment has been minimized.

Copy link

@maxnordlund maxnordlund commented May 2, 2019

Hi Mocha people, thank you for creating and maintaining nice tool!

Indeed, and I also would like to give thanks 😄

I took a different approach to make loadFiles stay synchronous, see below. It has worked for me since February. Unlike some other hacks, this still allows all flags, inspect/devtools and accurate code coverage using c8. That last feature is why I really need native ESM, because the esm package have different offsets for each file. (The module's exports get listed and confusing istanbul).

https://gist.github.com/maxnordlund/a860dd67013beaf0f31ce776536f0a47

@heruan

This comment has been minimized.

Copy link

@heruan heruan commented May 6, 2019

Hello! This is also needed to test any code which depends on native ES6 project, e.g. lit-element. Otherwise, it throws like this:

node_modules/lit-element/lit-element.js:14
import { TemplateResult } from 'lit-html';
       ^

SyntaxError: Unexpected token {
    at Module._compile (internal/modules/cjs/loader.js:703:23)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:770:10)
    at Module.load (internal/modules/cjs/loader.js:628:32)
    at Function.Module._load (internal/modules/cjs/loader.js:555:12)
    at Module.require (internal/modules/cjs/loader.js:666:19)
    at require (internal/modules/cjs/helpers.js:16:16)
    ...

I don't know if there's a workaround for this, but currently I don't think there's a way to use Mocha with ES6 native framework.

@demurgos

This comment has been minimized.

Copy link
Contributor

@demurgos demurgos commented May 6, 2019

@heruan I posted a solution working since Node 8 in the comments above. Here is an updated version that requires Node 10.12 for native file URL conversions:

Add the following test.esm.js file:

const {pathToFileURL} = require("url");
const {sync: globSync} = require("glob");
const {resolve} = require("path");

(async () => {
    const matches = globSync("**/*.spec.mjs"); // Change the glob to match your test files
    for (const match of matches) {
        await import(pathToFileURL(resolve(match)).href);
    }
    run();
})();

Then run the tests with mocha --experimental-modules --delay test.esm.js.

This code works by using test.esm.js as the commonjs bridge to load the ESM tests.

Node 12 has a reported issue where the IIAFE is collected by th GC (nodejs/node#27492), it should be fixed in one of the next minor versions (there may be some workarounds but I haven't looked into them yet). I recommend to use the latest Node 10 version until it is fixed.

@maxnordlund

This comment has been minimized.

Copy link

@maxnordlund maxnordlund commented May 6, 2019

Thar will trigger an UnhandledPromiseRejectionWarning warning if there's any error. Better to chain a console.error or otherwise handle the error.

import glob from "glob"
import { pathToFileURL } from "url"
import { resolve } from "path"
import { promisify } from "util"

const globAsync = promisify(glob)

async function main() {
  const matches = await glob("test/**/*.mjs")

  for (const match of matches) {
    await import(pathToFileURL(resolve(match)).href)
  }

  run()
}

main().catch(console.error)

I know this is using import over require, but see my gist for a solution that let's you stay in ESM land

@David-Else

This comment has been minimized.

Copy link

@David-Else David-Else commented May 16, 2019

@demurgos Thanks for the code snippet for Node 10.12 you posted ten days ago!

I am running Node 12.1 and it seems to work fine. Will this soon be added to Mocha, or is there an even easier way to do this with Node 12? Also, how to I use this in --watch mode?

https://github.com/standard-things/esm seems to work out of the box with --require esm, but it would be great to ditch another dependency :) Thanks.

@brettz9

This comment has been minimized.

Copy link

@brettz9 brettz9 commented Jun 5, 2019

If Mocha were to switch to ESM in source, Rollup could provide an ESM distribution file as well as a CommonJS and/or UMD ones.

Another advantage here is that browser HTML wouldn't need to be polluted with extra script tags to pull in Mocha. The test files (or main test entrance file) could do the importing (and avoid "magic" within the test files as far as figuring out where the variables are coming from--only need to trace the import paths).

@brettz9

This comment has been minimized.

Copy link

@brettz9 brettz9 commented Jun 5, 2019

One can use CSS plugins for Rollup as well to allow injecting mocha.css, further minimizing need for HTML clutter.

@stale

This comment has been minimized.

Copy link

@stale stale bot commented Oct 3, 2019

I am a bot that watches issues for inactivity.
This issue hasn't had any recent activity, and I'm labeling it stale. In 14 days, if there are no further comments or activity, I will close this issue.
Thanks for contributing to Mocha!

@stale stale bot added the stale label Oct 3, 2019
@maxnordlund

This comment has been minimized.

Copy link

@maxnordlund maxnordlund commented Oct 3, 2019

I think this is still relevant.

@stale stale bot removed the stale label Oct 3, 2019
@tomalec

This comment has been minimized.

Copy link

@tomalec tomalec commented Oct 3, 2019

Was anybody able to run Mocha with ES6 Modules on (edit: Travis) Node >=12.11.0?
On 12.10.0 it seems, I set it up successfully:
mocha-run.js

(async () => {
    await import("./tests.js");
    run();
})();

Then mocha --experimental-modules --delay ./mocha-run.js works like charm.

But for some unknown reason, on 12.11.0, it behaves as if there would be no --delay param:

>  mocha --experimental-modules --delay ./mocha-run.js

(node:6439) ExperimentalWarning: The ESM module loader is experimental.

internal/modules/cjs/loader.js:1007

      internalBinding('errors').triggerUncaughtException(

                                ^

Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: /home/travis/build/Palindrom/Palindrom/mocha-run.js
@vanslly

This comment has been minimized.

Copy link

@vanslly vanslly commented Oct 10, 2019

@tomalec I am running mocha with ES modules on node 12.11.1:

mocha-run.js

(async () => {
    await import("./tests.mjs");
    run();
})();

However, watch mode is not working. mocha waits for file changes, but doesn't run another test run after a file has been changed.

@tomalec

This comment has been minimized.

Copy link

@tomalec tomalec commented Oct 14, 2019

@vanslly Lucky you ;)
For me with Node 12.12.0 (https://travis-ci.org/Palindrom/Palindrom/builds/597771311#L450) and mocha-run.js as you suggested above (Palindrom/Palindrom@4983596#diff-24eabf03aee8844b2b4747aa95a6af7d),

mocha --experimental-modules --delay test/mocha-run.js https://travis-ci.org/Palindrom/Palindrom/builds/597771311#L643, , throws still the same error

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
You can’t perform that action at this time.