Karma + Webpack (babel-loader) + ES6 “Unexpected t

2019-02-04 06:45发布

问题:

/edit: I stripped the config completely to a minimum demonstrating the problem. I also uploaded a working project to GitHub that you can checkout, so you can see for yourself.

Problematic use case on GitHub: webpack-angular15-es6-karma (download 7z archive)

npm install
npm run build
npm run test

I know there are a couple of related questions, but the world is moving so fast and there are so many factors / dependencies that I'm not able to crack this problem with the current proposed solutions.

I have a webpack config that takes care of all my source code and it works great.

For testing, I reuse that config to take care of a single central entry script that loads both the sources and the test files. I could not find another way to import modules from my source code into my test code for testing.

Karma builds fine reusing my webpack config, but the browser reports an error as soon as it is opened.

The source code uses ES6 imports and webpack require statements.

package.json:

npm run build >>> webpack --config webpack.config.js --display-error-details --colors --progress

npm run test >>> karma start --single-run --no-auto-watch karma.config.js

{
  "name": "ProblemDemo",
  "scripts": {
    "build": "rimraf dist && webpack --config webpack.config.js --display-error-details --colors --progress",
    "test": "karma start --single-run --no-auto-watch karma.config.js"
  },
  "dependencies": {
    "angular": "^1.5.7",
    "angular-filter": "^0.5.8"
  },
  "devDependencies": {
      "webpack": "1.13.1",
      "html-loader": "0.4.3",
      "babel-loader": "5.3.2",
      "html-webpack-plugin": "1.6.1",
      "rimraf": "^2.5.3",
      "run-sequence": "1.1.2",
      "jasmine-core": "^2.4.1",
      "karma": "^0.13.19",
      "karma-chrome-launcher": "^0.2.2",
      "karma-coverage": "^0.5.3",
      "karma-jasmine": "^0.3.6",
      "karma-webpack": "^1.7.0",
      "loader-utils": "^0.2.12"
  }
}

karma.config.js:

module.exports = function (config) {
    config.set({
        browsers: ['Chrome'],
        coverageReporter: {
            reporters: [
                { type: 'html', subdir: 'html' },
                { type: 'lcovonly', subdir: '.' }
            ]
        },
        files: ['./tests.webpack.js'],
        frameworks: ['jasmine'],
        preprocessors: { './tests.webpack.js': ['webpack'] },
        reporters: ['progress', 'coverage'],
        webpack: configureWebpack()
    });

    function configureWebpack(webpackConfigFunction) {
        var webpackConfig = require('./webpack.config');
        webpackConfig.entry = undefined; // karma will pass the proper argument for entry
        return webpackConfig;
    }
};

As you can see I'm not using karma-babel plugins: I'm not sure why I would need those, seeing I already have a working build for code with import / require statements.

test_entry.js:

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

var srcContext = require.context('./app', true, /\.js$/);
srcContext.keys().forEach(srcContext);

The webpack build succeeds without a hitch (and emits test_entry.js chunk of expected file size), but then Karma opens Chrome and as soon as the page is loaded I'm greeted with the following error:

Chrome 51.0.2704 (Windows 7 0.0.0) ERROR

Uncaught SyntaxError: Unexpected token import

at the_path/test_entry.js:41

test_entry.js doesn't have 41 lines and doesn't contain import statements and anyway they should have been taken care of. What's going wrong?

In case you want to know the webpack config as well:

