Vue-test-utils can't find `Set()`: ReferenceError: Can't find variable: Set

Vue-test-utils can't find `Set()`: ReferenceError: Can't find variable: Set

目前我正在尝试为 Vue 组件编写一些测试。但是,当我尝试 运行 测试时,它会出错并显示以下消息:

[BABEL] Note: The code generator has deoptimised the styling of /home/bono/dev/git_repos/hypernode-control-panel/hncp/vue/common/tests/unit/UpdateElementRadio.spec.es5.js as it exceeds the max of 500KB.
13 12 2018 15:52:18.339:INFO [karma-server]: Karma v3.1.3 server started at http://0.0.0.0:9876/
13 12 2018 15:52:18.341:INFO [launcher]: Launching browsers PhantomJS with concurrency unlimited
13 12 2018 15:52:18.344:INFO [launcher]: Starting browser PhantomJS
13 12 2018 15:52:18.594:INFO [PhantomJS 2.1.1 (Linux 0.0.0)]: Connected on socket fBfbs8v8mD1WMsLOAAAA with id 26571362
ERROR LOG: '[Vue warn]: Error in config.errorHandler: "ReferenceError: Can't find variable: Set"'
ERROR LOG: ReferenceError: Can't find variable: Set
patchRenderMixin
callHook
_init
VueComponent
createComponentInstanceForVnode
init
createComponent
createElm
patch
_update
updateComponent
get
Watcher
mountComponent
$mount
mount
shallowMount

callFn@http://localhost:9876/base/node_modules/mocha/mocha.js?840d4fbbcc65a16cec39f9e25af2ea6d2cff68ea:4550:25
run@http://localhost:9876/base/node_modules/mocha/mocha.js?840d4fbbcc65a16cec39f9e25af2ea6d2cff68ea:4542:13
runTest@http://localhost:9876/base/node_modules/mocha/mocha.js?840d4fbbcc65a16cec39f9e25af2ea6d2cff68ea:5078:13
http://localhost:9876/base/node_modules/mocha/mocha.js?840d4fbbcc65a16cec39f9e25af2ea6d2cff68ea:5196:19
next@http://localhost:9876/base/node_modules/mocha/mocha.js?840d4fbbcc65a16cec39f9e25af2ea6d2cff68ea:4992:16
http://localhost:9876/base/node_modules/mocha/mocha.js?840d4fbbcc65a16cec39f9e25af2ea6d2cff68ea:5002:11
next@http://localhost:9876/base/node_modules/mocha/mocha.js?840d4fbbcc65a16cec39f9e25af2ea6d2cff68ea:4926:16
http://localhost:9876/base/node_modules/mocha/mocha.js?840d4fbbcc65a16cec39f9e25af2ea6d2cff68ea:4970:9
timeslice@http://localhost:9876/base/node_modules/mocha/mocha.js?840d4fbbcc65a16cec39f9e25af2ea6d2cff68ea:82:27
ERROR LOG: '[Vue warn]: Error in beforeCreate hook: "ReferenceError: Can't find variable: Set"

found in

---> <UpdateElementRadio> at hncp/vue/common/src/components/UpdateElementRadio.vue
       <Root>'
ERROR LOG: ReferenceError: Can't find variable: Set
patchRenderMixin
callHook
_init
VueComponent
createComponentInstanceForVnode
init
createComponent
createElm
patch
_update
updateComponent
get
Watcher
mountComponent
$mount
mount
shallowMount

callFn@http://localhost:9876/base/node_modules/mocha/mocha.js?840d4fbbcc65a16cec39f9e25af2ea6d2cff68ea:4550:25
run@http://localhost:9876/base/node_modules/mocha/mocha.js?840d4fbbcc65a16cec39f9e25af2ea6d2cff68ea:4542:13
runTest@http://localhost:9876/base/node_modules/mocha/mocha.js?840d4fbbcc65a16cec39f9e25af2ea6d2cff68ea:5078:13
http://localhost:9876/base/node_modules/mocha/mocha.js?840d4fbbcc65a16cec39f9e25af2ea6d2cff68ea:5196:19
next@http://localhost:9876/base/node_modules/mocha/mocha.js?840d4fbbcc65a16cec39f9e25af2ea6d2cff68ea:4992:16
http://localhost:9876/base/node_modules/mocha/mocha.js?840d4fbbcc65a16cec39f9e25af2ea6d2cff68ea:5002:11
next@http://localhost:9876/base/node_modules/mocha/mocha.js?840d4fbbcc65a16cec39f9e25af2ea6d2cff68ea:4926:16
http://localhost:9876/base/node_modules/mocha/mocha.js?840d4fbbcc65a16cec39f9e25af2ea6d2cff68ea:4970:9
timeslice@http://localhost:9876/base/node_modules/mocha/mocha.js?840d4fbbcc65a16cec39f9e25af2ea6d2cff68ea:82:27

  UpdateElementRadio
    ✗ accepts label_text prop
    Can't find variable: Set
    patchRenderMixin
    callHook
    _init
    VueComponent
    createComponentInstanceForVnode
    init
    createComponent
    createElm
    patch
    _update
    updateComponent
    get
    Watcher
    mountComponent
    $mount
    mount
    shallowMount



