Mocha: Tests not run/reported using ES modules in browser

Created on 21 May 2017  Â·  4Comments  Â·  Source: mochajs/mocha

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:

image

This test can be run from www.movable-type.co.uk/dev/mocha-es-modules (using Chrome 60 with flag).

browser

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:

<!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:

image

Hooray!

All 4 comments

Side notes:

  • According to http://caniuse.com/#feat=es6-module, this is also behind a flag in Firefox 54 or in MS Edge 15, and available without a flag in Safari. (Additionally, Edge and Safari appear to be the only current -- as opposed to dev/beta/preview -- browsers that support it, if I'm reading that list correctly; so keep your target browser support in mind when relying on next-gen modules having native support as opposed to transpiling.)
  • While Mocha runs in the browser and can be demonstrated by doing so manually, it really shines when used programmatically (e.g. as part of continuous integration); I'd be curious whether and how programmatic browser test harnesses such as Karma handle next-gen modules.
  • 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).
  • If the 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.

image

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:

image

Hooray!

Was this page helpful?
0 / 5 - 0 ratings