// webpack.config.js, works perfectly for normal builds but not with Karma
var path = require('path');
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
	debug: true,
	entry: {
		app: ['./app/index.js'],
		vendor: ['./app/vendor.js']
	},
	output: {
		path: path.join(__dirname, 'dist'),
		filename: 'js/[name].js'
	},
	plugins: [
		new HtmlWebpackPlugin({
			template: './app/index.html',
			inject: 'body',
			minify: false
		}),
		new webpack.optimize.CommonsChunkPlugin('vendor', 'js/vendor.js'),
		new webpack.SourceMapDevToolPlugin({
			filename: '[file].map',
			exclude: /vendor/
		})
	],
	resolve: {
		extensions: ['', '.js'],
		alias: {
			app: path.join(__dirname, 'app')
		}
	},
	module: {
		loaders: [
			{
				test: /\.js$/,
				loader: 'babel-loader',
				include: path.join(__dirname, 'app'),
				exclude: path.join(__dirname, 'node_modules')
			},
			{
				test: /\.html$/,
				loader: 'html-loader'
			}
		]
	},
	resolveLoader: {
		root: path.join(__dirname, 'node_modules')
	},
};

In case you want to see my test, required by test_entry.js, which I can't get to run:

import jasmine from 'jasmine-core';
import readableNumberFilter from './file_path/readableNumber.filter';

// for some reason needed, or else unexpected token errors during build:
var describe = jasmine.describe;
var it = jasmine.it;
var expect = jasmine.expect;

describe('readableNumber Filter', function () {
	describe('readableNumber Filter formatting', () => {
		it('it should support optional arguments', function () {
			expect(readableNumberFilter("50.3")).toEqual("50,30");
		});
	});
});


edit 20-7-2016

The problem persists with updated babel-loader dependencies as well (including setting es2015 presets option). "babel-core": "^6.11.4", "babel-loader": "^6.2.4", "babel-preset-es2015": "^6.9.0",

回答1:

The problem is that you are not transpiling you test sources - file src/global/filters/dateFormat.filter.spec.js was not transpiled.

You need to change this loader config:

{
    test: /\.js$/,
    loader: 'babel-loader',
    include: path.join(__dirname, 'app'),
    exclude: path.join(__dirname, 'node_modules')
},

As following, to make it work:

{
    test: /\.js$/,
    loader: 'babel-loader',
    include: [
        path.join(__dirname, 'app'),
        path.join(__dirname, 'test')
    ],
    exclude: path.join(__dirname, 'node_modules')
},

There is also issue with webpack.optimize.CommonsChunkPlugin plugin - it should be disabled for karma: https://github.com/webpack/karma-webpack/issues/22

Than after disabling this plugin, there is mistakes in your test file:

import jasmine from 'jasmine-core';

import readableNumberFilter from 'readableNumber.filter';

var describe = jasmine.describe;
var it = jasmine.it;
var expect = jasmine.expect;

describe('readableNumber Filter', function () {
    describe('readableNumber Filter formatting', () => {
        it('it should support optional arguments', function () {
            expect(readableNumberFilter("50.3")).toEqual("50,30");
        });
    });
});

there is few errors in this test:

  1. import jasmine from 'jasmine-core'; - you should not do that (karma will do, also it will add describe, it and expect)
  2. import readableNumberFilter from 'readableNumber.filter'; - it is not the way how you can instantiate angular services, to test them.

You should do something like, this(this test actually works with all fixes mentioned above):

import angular from 'angular';
import 'angular-mocks';

import testModule from '../../../../app/src/global/index';

const { module, inject } = angular.mock;

describe('readableNumber Filter', () => {
    beforeEach(module(testModule));

    let $filter;

    beforeEach(inject((_$filter_) => {
        $filter = _$filter_;
    }));

    describe('readableNumber Filter formatting', () => {
        it('it should support optional arguments', () => {
            const result = $filter('readableNumber')("50.3");
            expect(result).toEqual("50,30");
        });
    });
});

Notice: you will need to install module angular-mocks;


To support code coverage reporting you will need to configure you test webpack configuration to use something like babel-istanbul-loader. By the way you will also need to upgrade to Babel6.

Also you will need to make webpack config more configurable(configurations for testing and production need do be slightly different).

I have sent you a pull-request with all these fixes: https://github.com/bbottema/webpack-angular15-es6-karma/pull/1


About building angular 1.x project with webpack, including testing with code coverage via karma - maybe you would be interested in my project: https://github.com/zxbodya/angular-webpack-seed - it is starter template with all required configurations.