PhantomJS 2.1.1 (Linux 0.0.0): Executed 1 of 1 (1 FAILED) ERROR (0.041 secs / 0.01 secs)

npm ERR! Test failed.  See above for more details.

这是我正在尝试的测试 运行(请注意,这可能是一个毫无意义的测试,我只是想让事情先行):

import Vue from 'vue'
import { expect } from 'chai';
import { shallowMount } from '@vue/test-utils';
import UpdateElementRadio from '../../src/components/UpdateElementRadio.vue';

describe('UpdateElementRadio', () => {
  it('accepts label_text prop', () => {
    shallowMount(UpdateElementRadio);
    const vm = new Vue(UpdateElementRadio).$mount();
    console.log(vm.props);
    expect(vm.props.label_text).to.equal(String);
  });
});

我在设置这些东西方面不太有经验,但是当我不尝试使用 vue-test-utils 中的任何 mount 函数时一切正常。我觉得我的 babel 设置或其他东西有问题,因为它是 es2015 参考(至少我发现它试图从 lib.es2015 文件中获取它)。我尝试使用不同的 babel 预设,none 确实有效(或出现更多错误)。我还检查了 @babel/preset-env 的文档,我认为这是正确的预设。

我将分享我的业力配置、webpack 配置和 package.json

karma.conf.js:

// Karma configuration
// Generated on Thu Dec 13 2018 11:28:18 GMT+0100 (Central European Standard Time)
let webpackConfig = require('./webpack.config.js');

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

        // base path that will be used to resolve all patterns (eg. files, exclude)
        basePath: '',


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


        // list of files / patterns to load in the browser
        files: [
            'hncp/vue/**/tests/unit/*.spec.js'
        ],


        // preprocess matching files before serving them to the browser
        // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
        preprocessors: {
            'hncp/vue/**/tests/unit/*.spec.js': ['webpack', 'sourcemap', 'babel']
        },


        babelPreprocessor: {
            options: {
                presets: ['@babel/preset-env'],
                sourceMap: 'inline',
            },
            filename: function (file) {
                return file.originalPath.replace(/\.js$/, '.es5.js')
            },
            sourceFileName: function (file) {
                return file.originalPath;
            },
        },


        webpack: webpackConfig,


        // test results reporter to use
        // possible values: 'dots', 'progress'
        // available reporters: https://npmjs.org/browse/keyword/karma-reporter
        reporters: ['spec'],


        plugins: [
            'karma-phantomjs-launcher',
            'karma-webpack',
            'karma-sourcemap-loader',
            'karma-mocha',
            'karma-spec-reporter',
            'karma-babel-preprocessor',
            'karma-chai',
        ],


        // web server port
        port: 9876,


        // enable / disable colors in the output (reporters and logs)
        colors: true,


        // level of logging
        // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
        logLevel: config.LOG_INFO,


        // enable / disable watching file and executing tests whenever any file changes
        autoWatch: true,


        // start these browsers
        // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
        browsers: ['PhantomJS'],


        // Continuous Integration mode
        // if true, Karma captures browsers, runs the tests and exits
        singleRun: true,

        // Concurrency level
        // how many browser should be started simultaneous
        concurrency: Infinity,
    })
};

webpack.config.js:

var path = require('path'); var webpack = require('webpack'); var BundleTracker = require('webpack-bundle-tracker'); const VueLoaderPlugin = require('vue-loader/lib/plugin');

module.exports = {
    context: __dirname,
    mode: 'development',
    entry: {
        'hncp/order': ['./hncp/vue/order/src/main'],
    },
    output: {
        path: path.resolve('./hncp/static/webpack_bundles/'),
        filename: "[name].js"
    },
    module: {
        rules: [
            {  // Needed for loading Vue stuff
                test: /\.vue$/,
                loader: 'vue-loader'
            },
            {  // Needed for loading things like <style> tags (used inside Vue stuff)
                test: /\.css$/,
                loader: 'style-loader!css-loader'
            },
            {  // Needed for loading things like png files (used inside Vue templates)
                test: /\.(eot|svg|png|ttf|woff|woff2)(\?\S*)?$/,
                loader: 'file-loader'
            },
            {
                test: /\.js$/,
                exclude: /(node_modules|bower_components)/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: ['@babel/preset-env']
                    }
                }
            }
        ]
    },

    plugins: [
        new BundleTracker({filename: './webpack-stats.json'}),
        new VueLoaderPlugin()
    ],
};

