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

How to build mocha with webpack #2448

Closed
vitalets opened this issue Aug 20, 2016 · 15 comments
Closed

How to build mocha with webpack #2448

vitalets opened this issue Aug 20, 2016 · 15 comments

Comments

@vitalets
Copy link

vitalets commented Aug 20, 2016

For some reason I need to bundle mocha with webpack.
I see browser section in mocha package.json so it looks like webpack should exclude node specific require's.
But when i try var mocha = require('mocha') I'm getting warnings:

WARNING in ./~/mocha/lib/mocha.js
Critical dependencies:
151:20-37 the request of a dependency is an expression
183:17-30 the request of a dependency is an expression
220:26-39 the request of a dependency is an expression
 @ ./~/mocha/lib/mocha.js 151:20-37 183:17-30 220:26-39

WARNING in ./~/mocha/lib/interfaces/bdd.js.orig
Module parse failed: /Users/vitalets/projects/autotester/node_modules/mocha/lib/interfaces/bdd.js.orig Unexpected token (93:0)

and errors:

ERROR in ./~/mocha/~/growl/lib/growl.js
Module not found: Error: Cannot resolve module 'fs' in /Users/vitalets/projects/autotester/node_modules/mocha/node_modules/growl/lib
 @ ./~/mocha/~/growl/lib/growl.js 8:9-22

Can anybody help to fix this?
How to correctly use browser-entry.js?
thanks!

@Munter
Copy link
Member

Munter commented Aug 21, 2016

For some reason I need to bundle mocha with webpack

Can we start there? What is that reason and could whatever you want to achieve be done without? The amount of scope creep webpack adds, like having to bundle your test runner seems like the real issue to me here

@ericclemmons
Copy link

ericclemmons commented Aug 21, 2016

If you're using mocha in Node, then you don't need to bundle. (More on this if you need it)
If you're using mocha in the browser for browser tests, then I'd recommend doing something like this:

// webpack.config.mocha.js
plugins: [
  ...
  entry: ["./test/all.js"],
  new HtmlWebpackPlugin({
    cache: true,
    filename: "test/index.html",
    showErrors: true,
    template: "./test/support/index.html",
    title: "Mocha Browser Tests",
  }),
  ...
],
// test/all.js
const context = require.context(
  "../src",       // Root directory
  true,           // Recursive
  /.+\.test\.js$/ // Test pattern
);

// Require each within build
context.keys().forEach(context);
<!-- test/support/index.html -->
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title><%= htmlWebpackPlugin.options.title %></title>

    <link href="https://npmcdn.com/mocha@2.5.3/mocha.css" rel="stylesheet" />
    <script src="https://npmcdn.com/mocha@2.5.3/mocha.js"></script>
  </head>
  <body>

    <!-- A container element for the visual Mocha results -->
    <div id="mocha"></div>

    <!-- Mocha setup and initiation code -->
    <script>
      mocha.setup('bdd');
      window.onload = function() {
        var runner = mocha.run();
        var failedTests = [];

        runner.on('end', function() {
          window.mochaResults = runner.stats;
          window.mochaResults.reports = failedTests;
        });

        runner.on('fail', logFailure);

        function logFailure(test, err){
          var flattenTitles = function(test){
            var titles = [];
            while (test.parent.title){
              titles.push(test.parent.title);
              test = test.parent;
            }
            return titles.reverse();
          };

          failedTests.push({
            name: test.title,
            result: false,
            message: err.message,
            stack: err.stack,
            titles: flattenTitles(test)
          });
        };
      };
    </script>

  </body>
</html>

@vitalets
Copy link
Author

vitalets commented Aug 22, 2016

@Munter

For some reason I need to bundle mocha with webpack

Can we start there? What is that reason and could whatever you want to achieve be done without? The amount of scope creep webpack adds, like having to bundle your test runner seems like the real issue to me here

