SyntaxError: Unexpected token 'const' (wit

2020-02-11 05:21发布

问题:

I have a more-or-less vanilla Laravel + Vue.js app and I am trying to do some JS testing with Karma and Jasmine. If I try to use () => {} style functions or keywords like const in my tests, they fail with Unexpected token errors, however, I do not have a problem using the import keyword and I am able to transpile and work with .vue files without issue.

A trivial assertion like

expect(true).toBe(true);

Seems to work fine (see last line)

$ ./node_modules/karma/bin/karma start
22 11 2016 11:09:23.250:INFO [karma]: Karma v1.3.0 server started at http://localhost:9876/
22 11 2016 11:09:23.254:INFO [launcher]: Launching browser PhantomJS with unlimited concurrency
22 11 2016 11:09:23.263:INFO [launcher]: Starting browser PhantomJS
22 11 2016 11:09:24.025:INFO [PhantomJS 2.1.1 (Mac OS X 0.0.0)]: Connected on socket /#U1dCZ7i3UtsC-M3_AAAA with id 84458811
PhantomJS 2.1.1 (Mac OS X 0.0.0): Executed 1 of 1 SUCCESS (0.004 secs / 0.002 secs)

Then, if I add a trivial const declaration to my test function

const myVar = 1
expect(true).toBe(true);

I get an error:

$ ./node_modules/karma/bin/karma start
22 11 2016 11:10:00.741:INFO [karma]: Karma v1.3.0 server started at http://localhost:9876/
22 11 2016 11:10:00.745:INFO [launcher]: Launching browser PhantomJS with unlimited concurrency
22 11 2016 11:10:00.752:INFO [launcher]: Starting browser PhantomJS
22 11 2016 11:10:01.659:INFO [PhantomJS 2.1.1 (Mac OS X 0.0.0)]: Connected on socket /#Gwh8ywcLStrKf-ljAAAA with id 78654911
PhantomJS 2.1.1 (Mac OS X 0.0.0) ERROR
  SyntaxError: Unexpected token 'const'
  at tests/unit/Example.spec.js:165

Instead, if I try to cause a syntax error

const  = 1   // syntax error
expect(true).toBe(true);

then Babel complains (on the first line, before Karma or PhantomJS boots up)

22 11 2016 11:07:00.079:ERROR [preprocessor.babel]: /Users/crcarter/Software/CropPlanning/cps-php/resources/assets/js/tests/unit/Example.spec.js: Unexpected token (8:15)
 at /Users/crcarter/Software/CropPlanning/cps-php/resources/assets/js/tests/unit/Example.spec.js
22 11 2016 11:07:00.090:INFO [karma]: Karma v1.3.0 server started at http://localhost:9876/
22 11 2016 11:07:00.091:INFO [launcher]: Launching browser PhantomJS with unlimited concurrency
22 11 2016 11:07:00.101:INFO [launcher]: Starting browser PhantomJS
22 11 2016 11:07:00.986:INFO [PhantomJS 2.1.1 (Mac OS X 0.0.0)]: Connected on socket /#9Y6QLVxtJ57qRrgDAAAA with id 56249014
PhantomJS 2.1.1 (Mac OS X 0.0.0) ERROR
  You need to include some adapter that implements __karma__.start method!

This seems to imply that the Example.spec.js file is being parsed/transpiled by Babel, but that the transpiled version is not being delivered to the browser properly, even though the Example.vue file seems to be delivered properly.

What can I do to be able to get things like const and () => {} to work in my tests? Thank you.

Here are the relevant files:

resources/assets/js/components/Example.vue
resources/assets/js/tests/unit/Example.spec.js
karma.conf.js                             
package.json

// Example.vue
<template>
    <div class="container">
    </div>
</template>

<script>
    export default {
        mounted() {
            console.log('Component ready.')
        },
        data() {
            return { input: '# Hello!' }
        }
    }
</script>

// Example.spec.js
import Example from '../../components/Example.vue';

describe('Example', function () {
    it('should set correct default data', function () {

        const myVar = 1

        // trivial assertions
        expect(true).toBe(true);
    });
});

// karma.conf.js
var path = require('path')
var basePath = './resources/assets/js/';

module.exports = function(config) {
    config.set({

        frameworks: ['jasmine'],
        port: 9876,
        logLevel: config.LOG_INFO,
        autoWatch: true,
        browsers: ['PhantomJS'],
        singleRun: true,
        basePath: basePath,

        webpack: {
            resolve: {
                extensions: ['', '.js', '.vue'],
                fallback: [path.join(__dirname, 'node_modules')],
            },
            resolveLoader: {
                fallback: [path.join(__dirname, 'node_modules')]
            },
            module: {
                loaders: [
                  { test: /\.vue$/, loader: 'vue' },
                  { test: /\.js$/,  loader: 'babel',
                    include: basePath,
                  }
                ]
            }
        },

        webpackMiddleware: {
            noInfo: true,
            stats: 'errors-only'
        },

        files: [
            { pattern: 'tests/**/*.spec.js', watched: false },
        ],

        exclude: [],

        preprocessors: {
            'app.js': ['webpack', 'babel'],
            'tests/**/*.spec.js': [ 'babel', 'webpack' ]
        },

    })
}

And package.json

{
  "private": true,
  "scripts": {
    "prod": "gulp --production",
    "dev": "gulp watch"
  },
  "devDependencies": {
    "bootstrap-sass": "^3.3.7",
    "gulp": "^3.9.1",
    "jquery": "^3.1.0",
    "laravel-elixir": "^6.0.0-11",
    "laravel-elixir-vue-2": "^0.2.0",
    "laravel-elixir-webpack-official": "^1.0.2",
    "lodash": "^4.16.2",
    "vue": "^2.0.1",
    "vue-resource": "^1.0.3"
  },
  "dependencies": {
    "jasmine-core": "^2.5.2",
    "karma": "^1.3.0",
    "karma-babel-preprocessor": "^6.0.1",
    "karma-chrome-launcher": "^2.0.0",
    "karma-firefox-launcher": "^1.0.0",
    "karma-jasmine": "^1.0.2",
    "karma-phantomjs-launcher": "^1.0.2",
    "karma-webpack": "^1.8.0"
  }
}

回答1:

The comments by @craig_h and @PanJunjie set me on the track of looking at the configuration for karma-babel-preprocessor, which led me to the config for karma-webpack. I'm still not sure what was causing the original problem, but it appears that my webpack config for Karma was incorrect or incomplete, and was failing silently. I added babel-loader and babel-preset-es2015 packages via

yarn add babel-loader babel-preset-es2015

And then I redid and cleaned up my karma.conf.js as such:

module.exports = function(config) {

    config.set({

        singleRun: false, // false => watch for changes and rerun tests
        autoWatch: true, // enable / disable watching files & then run tests

        frameworks: ['jasmine'],
        browsers: ['PhantomJS'],

        // Options: LOG_DISABLE, LOG_ERROR, LOG_WARN, LOG_INFO, LOG_DEBUG
        logLevel: config.LOG_INFO,

        basePath: './resources/assets/js/',

        files: [
            { pattern: 'tests/**/*.spec.js', watched: false },
        ],

        // how to process files before serving them to the browser for testing
        preprocessors: {
            'app.js': ['webpack'],
            'tests/**/*.spec.js': ['webpack'],
        },

        webpack: {
            module: {
                loaders: [
                    { test: /\.vue$/, loader: 'vue' },
                    { test: /\.js$/,
                      loader: 'babel-loader',
                      exclude: /node_modules/,
                      query: { presets: ['es2015'] }
                    }
                ]
            },
            // make sure to use the stand-alone version of Vue
            resolve: {
                alias: {vue: 'vue/dist/vue.js'}
            }
        },

        webpackMiddleware: {
          noInfo: true,
          stats: 'errors-only'
        }
    });
};

My package.json now looks like this:

{
  "private": true,
  "scripts": {
    "prod": "gulp --production",
    "dev": "gulp watch"
  },
  "devDependencies": {
    "bootstrap-sass": "^3.3.7",
    "gulp": "^3.9.1",
    "jquery": "^3.1.0",
    "laravel-elixir": "^6.0.0-11",
    "laravel-elixir-vue-2": "^0.2.0",
    "laravel-elixir-webpack-official": "^1.0.2",
    "lodash": "^4.16.2",
    "vue": "^2.0.1",
    "vue-resource": "^1.0.3"
  },
  "dependencies": {
    "babel-loader": "^6.2.8",
    "babel-preset-es2015": "^6.18.0",
    "jasmine-core": "^2.5.2",
    "karma": "^1.3.0",
    "karma-babel-preprocessor": "^6.0.1",
    "karma-chrome-launcher": "^2.0.0",
    "karma-firefox-launcher": "^1.0.0",
    "karma-jasmine": "^1.0.2",
    "karma-phantomjs-launcher": "^1.0.2",
    "karma-webpack": "^1.8.0"
  }
}

With all of that, I can now use all of the ES2015 goodness like const and () => {}. Sorry to answer my own question, but I hope this helps someone else that encounters a similar issue.



回答2:

I had a similar problem (though not identical; your configuration is pretty specific). I was also using Vue, karma, webpack, and phantomjs (as configured in the vue-Webpack template).

However, my problem is that I was defining const in a helper file that was getting imported into my app. When I changed const to var in that file, the tests were able to run.(It didn't matter whether or not const was used in other files that were already within the src directory).

This problem was fixed when I moved this helper file into my src folder or one of its subdirectories. I'm too much of a novice to know why this solved the problem, but I'm guessing that babel wasn't configured to work in the root folder, and was only pointed towards the src folder.

Hopefully this is helpful for others as well.



回答3:

In addition to RyanQuey's comment: he is right. The default Vue + Webpack CLI set-up only includes certain contexts to be handled by the babel-loader. Look into build/webpack.base.conf.js and then to the module rule for JS-files. You can see that only the src, test and node_modules/webpack-dev-server/client paths are included (at the time of this writing).