taylorhakes / painless

Painless Test Library - Easy to learn, use and debug
MIT License
58 stars 3 forks source link

Using Painless with Webpack #20

Open PhiLhoSoft opened 8 years ago

PhiLhoSoft commented 8 years ago

Beware: I am new to Painless and to Webpack! I have some experience with Karma, though...

I am trying to use Webpack for a small AngularJS 1 project, with ES5. So I made a little variant of the https://github.com/preboot/angular-webpack project. Instead of using Jasmine, I try to use Painless. (BTW, the choice of name is poor, if I search painless test or similar, I get lot of wrong hits... :anguished:)

The project itself works, I can get it running in the browser. Things get trickier when I try to test... So, I would appreciate if you could give some instructions, or even better some sample files, showing how to use Painless with Webpack and Karma.

PhiLhoSoft commented 8 years ago

Some relevant extracts of the project files:

package.json

    "karma": "^0.13.14",
    "karma-webpack": "^1.7.0",
    "karma-sourcemap-loader": "^0.3.7",
    "karma-painless": "^1.3.0",
    "karma-coverage": "^0.5.3",
    "karma-phantomjs-launcher": "^1.0.0",
    "phantomjs-prebuilt": "^2.1.4",
    "angular-mocks": "^1.5.0",

    "painless": "^0.9.5",

    "webpack": "^1.12.13",
    "webpack-dev-server": "^1.14.1",

webpack.config.js

module.exports = function makeWebpackConfig()
{
    var config = {};

    /**
     * Entry
     * Reference: http://webpack.github.io/docs/configuration.html#entry
     * Should be an empty object if it's generating a test build
     * Karma will set this when it's a test build
     */
    config.entry = isTest ? {} :
    {
        app: './src/app/app.js'
    };

    /**
     * Output
     * Reference: http://webpack.github.io/docs/configuration.html#output
     * Should be an empty object if it's generating a test build.
     * Karma will handle setting it up for you when it's a test build.
     */
    config.output = isTest ? {} :
    {
        // Absolute output directory
        path: __dirname + '/dist',

        // Output path from the view of the page
        // Uses webpack-dev-server in development
        publicPath: isProd ? '/' : 'http://localhost:8080/',

        // Filename for entry points
        // Only adds hash in build mode
        filename: isProd ? '[name].[hash].js' : '[name].bundle.js',

        // Filename for non-entry points
        // Only adds hash in build mode
        chunkFilename: isProd ? '[name].[hash].js' : '[name].bundle.js'
    };

    config.module =
    {
        preLoaders: [],
        loaders:
        [
            {
                test: /\.css$/,
                loader: isTest ? 'null' : ExtractTextPlugin.extract('style', 'css?sourceMap!postcss')
            },
            {
                test: /\.(png|jpg|jpeg|gif|svg|woff|woff2|ttf|eot)$/,
                loader: 'file'
            },
            {
                test: /\.html$/,
                loader: 'raw'
            }
        ],
        noParse:
        [
            /sinon\.js/,
            /painless/,
        ]
    };

    if (isTest)
    {
        config.module.preLoaders.push(
        {
            test: /\.js$/,
            include: __dirname + '/src/app/',
            exclude:
            [
                /node_modules/,
                /\.test\.js$/
            ],
            loader: 'istanbul-instrumenter'
        });
    }
// [...]
};

karma.conf.js

module.exports = function karmaConfig(config)
{
    var plugins =
    [
        'karma-webpack', 'karma-sourcemap-loader',
        'karma-painless',
        'karma-phantomjs-launcher', 'karma-coverage',
    ];
    var reporters = [ 'dots', 'coverage' ];

    var configuration =
    {
        frameworks:
        [
            'painless'
        ],

        plugins: plugins,
        reporters: reporters,

        files:
        [
            'src/tests.webpack.js'
        ],
        exclude: [],

        preprocessors:
        {
            'src/tests.webpack.js': [ 'webpack', 'sourcemap' ]
        },

        browsers: [ 'PhantomJS' ],

        autoWatch: false,
        singleRun: true,

        coverageReporter:
        {
            dir: 'coverage/',
            reporters:
            [
                { type: 'text-summary' },
                { type: 'html' }
            ]
        },

        webpack: require('./webpack.config'),

        // Hide Webpack build information from output
        webpackMiddleware:
        {
            noInfo: 'errors-only'
        }
    };
    config.set(configuration);
};

