I am trying – and failing – to use mocha to test ES modules in the browser.
I am using the following code:
test.html:
<!doctype html>
<html lang="en">
<head>
<title>Mocha/Chai ES Modules Test</title>
<meta charset="utf-8">
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/mocha/3.4.1/mocha.min.css">
</head>
<body>
<div id="mocha"></div>
<script src="//cdnjs.cloudflare.com/ajax/libs/mocha/3.4.1/mocha.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/chai/3.5.0/chai.js"></script>
<script>mocha.setup('bdd')</script>
<script type="module" src="./test.js"></script>
<script>mocha.run()</script>
</body>
</html>
test.js:
import MyLib from './lib.js';
const expect = chai.expect;
describe('test', function() {
it('should invoke test without run', function() { expect(MyLib.hypotenuse(5, 12)).to.equal(13); });
it('needs run to invoke test', function() { expect(MyLib.hypotenuse(8, 15)).to.equal(17); }).run();
});
lib.js:
class MyLib {
static hypotenuse(x, y) {
console.log('MyLib.hypotenuse', x, y);
return Math.sqrt(x**2 + y**2);
}
}
export default MyLib;
I am running tests in Chrome 60.0.3100.0 (dev channel, using Experimental Web Platform flag, on Linux).
At first I got no tests run at all; going through issues, I discovered #2760 suggested adding .run(), which does invoke the test (per the console.log), but still does not make the test appear in the browser:

This test can be run from www.movable-type.co.uk/dev/mocha-es-modules (using Chrome 60 with flag).
Side notes:
it(...).run() isn't the normal way to run tests in Mocha and (whatever it may have been intended for...) may not work correctly in the context of Mocha's typical usage (with or without other issues fixed); whatever's going on here is a different issue requiring a different fix, and the run() should be removed (at least in this case).describe blocks are being run at all then clearly Mocha's interface has been set up and is useable from the module...Question -- if you add logging here:
import MyLib from './lib.js';
const expect = chai.expect;
console.log("calling describe");
describe('test', function() {
console.log("inside describe callback");
...and here:
<script type="module" src="./test.js"></script>
<script>console.log("running Mocha");mocha.run()</script>
...What ends up in the console then?
The describe callback is being invoked correctly. I appreciate the it callback should not require the .run() method, but without it, the callback doesn't appear to be invoked (I wasn't even aware of the .run() method until I saw #2760).
Hence "MyLib.hypotenuse 8 15" is output in the console, but not the previous "MyLib.hypotenuse 5 12".
I don't currently have access to Edge or Safari, and for some reason cannot manage to install Firefox 54 – I will try again.

So, in that screenshot of the console logs "running Mocha" comes before the rest. The reason the tests aren't getting run and reported is, quite simply, that the test file is loading after the script tag that calls mocha.run().
I don't happen to have a browser handy that supports the new module system (or whatever's specified of it so far, anyway...), but I'd guess from this behavior that modules are implicitly async or something along those lines. There might be something that can be specified to make a given set of scripts load in order even when some of them are modules, although I don't happen to know (I can pull up a lot of information on google; figuring out which information is current and whether it's going to be superseded next year is... harder). A sure-fire alternative would be to put mocha.run() in a module that first imports the test file(s) instead.
Thank you! That got me there.
Modules are defer by default. Regular inline scripts ignore defer ... but inline module scripts are deferred.
Hence the following works (and also benefits from defer, and also reads cleanly!):
test.html:
<!doctype html>
<html lang="en">
<head>
<title>Mocha/Chai ES Modules Test</title>
<meta charset="utf-8">
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/mocha/3.4.1/mocha.min.css">
<script defer src="//cdnjs.cloudflare.com/ajax/libs/mocha/3.4.1/mocha.min.js"></script>
<script defer src="//cdnjs.cloudflare.com/ajax/libs/chai/3.5.0/chai.min.js"></script>
<script type="module">mocha.setup('bdd')</script>
<script type="module" src="./test.js"></script>
<script type="module">mocha.run()</script>
</head>
<body>
<div id="mocha"></div>
</body>
</html>
test.js:
import MyLib from './lib.js';
const expect = chai.expect;
describe('test', function() {
it('hypotenuse 5, 12', function() { expect(MyLib.hypotenuse(5, 12)).to.equal(13); });
it('hypotenuse 8, 15', function() { expect(MyLib.hypotenuse(8, 15)).to.equal(17); });
});
lib.js:
class MyLib {
static hypotenuse(x, y) {
return Math.sqrt(x**2 + y**2);
}
}
export default MyLib;
Produces:

Hooray!
Most helpful comment
Thank you! That got me there.
Modules are defer by default. Regular inline scripts ignore defer ... but inline module scripts are deferred.
Hence the following works (and also benefits from defer, and also reads cleanly!):
test.html:test.js:lib.js:Produces:
Hooray!