package.json:

{
    "scripts": {
        "test": "karma start --single-run"
    },
    "dependencies": {
        "babel-preset-es2015": "^6.24.1",
        "karma-cli": "^2.0.0",
        "karma-phantomjs-launcher": "^1.0.4",
        "vue-template-compiler": "^2.5.21"
    },
    "devDependencies": {
        "@babel/core": "^7.2.0",
        "@babel/preset-env": "^7.2.0",
        "@vue/test-utils": "^1.0.0-beta.27",
        "babel-loader": "^8.0.4",
        "chai": "^4.2.0",
        "css-loader": "^2.0.0",
        "file-loader": "^2.0.0",
        "karma": "^3.1.3",
        "karma-babel-preprocessor": "^8.0.0-beta.0",
        "karma-chai": "^0.1.0",
        "karma-chrome-launcher": "^2.2.0",
        "karma-mocha": "^1.3.0",
        "karma-sourcemap-loader": "^0.3.7",
        "karma-spec-reporter": "0.0.32",
        "karma-webpack": "^3.0.5",
        "mocha": "^5.2.0",
        "style-loader": "^0.23.1",
        "vue": "^2.5.21",
        "vue-loader": "^15.4.2",
        "vue-test-utils": "^1.0.0-beta.11",
        "webpack": "^4.27.1",
        "webpack-bundle-tracker": "^0.4.2-beta",
        "webpack-cli": "^3.1.2"
    }
}

如果尝试使用 Vue 中的 $mount,效果很好。即:

import Vue from 'vue'
import { expect } from 'chai';
import { mount } from '@vue/test-utils';
import UpdateElementRadio from '../../src/components/UpdateElementRadio.vue';

describe('UpdateElementRadio', () => {
  it('accepts label_text prop', () => {
    const vm = new Vue(UpdateElementRadio).$mount();
    console.log(vm);
    expect(vm.props.label_text).to.equal(String);
  });
});

我知道要看的东西有点多,但是运行想不出解决这个问题的办法。


编辑

这是UpdateElementRadio:

<template>
    <div class="float-label">
      <label>{{ label_text }}</label>
      <input type="radio" :name="name" v-on:change="$emit('update-value', value)"/>
      <slot></slot>
      <div class="update-element-radio-content" style="float:right">
        {{ value }}
      </div>
    </div>
</template>

<script>
export default {
  name: 'UpdateElementRadio',
  props: {
    label_text: String,
    name: String,
    value: Number
  },
};
</script>

我认为它与 babel 中的 compact 标志有关,它的默认值为 500KB。

您可以在 .babelrc

中更改配置
{
    "compact": false
    // other config
}

如果没有babel文件,可以尝试编辑webpack

rules: [
    {  // Needed for loading Vue stuff
        test: /\.vue$/,
        loader: 'vue-loader',
        compact: false
    },
    {  // Needed for loading things like <style> tags (used inside Vue stuff)
        test: /\.css$/,
        loader: 'style-loader!css-loader',
        compact: false

    },
    {  // Needed for loading things like png files (used inside Vue templates)
        test: /\.(eot|svg|png|ttf|woff|woff2)(\?\S*)?$/,
        loader: 'file-loader'
    },
    {
        test: /\.js$/,
        exclude: /(node_modules|bower_components)/,
        use: {
            loader: 'babel-loader',
            options: {
                presets: ['@babel/preset-env']
            }
        },
        compact: false

    }
]

Babel doc link

所以我弄清楚了为什么会发生这种情况,结果发现罪魁祸首是 PhantomJS。由于它不再被积极开发,这也意味着它不支持 ES6。有关我遇到的相同问题,请参阅 this issue on Github

所以我决定使用 Chrome 进行测试,我做了以下更改:

在karma.conf.js中:

//Other config...

plugins: [
            'karma-chrome-launcher',
            ...
         ]

//Other config...

browsers: ['ChromeHeadless'],

//Other config

在 package.json 中确保 karma-chrome-launcher 已设置:

{
  //other
  "devDependencies": {
    // Other...
    "karma-chrome-launcher": "^2.2.0",
    // Other...
  }
}