tests.webpack.js

require('angular');
require('angular-mocks/angular-mocks');

var testsContext = require.context(".", true, /.test$/);
testsContext.keys().forEach(testsContext);

As you can see, I have set as "noParse" both Sinon and Painless, to avoid Webpack to parse them. Not sure if that's the right way... Anyway, with this setting, I have the following error:

PhantomJS 2.1.1 (Windows 8 0.0.0) ERROR
  ReferenceError: Can't find variable: require
  at D:/3rdPartySources/angular-webpack-playground/src/tests.webpack.js:34281 <- webpack:///~/painless/index.js:1:0

OK, require is not understood because Webpack couldn't parse it. But if I remove Painless from the noParse setting, Webpack complains that require is called with a non-constant expression:

WARNING in ./~/painless/index.js
Critical dependencies:
58:19-121 the request of a dependency is an expression
 @ ./~/painless/index.js 58:19-121

Beside, it tries to parse (and complains) all files at top-level of node_modules/painless.

WARNING in ./~/painless/index.js
Critical dependencies:
58:19-121 the request of a dependency is an expression
 @ ./~/painless/index.js 58:19-121

WARNING in ./~/painless/package.json
Module parse failed: D:\Sources\angular-webpack-playground\node_modules\painless\package.json Unexpected token (2:9)
You may need an appropriate loader to handle this file type.
SyntaxError: Unexpected token (2:9)
[...]
WARNING in ./~/painless/CHANGELOG.md
Module parse failed: D:\Sources\angular-webpack-playground\node_modules\painless\CHANGELOG.md Unexpected character '#' (1:0)
You may need an appropriate loader to handle this file type.
SyntaxError: Unexpected character '#' (1:00)
[...]

What I am missing?

PhiLhoSoft commented 8 years ago

Mmm, seems relevant: https://github.com/sinonjs/sinon/issues/555 and https://github.com/webpack/webpack/issues/304 Ugh! Ugly... I wouldn't like to go back to Jasmine (it is OK, but sucks at reporting differences in objects, among other problems).

taylorhakes commented 8 years ago

Thanks for reporting. This appears to be an issue with painless and webpack. Painless has a dynamic require that forces webpack to include all files in the folder. I will work to fix this.

There is also an issue with sinon using UMD module definitions. I will have to fix that as well. It is going to take a few days.

Browserify works currently. You can either use that or wait a few days for a fix.

PhiLhoSoft commented 8 years ago

I will wait... :persevere: I thought it worked out of the box with Webpack, as you mention it several times in the readme... :expressionless:

For Sinon, from what I have read, the version 2.0 should fix that (but it isn't out yet), and meanwhile, the trick I found might do the right thing:

    noParse:
    [
        /sinon\.js/, // Don't load sinon's source and its problematic requires
    //  /painless/,
    ],
    resolve:
    {
        alias:
        {
            // Loads the bundled version instead
            // (see https://github.com/webpack/webpack/issues/304)
            'sinon': 'sinon/pkg/sinon'
        }
    }

Side note: you mention "It is possible to switch out assertion and mocking libraries". For clueless people like me, it would be nice to show how... At least if we want to upgrade Chai or Sinon. :smile:

taylorhakes commented 8 years ago

I mentioned webpack once in karma-painless. I will remove it from the readme until it's fixed.

In order to use another mocking library, you can just install another library via NPM and require it yourself. Sinon and chai will still be included as well, so that won't fix your issue. You can also do the alias thing you are doing above with browserify or webpack (when it's fixed).

BarthesSimpson commented 6 years ago

Is this fixed now?