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

Interface "exports" not working in browser #2198

Closed
ScottFreeCode opened this issue Apr 10, 2016 · 3 comments
Closed

Interface "exports" not working in browser #2198

ScottFreeCode opened this issue Apr 10, 2016 · 3 comments
Labels
area: browser browser-specific area: documentation anything involving docs or mochajs.org

Comments

@ScottFreeCode
Copy link
Contributor

Getting error due to module not being defined, no tests detected because of that error in the JS file.

Windows 8.1, IE 11 & Firefox 45.0.1, Mocha 2.4.5

Works in Mocha commandline. Also works in browser if I use BDD interface instead of exports interface.

./index.html:

<!DOCTYPE html>
<html>
<head>

<link rel="stylesheet" href="./node_modules/mocha/mocha.css" />
<script src="./node_modules/mocha/mocha.js"></script>
<script>mocha.setup("exports")</script>
<script src="./test/X.js"></script>
<script>
document.addEventListener("readystatechange",function initialize(){
    if (document.readyState == "interactive") {
        mocha.checkLeaks()
        mocha.run()
        document.removeEventListener("readystatechange",initialize)
    }
})
</script>

</head>
<body>

<div id="mocha"></div>

</body>
</html>

./test/X.js:

;(function(){"use strict"

module.exports = {
    "foo": function bar(done) {
        setTimeout(done,10)
    },
    "bar": function foo() {
        throw new Error("FAILED TEST!")
    }
}

}())
@ScottFreeCode
Copy link
Contributor Author

Some digging into the code and experimenting has yielded the following insights:

  1. It's possible to add exports and module.exports before including the test JS files in the page via the script tag, and then call mocha.suite.emit("require", module.exports) after including them but before running them, in order to get the tests to work. However...
  2. Doing this before and after the inclusion of the test files doesn't have exactly the same effect as running them through the CLI; specifically: name collision causes tests to be missed when doing it this way in the browser whereas in the CLI both suites/tests will be used even though the names appear redundant, and there's not much protection against one of the test cases messing up module or exports for everything.
  3. I don't think there's any way (in the normal course of browser-based JS code at least) to get each included test file to have whatever it added to module.exports be sent to mocha.suite.emit("require", ...) individually except to call it after each included script and reset module.exports/exports for each test file.
  4. However, it's pretty straightforward to get the test files themselves to take care of this and be compatible with both the CLI and the browser. Simply wrap them for compatibility with something like:
;(function(module){"use strict" // "use strict" optional
;(function(module, exports){

// SAME TEST CODE HERE

if (typeof mocha !== "undefined") { mocha.suite.emit("require", module.exports) }
}(module, module.exports))
}(typeof module !== "undefined" ? module : {exports:{}}))

It's a bit of boilerplate, but largely out of the way of the actual test code, and it saves me from having to handle similar boilerplate in the page as I'm adding or removing tests from the in-page list and from having to depend on additional tools and/or extra steps to prepare the tests and/or page. However, there are some possible alternatives that don't require changing the test scripts themselves:

  • Calling self.module = { exports: {} }; self.exports = self.module.exports before and mocha.suite.emit("require", module.exports) after each test's script file include in the page (advantage: still no extra tools or steps, boilerplate is smaller; disadvantage: boilerplate is not necessarily in the most convenient place, might still be some weird edge cases with module and/or exports though at least it shouldn't affect other test scripts)
  • Bundling the tests somehow and including the bundle in the browser instead, or something along those lines
  • Perhaps something involving loading the tests into the browser as modules instead of plain scripts (I don't know that any browsers have implemented this natively, but I hear there are shims)

In any case, I don't think there's a good way to make it happen just by modifications to Mocha without risk of odd behavior in edge cases. Instead, I'd like to recommend just documenting how to use the exports interface with in-browser tests, either in the section on using Mocha in a browser or in the section on the exports interface or both. If it would help, I can try writing something up and put in a pull request, although I don't know that I'll have time to actually try the bundling/module-based alternatives given that I'm not sure off the top of my head how exactly to do them and the other solution works fine for me.

Until then, hopefully this comment itself can help anyone else looking for a solution to run exports-style Mocha tests in a page in a browser.

@ScottFreeCode
Copy link
Contributor Author

ScottFreeCode commented Apr 14, 2016

On further study, I think this may theoretically actually be doable -- with AMD modules. Which isn't necessarily as bad as it sounds. For Node/non-browser running, if I understand correctly (haven't tested extensively yet, but I'll be back to let you know if I do run into any problems with it) they'll need to be like this:

var define = typeof define !== "undefined" ? define : function(factory){ factory(require, exports, module) }
define(function(require, exports, module) {

// SAME TEST MODULE CODE HERE AS IN NORMAL EXPORT-STYLE TEST (except possible using only exports rather than module.exports?)

})

That's even less boilerplate than my earlier solution. It's not a full implementation of an AMD loader in Node.js (although you can find those out there), but it should be sufficient for the typical exports-based Mocha test, I would think. And then in the browser, we'd want mocha's "exports" suite setup to provide a similarly rudimentary AMD loader implementation that sends the test modules to the exports interface. It would need some criteria (preferably configurable) to tell which AMD modules are tests (in case they're pulling in other AMD modules), perhaps something like /(^|\/)test/.test(moduleId) by default, and then be able to forward the call to whatever other AMD module loader is in effect if it's not a test. But it should be pretty simple... I think...

On the other hand, whether it's a good or bad idea may depend on whether or not you want to increase mutual compatibility with another major JavaScript testing framework out there, since I recall noticing examples for that one that opened with define (though I'm not too sure of exactly how it works, for lack of extensive experience).

In any case, it's another option to consider. And, I suppose, the exports-interface browser AMD loader wouldn't necessarily need to be supplied by Mocha even; I could probably write up a small project for it. (The downside to that is it's pulled in separately rather than pulled in or not based on whether the "exports" interface is in use. I don't know what would happen if, say, when using another Mocha interface the AMD loader tried to pass a test module to mocha.suite.emit("require",...). Although maybe that would be the start of a way to get the other interfaces to provide their functions to the test modules without putting said functions in the global namespace?)

@ScottFreeCode
Copy link
Contributor Author

I was able to set up the AMD-modules idea as an AMD loader plugin that works quite well for me so far: https://github.com/ScottFreeCode/mocha-exports-amd

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: browser browser-specific area: documentation anything involving docs or mochajs.org
Projects
None yet
Development

No branches or pull requests

3 participants