I'm building chrome extension that allows to run tests with mocha.
I achieved it without bundling mocha via manually re-loading it as script tag every time I need.
But my intention was to bundle it as all other files in project.

@ericclemmons
Thanks for HtmlWebpackPlugin, will have a look on it and try this approach.

Anyway, guys, could you shortly explain for what reason there is browser-entry.js and how you use it?

@boneskull
Copy link
Member

boneskull commented Aug 23, 2016

@vitalets That's the entry point for Browserify. It's used when building the browser version of Mocha (./mocha.js; not ./lib/mocha.js). It's also used if you are attempting to bundle Mocha itself (via Browserify), because it's in the browser field of package.json.

Webpack may not respect the browser field in package.json; if it doesn't, you could try to point it at this file instead of index.js. But there's likely Browserify-specific stuff tangled up in there, and you might also find some conditional require statements that Webpack won't like.

Regardless, the entry point should be browser-entry.js and not index.js if you are attempting to bundle yourself (though it's still unclear to me why anyone would really need to do this). Otherwise, ./mocha.js is already built for the browser.

I'm going to close this, as I don't think there's anything to be done here, but feel free to ask more questions here or in Gitter.

@vitalets
Copy link
Author

@boneskull thanks for explanation!
AFAIK webpack supports browser field in package.json but when I tried where are some other errors with conditional requires as you said.
Will contact you in gitter if needed!

@Aqours
Copy link

Aqours commented Jan 4, 2018

A nagging problem.
My workaround. (add exprContextCritical: false and node: {fs: 'empty'})

// Karma configuration

const path = require('path');
const webpack = require('webpack');

module.exports = function (config) {


    config.set({

        // frameworks to use
        // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
        frameworks: ['mocha'],


        // webpack configuration
        webpack: {
            module: {

                // Suppress warning from mocha: "Critical dependency: the request of a dependency is an expression"
                // @see https://webpack.js.org/configuration/module/#module-contexts
                exprContextCritical: false,

                // doesn't append `babel-loader` because `jsdiff` package will cause some issue.
                // @see https://github.com/kpdecker/jsdiff/issues/161
                rules: [
                    {
                        test: /\.tsx?$/,
                        use: 'ts-loader',
                    },
                    {
                        test: /\.tsx?$/,
                        include: path.resolve(__dirname, 'source'),
                        exclude: path.resolve(__dirname, 'test'),
                        enforce: 'post',
                        use: {
                            loader: 'istanbul-instrumenter-loader',
                            options: {esModules: true},
                        },
                    },
                ],
            },

            // Suppress fatal error: Cannot resolve module 'fs'
            // @relative https://github.com/pugjs/pug-loader/issues/8
            // @see https://github.com/webpack/docs/wiki/Configuration#node
            node: {
                fs: 'empty',
            },
        }

        // Other code...
    });
};

@jedwards1211
Copy link

jedwards1211 commented Jan 16, 2018

@Munter

Can we start there? What is that reason and could whatever you want to achieve be done without? The amount of scope creep webpack adds, like having to bundle your test runner seems like the real issue to me here.

No, we're not going to go into that, Webpack is important for a lot of devs whether you like it or not. We can start with why Mocha's browser-entry.js triggers a chain of requires that end up trying to require('fs'):

Module not found: Error: Can't resolve 'fs' in '/Users/andy/react-fader/node_modules/mkdirp'
 @ ./node_modules/mkdirp/index.js 2:9-22
 @ ./node_modules/mocha/lib/reporters/xunit.js
 @ ./node_modules/mocha/lib/reporters/index.js
 @ ./node_modules/mocha/lib/mocha.js
 @ ./node_modules/mocha/browser-entry.js
 @ ./test/index.js

If browser-entry.js is designed to work in the browser, then I'm pretty sure it, uh, shouldn't require('fs') or even mkdirp or any reporter that depends on mkdirp, regardless of what mocha is being bundled with.

I mean, what hack are you guys using to build mocha.js for the browser without running into this issue?

@jedwards1211
Copy link

jedwards1211 commented Jan 16, 2018

@boneskull I dug into this some more, and I believe Mocha doesn't really use the browser field correctly. To quote the browser field spec (emphasis added):

You can simply prevent a module or file from being loaded into a bundle by specifying a value of false for any of the keys. This is useful if you know certain codepaths will not be executed client side but find it awkward to split up or change the code structure.

Mocha is using "fs": false in its browser field even though the codepaths that end up trying to require('fs') are executed client side.

This is why even though Webpack supports the browser field, one still gets an error when trying to run Mocha via Webpack (without @Aqours' workaround). The require('fs') call fails as it should.

I see in your build config you are telling Browserify to ignore fs and path, which I'm guessing is why the require('fs') calls don't fail in the browserified bundle. Relying on build toolchain magic will always be more error-prone than simply writing code that works in a well-specified JS environment (which in this case should be Browser + CommonJS require) regardless of what it's being bundled with. Ignoring packages in a build tool should be viewed as a hack.

So Mocha should ensure that when run on the client it won't run any code that does require('fs') or require('path'), instead of relying on ignoring those packages in Browserify.

@boneskull
Copy link
Member

you’re welcome to send a PR if it doesn’t break the build.

@kuceb
Copy link

kuceb commented Mar 15, 2019

@vitalets webpack only supports using the browser field in as an alias of main, not the full browser object as defined in the spec. webpack/webpack#151 (comment)

browserify supports the spec, hence why builds work fine using that.

@tstibbs
Copy link

tstibbs commented Jul 7, 2020

I struggled with some of the same issues expressed on this thread, so here's my solution just in case it helps someone else.

My requirements were that I could run the tests using karma but I also had a webpage that allowed me to run the mocha tests in a browser.

I did manage to get something working using an html page based on the html that @ericclemmons posted. However, I was pretty unhappy with my approach because all my other modules were being bundled nicely with webpack, but then I had a few files that were sitting outside of that, and I was using things like the webpack copy plugin to move mocha.js to the right place in the build and it all looked wrong.

So, I wanted to load mocha using webpack so it was managed in the same way as all my other modules (I think that's a fairly normal thing to want, despite some of the comments on this thread). It would be straightforward to shim mocha using exports-loader if you only wanted to run in a browser, but karma-mocha provides its own version of mocha which will conflict with the version pulled in using webpack if you do this. I did finally get it working by:

  • Importing mocha/browser-entry instead of plain mocha
  • Importing the mocha js and css in a 'mocha-wrapper' module which also called mocha.setup
  • Importing this wrapper module conditionally, only if the describe function is not in global scope already (if yes, then we're running with karma so no need to load mocha ourselves; if no, we're running in the webpage so load mocha using webpack in the normal way)

@rafikhan
Copy link

@tstibbs - Would you mind posting a link to your working project somewhere?

@tstibbs
Copy link

tstibbs commented Oct 19, 2020

@tstibbs - Would you mind posting a link to your working project somewhere?

@rafikhan You'll have to have a dig around, but the following should be most of what you need:

https://github.com/tstibbs/geo-bagging/blob/master/ui/test/suite/mocha-wrapper.js
https://github.com/tstibbs/geo-bagging/blob/master/ui/test/suite/suite.js#L8
https://github.com/tstibbs/geo-bagging/blob/master/ui/webpack.config.cjs#L185

(to be clear I'm not saying my approach is right or even valid, I'm just saying it appears to be working for me - on a small personal project with just a handful of tests)

@aovchinn
Copy link

I think import mocha from "mocha/mocha-es2018"; should work

added in 9.0.1
#4657

@cristian-spiescu
Copy link

Applying directly @aovchinn's solution still displayed in console the error:

Critical dependency: the request of a dependency is an expression

However I was successful using import "script-loader!mocha/mocha-es2018" (after adding the "script-loader" npm dependency).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests