Vue Jest test suite failed to run: SyntaxError: Unexpected identifier - vue.js

I've set up my Jest, and it runs properly. But when I create a test for a file that contains a spreadoperator the test suite fails.
I'm using Vue configured from the CLI with Jest.
What I have tried
I've tried adding the babel-plugin-transform-object-rest-spread as a plugin to the babel.config.js but this had no result.
I also tried adding the #babel/plugin-proposal-object-rest-spread as a plugin to the babel.config.js but this also had no result.
babel.config.js:
module.exports = {
presets: ["#babel/preset-env", "#vue/app"]
};
package.json (jest part):
"jest": {
"collectCoverage": true,
"collectCoverageFrom": [
"**/*.{js,vue}",
"!**/node_modules/**"
],
"moduleFileExtensions": [
"js",
"json",
"vue"
],
"moduleNameMapper": {
"^#/(.*)$": "<rootDir>/src/$1"
},
"transform": {
"^.+\\.js?$": "<rootDir>/node_modules/babel-jest",
".*\\.(vue)$": "<rootDir>/node_modules/vue-jest"
}
}
component.spec.js:
import { shallowMount } from '#vue/test-utils';
import Component from '#/views/xx/x/Component.vue';
import Vue from 'vue';
import BootstrapVue from 'bootstrap-vue';
Vue.use(BootstrapVue);
describe('About component', () => {
let wrapper;
beforeEach(() => {
wrapper = shallowMount(Component);
});
test('is a Vue instance', () => {
expect(wrapper.isVueInstance()).toBeTruthy();
});
});
Error:
FAIL src/__tests__/views/x/Component.spec.js
● Test suite failed to run
D:\projects\project\project-frontend\node_modules\#babel\runtime-corejs2\helpers\esm\objectSpread.js:1
({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,global,jest){import _Object$getOwnPropertyDescriptor from "../../core-js/object/get-own-property-descriptor";
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
SyntaxError: Unexpected identifier
at ScriptTransformer._transformAndBuildScript (node_modules/#jest/transform/build/ScriptTransformer.js:471:17)
at ScriptTransformer.transform (node_modules/#jest/transform/build/ScriptTransformer.js:513:25)
at src/views/xx/x/Component.vue:670:49
at Object.<anonymous> (src/views/xx/x/Component.vue:810:3)

In package.json
make sure this pluging"babel-core" version up to ^7.0.0-0 that like under.
"devDependencies": {
"babel-core": "^7.0.0-0"
}

The final step for me was to provide correct env variables in jest config an explanation why it needs it may be found here.Also make sure babel packages installed
Sharing whole config
// jest
process.env.VUE_CLI_BABEL_TARGET_NODE = true;
process.env.VUE_CLI_BABEL_TRANSPILE_MODULES = true;
module.exports = {
verbose: true,
roots: ["<rootDir>/src/", "<rootDir>/spec/"],
moduleFileExtensions: ['js', 'vue','json'],
moduleNameMapper: {
'^#/(.*)$': '<rootDir>/src/$1',
},
transform: {
"^.+\\.js$": "babel-jest",
"^.+\\.vue$": "vue-jest",
},
snapshotSerializers: [
"jest-serializer-vue"
]
}
// in package json
"#babel/core": "^7.9.6",
"#babel/plugin-transform-runtime": "^7.9.6",
"#babel/preset-env": "^7.9.5",
"babel-core": "^7.0.0-bridge.0",
"babel-jest": "^25.4.0",

Related

Why is Vue undefined in my webpack-bundled app after trying to import it as a client-side library

I have been having an issue for 2 days and finally isolated it. Importing Vue and VueRouter gives me undefined in my frontend code. This is because the output from webpack, using the externals property to load client-side libraries, checks for module.__esModule when you import an externally loaded library, and if true then it returns module.default. In this case Vue.__esModule and VueRouter.__esModule are true while Vue.default and VueRouter.default are undefined. I'm not even sure who to file a bug with, or whether there is something I can add to webpack.config.js to make this work as before. It could be Vue for including __esModule on their global builds. Or it could be something in the internals of webpack.
Here is a section of my package.json
"devDependencies": {
"#babel/core": "^7.17.0",
"#babel/preset-env": "^7.16.11",
"#vue/compiler-sfc": "^3.2.30",
"babel-loader": "^8.2.3",
"css-loader": "^6.6.0",
"mini-css-extract-plugin": "^2.5.3",
"node-sass": "^7.0.1",
"sass-loader": "^12.4.0",
"vue-loader": "^17.0.0",
"webpack": "^5.68.0",
"webpack-cli": "^4.9.2"
},
"dependencies": {
"vue": "^3.2.29"
}
And webpack.config.js
const VueLoader = require('vue-loader');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
mode: 'production',
stats: 'errors-warnings',
entry: [
'./src/app.js',
],
output: {
filename: 'compiled.js',
path: __dirname + '/js',
},
optimization: {
minimize: true,
},
performance: {
hints: 'warning',
maxEntrypointSize: 250000, // JS output 250 kB
maxAssetSize: 250000, // CSS output 250 kB
},
externals: {
'vue': 'Vue',
'vuex': 'Vuex',
'vue-router': 'VueRouter',
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
},
{
test: /\.m?js$/,
resolve: {
fullySpecified: false,
},
use: {
loader: 'babel-loader',
options: {
presets: [
['#babel/preset-env', {targets: '>1%'}],
],
},
},
},
{
test: /\.s?css$/,
use: [
MiniCssExtractPlugin.loader, // add support for `import 'file.scss';` in JS
{
loader: 'css-loader',
options: {
url: false, // whether to resolve urls; leave urls in the code as written
},
},
{
loader: 'sass-loader',
options: {
sassOptions: {
includePaths: [
//__dirname + '/bower_components/bootstrap-sass/assets/stylesheets',
],
},
},
},
],
},
],
},
plugins: [
new VueLoader.VueLoaderPlugin(),
new MiniCssExtractPlugin({
// Output destination for compiled CSS
filename: '../css/compiled.css',
}),
],
};
And index.html loads Vue, VueRouter, Vuex, and then my bundled webapp:
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.2.29/vue.runtime.global.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/4.0.2/vuex.global.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue-router/4.0.12/vue-router.global.js"></script>
<script>console.log('healthcheck', Vue, Vuex, VueRouter);</script>
<script src="/js/compiled.js"></script>
Then in my frontend code, bundled with webpack:
import Vue from "vue";
import Vuex from "vuex";
import VueRouter from "vue-router";
console.log('client', Vue, Vuex, VueRouter);
Logs healthcheck {...} {...} {...} in the HTML and then the compiled app logs client undefined, {...}, undefined (Vuex is defined because Vuex.__esModule is undefined)
Any ideas what to do?
Answered here https://github.com/vuejs/core/issues/5380
Vue 3 only supports named imports, like import {createApp} from 'vue';

Why am I getting "Cannot find module..." Typescript error for ".vue" file on webpack-dev-server recompilation?

I've setup a small webpack project which creates a Vue app bundle which is included in a static HTML file where the app is injected. I want to have components written in Typescript so I've included ts-loader in the webapck configuration. The build process - using the "webpack" command - works ok, but I'm having some trouble when I use webpack-dev-server.
When I initially start the server, everything works fine: the bundle is created and served on my local server and the browser displays the app correcly. However, when I make a change in the source code and save, I get a Typescript error when the code is recompiled telling me that a module or declaraton is missing for the ".vue" file for my component:
TS2307: Cannot find module './components/Banner.vue' or its corresponding type declarations.
To start the server I use the following command:
webpack serve --open
Project's folder structure
=======
webpack.config.js
const { VueLoaderPlugin } = require('vue-loader')
const path = require('path')
module.exports = {
mode: 'development',
devtool: 'inline-source-map',
entry: {
app: './src/app.js',
},
output: {
filename: '[name].bundle.js',
},
plugins: [
new VueLoaderPlugin(),
],
devServer: {
contentBase: path.join(__dirname, 'dist'),
},
module: {
rules: [
{
test: /\.vue$/,
use: ['vue-loader']
},
{
test: /\.ts$/,
loader: 'ts-loader',
exclude: [/node_modules/],
options: { appendTsSuffixTo: [/\.vue$/] }
},
],
},
}
app.js
import Vue from 'vue'
import App from './App.vue'
const app = new Vue({
render: (h) => h(App)
})
app.$mount('#app')
App.vue
<template>
<div id="app">
<h1>{{ welcomeMessage }}</h1>
<Banner />
</div>
</template>
<script lang="ts">
import Vue from 'vue'
import Banner from './components/Banner.vue'
export default Vue.extend({
components: {
Banner,
},
data: () => ({
welcomeMessage: 'Hello world!'
})
})
</script>
tsconfig.json
{
"compilerOptions": {
"target": "es5",
"strict": true,
"module": "es2015",
"moduleResolution": "node"
}
}
#types/vue-shims.d.ts
declare module "*.vue" {
import Vue from 'vue'
export default Vue
}
package.json
{
"name": "2021-06-21-webpack",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "webpack",
"dev": "webpack serve --open"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"ts-loader": "^8.3.0",
"typescript": "^4.3.4",
"vue-loader": "^15.9.7",
"vue-template-compiler": "^2.6.14",
"webpack": "^4.46.0",
"webpack-cli": "^4.7.2",
"webpack-dev-server": "^3.11.2"
},
"dependencies": {
"vue": "^2.6.14"
}
}

Vue 3 vendor bundle is bloated with #babel/parser/lib when vue.esm-bundler.js is used in webpack

I created a Vue 3 project and using webpack for bundling the package. Since I have in-DOM templates, I cannot go with the default #runtime-dom. So I have aliased Vue to point to vue.esm-bundler.js.
The issue I am facing is that, when I take a prod build, my vendor bundle is bloated with #babel/parser/lib.
Sample project to reproduce this issue is available here
Steps to follow:
npm install
npm run bundle
Open dist folder and see the Webpack bundle analyser report.
For ease of config, pasting the configs below.
webpack.config.js
const path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer")
.BundleAnalyzerPlugin;
const { VueLoaderPlugin } = require("vue-loader");
const TerserPlugin = require("terser-webpack-plugin");
module.exports = (env, options) => {
const devMode = options.mode != "production";
return {
context: path.resolve(__dirname, "src"),
entry: {
"vue-bundle": "./entry/main.js",
},
output: {
path: path.resolve(__dirname, "dist"),
filename: "[name].js",
chunkFilename: "[name].js",
},
module: {
rules: [
{
test: /\.js$/,
exclude: [/node_modules/],
use: {
loader: "babel-loader",
options: {
presets: [
[
"#babel/preset-env",
{
targets: [">25%"],
debug: true,
corejs: "3.6.5",
useBuiltIns: false,
},
],
],
},
},
},
{
test: /\.vue$/,
use: "vue-loader",
},
],
},
plugins: [
new CleanWebpackPlugin(),
new VueLoaderPlugin(),
new BundleAnalyzerPlugin({
openAnalyzer: false,
analyzerMode: "static",
reportFilename: "webpack_bundle_analyser_report.html",
defaultSizes: "gzip",
}),
],
optimization: {
mangleWasmImports: true,
removeAvailableModules: true,
sideEffects: true,
minimize: devMode ? false : true,
minimizer: [
new TerserPlugin({
test: /\.js(\?.*)?$/i,
exclude: /\/node-modules/,
parallel: 4,
extractComments: false,
}),
],
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: "vendor-bundle",
chunks: "all",
},
},
},
},
devtool: devMode ? "eval-cheap-source-map" : false,
resolve: {
extensions: [".ts", ".js", ".vue", ".json"],
alias: {
vue: "vue/dist/vue.esm-bundler.js"
},
},
};
};
package.json
{
"name": "testPro",
"version": "1.0.0",
"private": true,
"scripts": {
"bundle": "webpack --mode=production --config webpack.config.js",
"bundle-dev": "webpack --mode=development --config webpack.config.js"
},
"devDependencies": {
"#babel/core": "^7.12.3",
"#babel/preset-env": "^7.12.1",
"#babel/preset-typescript": "^7.12.1",
"#vue/compiler-sfc": "^3.0.2",
"babel-loader": "^8.1.0",
"clean-webpack-plugin": "^3.0.0",
"core-js": "^3.6.5",
"regenerator-runtime": "^0.13.7",
"terser-webpack-plugin": "^5.0.3",
"vue-loader": "^16.0.0-beta.4",
"webpack": "^5.3.0",
"webpack-bundle-analyzer": "^3.9.0",
"webpack-cli": "^4.1.0"
},
"dependencies": {
"vue": "^3.0.2"
}
}
Entry file main.js
import { createApp } from 'vue';
import App from '../App.vue';
createApp(App).mount('#app');
Not able to get what I am missing.
I strongly believe it is a bug in Vue 3 so I submitted a bug report - you can track it here
...I reproduced it myself using Vue CLI just to eliminate the chance the problem is in your Webpack config
You have 2 options to workaround this issue:
If you don't need to release right now, just work on your project and wait for a fix (I'm pretty sure it will be fixed - Vue builds for a browser which include compiler does not depend on #babel/parser so it's clear Vue don't need it to work correctly inside browser)
Don't use in-DOM templates and template option (string templates) - put everything in .vue files, <template></template> blocks - Runtime + Compiler vs. Runtime-only. Then you don't need a build with compiler...
EDIT: removed the part about missing process.env.NODE_ENV as --mode param to Webpack CLI does exactly that...

vue.runtime.esm-browser.js does not render Vue 3 components

I created a vue 3 project using Vue cli. I am using a webpack config to manage my build. When I point my vue bundle to vue.runtime.esm-browser.js, then I get a warning in browser console. "[Vue warn]: Component provided template option but runtime compilation is not supported in this build of Vue. Use "vue.esm-browser.js" instead."
When I checked the docs, it was mentioned as "vue-loader" plugin converts the html template to render functions. Looks like I am missing something which is needed to webpack.
Entry file : main.js
import { createApp } from "vue";
import corecomponentA from "../core/components/corecomponentA.vue";
createApp({
components: {
"core-component-a": corecomponentA,
},
}).mount("#app");
Webpack.config.js
var path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const { VueLoaderPlugin } = require("vue-loader");
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer")
.BundleAnalyzerPlugin;
const WebpackBar = require("webpackbar");
module.exports = (env, options) => {
const devMode = options.mode != "production";
return {
entry: {
"vue-bundle-store": "./src/entry/main.js",
},
output: {
path: path.resolve(
__dirname,
"./../ui.clientlibs/src/js/"
),
filename: "[name].js",
chunkFilename: "[name].js",
publicPath: process.env.BASE_URL,
},
module: {
rules: [
{
enforce: "pre",
test: /\.js$/,
exclude: /node_modules/,
loader: "eslint-loader",
},
{
test: /\.vue$/,
loader: "vue-loader",
},
{
test: /\.js$/,
loader: "babel-loader",
exclude: "/node_modules/",
query: {
presets: ["#babel/preset-env"],
},
},
{
test: /\.ts$/,
exclude: /node_modules/,
use: [
{
loader: "babel-loader",
options: { babelrc: true },
},
{
loader: "ts-loader",
options: { appendTsSuffixTo: [/\.vue$/] },
},
],
},
],
},
stats: {
colors: true,
},
optimization: {
splitChunks: {
cacheGroups: {
commons: {
test: /[\\/]node_modules[\\/]/,
name: "vendor-bundle",
chunks: "all",
},
},
},
minimizer: !devMode
? [
new UglifyJsPlugin({
sourceMap: false,
uglifyOptions: {
chunkFilter: (chunk) => {
if (chunk.name === "vendor-bundle") {
return false;
}
return true;
},
compress: {
drop_console: true,
},
mangle: {
reserved: ["vueIns", "args", "el"],
},
},
}),
]
: [],
},
devtool: "source-map",
plugins: [
new CleanWebpackPlugin(),
new VueLoaderPlugin(),
new WebpackBar(),
new BundleAnalyzerPlugin({
analyzerPort: 4000,
openAnalyzer: false,
analyzerMode: "static",
}),
] ,
resolve: {
extensions: [".ts", ".js", ".vue", ".json"],
alias: {
vue: devMode ? "vue/dist/vue.runtime.esm-browser.js" : "vue/dist/vue.runtime.esm-browser.prod.js"
}
}
};
};
coreComponentA.vue
<script lang="ts">
import { h, ref, reactive } from "vue";
export default {
setup() {
const str = ref("Core component B");
const object = reactive({ foo: "bar" });
return () => h("div", [str.value, object.foo]);
}
};
</script>
package.json
{
"name": "vue3.test",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"test:unit": "vue-cli-service test:unit",
"lint": "vue-cli-service lint",
"analyze-bundle": "webpack-bundle-analyzer stats.json",
"bundle": "webpack --mode=production --env.production --config webpack.config.js",
"bundle-dev": "webpack --mode=development --env.production=false --config webpack.config.js",
"stats": "webpack --mode=production --env.production --config webpack.config.js --profile --json > stats.json"
},
"dependencies": {
"vue": "^3.0.2"
},
"devDependencies": {
"#types/jest": "^24.0.19",
"#typescript-eslint/eslint-plugin": "^2.33.0",
"#typescript-eslint/parser": "^2.33.0",
"#vue/cli-plugin-babel": "~4.5.0",
"#vue/cli-plugin-eslint": "~4.5.0",
"#vue/cli-plugin-typescript": "~4.5.0",
"#vue/cli-plugin-unit-jest": "~4.5.0",
"#vue/cli-service": "~4.5.0",
"#vue/compiler-sfc": "^3.0.2",
"#vue/eslint-config-prettier": "^6.0.0",
"#vue/eslint-config-typescript": "^5.0.2",
"#vue/test-utils": "^2.0.0-0",
"clean-webpack-plugin": "^3.0.0",
"core-js": "^3.6.5",
"eslint": "^6.7.2",
"eslint-plugin-prettier": "^3.1.3",
"eslint-plugin-vue": "^7.0.0-0",
"html-webpack-plugin": "^3.2.0",
"prettier": "^1.19.1",
"typescript": "~3.9.3",
"uglifyjs-webpack-plugin": "^2.2.0",
"vue-jest": "^5.0.0-0",
"vue-loader": "^16.0.0-beta.8",
"webpack-cli": "^3.3.10",
"webpackbar": "^4.0.0"
}
}
babel.config.js
module.exports = {
ignore: [/\/core-js/],
presets: [
[
"#babel/preset-env",
{ modules: false, useBuiltIns: "usage", corejs: "3.6.5" },
],
],
overrides: [
{
test: "./node_modules",
sourceType: "unambiguous",
},
],
};
Usage of my component in a html file
<div id="app">
<core-component-a></core-component-a>
</div>
The component is not rendered in browser. Instead the below message is displayed.
VM211871:1 [Vue warn]: Component provided template option but runtime compilation is not supported in this build of Vue. Use "vue.esm-browser.js" instead.
at <App>
vue-loader converts the html template to render function only in SFC's (Sinle File Components) - .vue files (as you can tell from vue rule in Webpack config) - and only templates provided in <template></template> block of SFC
But you have a template in your HTML file - content of <div id="app"> is essentially Vue template. Runtime + Compiler vs. Runtime-only
Docs vue.esm-bundler.js: includes the runtime compiler. Use this if you are using a bundler but still want runtime template compilation (e.g. in-DOM templates or templates via inline JavaScript strings - component template option).
Also if you using Webpack, you should use "bundler" version of Vue
Webpack.config.js
alias: {
vue: "vue/dist/vue.esm-bundler.js"
}
...you don't need to switch minified/dev bundle because Webpack will (when configured correctly) optimize Vue code same way as your own code..
Also, pay attention to this sentence in the docs: Leaves prod/dev branches with process.env.NODE_ENV guards (must be replaced by bundler)
NODE_ENV is conventionally used to define the environment type and is used by Vue to decide what code to include...
Note
I don't really understand why are You using your own Webpack config for project created with Vue CLI when whole point of Vue CLI is to manage webpack config for you and offers plenty of options to customize it...doesn't make any sense
If you are using Vite, add the alias 'vue': 'vue/dist/vue.esm-bundler' to vite.config.js
import { defineConfig } from 'vite'
import vue from '#vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
'vue': 'vue/dist/vue.esm-bundler',
},
}
})
you just have to replace on Webpack.config.js
alias: {
vue: devMode ? "vue/dist/vue.runtime.esm-browser.js" : "vue/dist/vue.runtime.esm-browser.prod.js"
}
With
vue: "vue/dist/vue.esm-bundler.js"
This works for me.

(JEST/VueJS) Cannot use import statement outside a module

I'm using windows10 and when I run Jest on VUEJS project for tests i've got this :
({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,global,jest){import { shallowMount } from '#vue/test-utils';
^^^^^^
SyntaxError: Cannot use import statement outside a module
at Runtime.createScriptFromCode (node_modules/jest-runtime/build/index.js:1086:14)
Itried a lot of soltions, but still not working Here is my jest.config.js file:
module.exports = {
moduleFileExtensions: ['js', 'jsx', 'json', 'vue'],
transform: {
"^.+\\.js$": "babel-jest",
".+\\.(css|styl|less|sass|scss|png|jpg|ttf|woff|woff2)$": "jest-transform-stub"
},
moduleNameMapper: {
"^.+.(css|styl|less|sass|scss|png|jpg|ttf|woff|woff2)$": "jest-transform-stub"
},
snapshotSerializers: [
"<rootDir>/node_modules/jest-serializer-vue"
],
testMatch: [
'<rootDir>/tests/**/*.spec.(js|jsx|ts|tsx)|<rootDir>/**/__tests__/*.(js|jsx|ts|tsx)'
],
transformIgnorePatterns: ['<rootDir>/node_modules/']
};