Jest import common plugins like Vee Validate before each test - vue.js

I'm new to Jest and am testing my Nuxt JS application with unit testing. I have many test files set up and need to import various logic into each test file, such as my Vee Validate config.
I've tried adding it to a setup.js file and included this in setupFilesAfterEnv but it's not getting included.
What am I doing wrong?
tests/unit/setup.js
import Vue from 'vue'
import { ValidationProvider, ValidationObserver, extend, configure, localize } from 'vee-validate'
import * as rules from 'vee-validate/dist/rules';
import en from 'vee-validate/dist/locale/en.json'
const config = {
mode: 'lazy',
classes: {
valid: '',
invalid: 'tw-border-red-500 dark:tw-border-white'
}
}
Object.keys(rules).forEach(rule => {
extend(rule, rules[rule]);
});
configure(config)
// Register it globally
Vue.component('ValidationObserver', ValidationObserver)
Vue.component('ValidationProvider', ValidationProvider)
// // activate the locales
localize({
en
});
My jest.config.js file:
module.exports = {
silent: true,
testEnvironment: 'jsdom',
moduleNameMapper: {
'^#/(.*)$': '<rootDir>/$1',
'^~/(.*)$': '<rootDir>/$1',
'^vue$': 'vue/dist/vue.common.js',
},
moduleFileExtensions: ['js', 'vue', 'json'],
transform: {
'^.+\\.js$': 'babel-jest',
'.*\\.(vue)$': 'vue-jest',
'vee-validate/dist/rules': 'babel-jest'
},
collectCoverage: true,
collectCoverageFrom: [
'<rootDir>/components/**/*.vue',
'<rootDir>/pages/**/*.vue',
],
transformIgnorePatterns: [
'<rootDir>/node_modules/(?!vee-validate/dist/rules)',
],
setupFilesAfterEnv: [
'<rootDir>/tests/unit/setup.js'
]
}
And one of my test files called LoanAmount is:
import { mount } from '#vue/test-utils'
import flushPromises from 'flush-promises';
import LoanAmount from '#/components/Form/Steps/Loan/LoanAmount'
describe('LoanAmount', () => {
test('is a Vue instance', () => {
const wrapper = mount(LoanAmount)
expect(wrapper.vm).toBeTruthy()
})
test('amount is available for value', async () => {
const wrapper = mount(LoanAmount)
await flushPromises()
const amount = wrapper.findAll('.jest__amount-input')
expect(amount.exists()).toBe(true)
})
})
If I include all of the contents from tests/unit/setup.js and include above my describe block then it works, but I can't be repeating this in each test file.

You can use the setupFiles property in jest.config.js:
module.exports = {
rootDir: path.resolve(__dirname, '../../'),
moduleFileExtensions: [
'js',
'json',
'vue'
],
transform: {
'^.+\\.js$': 'babel-jest',
'.*\\.(vue)$': 'vue-jest',
'.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub',
'vee-validate/dist/rules': 'babel-jest'
},
transformIgnorePatterns: [
'<rootDir>/node_modules/(?!(vuetify)|(vue-material-design-icons)|(vee-validate/dist/rules)|(vue-picture-input/PictureInput.vue))'
],
setupFiles: [
'./tests/unit/jest.setup.js'
],
reporters: ['default', 'jest-junit'],
collectCoverageFrom: [
'src/**/*.{js,vue}',
'!src/**/Application/*.js'
],
testPathIgnorePatterns: [
'<rootDir>/node_modules/',
'<rootDir>/vendor/',
'<rootDir>/web/'
],
coverageThreshold: {
global: {
branches: process.env.COVERAGE_THRESHOLD_GLOBAL_BRANCH || 37,
functions: process.env.COVERAGE_THRESHOLD_GLOBAL_FUNCTIONS || 40,
lines: process.env.COVERAGE_THRESHOLD_GLOBAL_LINES || 50
}
}
}

Related

vue-pdf-embed Inline worker is not supported on jest

I am using Vue 3 + Jest 28.
I've decided do try vue-pdf-embed, which worked great.
The problem is when I run jest.
It says
Inline worker is not supported
With this I can't proceed and got stuck.
My jest.config.ts looks like this:
const esModules = ['quasar', 'quasar/lang', 'lodash-es', 'cnpj'].join('|');
module.exports = {
verbose: true,
testEnvironment: 'jsdom',
testEnvironmentOptions: {
url: 'http://localhost/',
customExportConditions: ['node', 'node-addons'],
},
globals: {
'ts-jest': {
tsconfig: '<rootDir>/tests/tsconfig.json',
isolatedModules: true,
},
},
collectCoverageFrom: ['src/**/*.{vue,js,ts}', '!src/*.{js,ts}', '!**/typings/**', '!src/**/definitions/*.ts'],
coverageProvider: 'v8',
setupFiles: [
'<rootDir>/tests/jest.init.ts',
],
setupFilesAfterEnv: ['<rootDir>/tests/setupTests.ts'],
moduleFileExtensions: [
'vue',
'js',
'ts',
'json',
'jsx',
'tsx',
],
transform: {
// See https://jestjs.io/docs/en/configuration.html#transformignorepatterns-array-string
[`^(${esModules}).+\\.js$`]: 'babel-jest',
'^.+\\.(ts|js|html)$': 'ts-jest',
// vue-jest uses find-babel-file, which searches by this order:
// (async) .babelrc, .babelrc.js, package.json, babel.config.js
// (sync) .babelrc, .babelrc.js, babel.config.js, package.json
// https://github.com/tleunen/find-babel-config/issues/33
'.*\\.vue$': '#vue/vue3-jest',
'.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$':
'jest-transform-stub',
},
transformIgnorePatterns: [`node_modules/(?!(${esModules}))`],
moduleNameMapper: {
'#/(.*)$': '<rootDir>/src/$1',
'^quasar$': 'quasar/dist/quasar.esm.prod.js',
'lodash-es': 'lodash',
},
snapshotSerializers: ['<rootDir>/node_modules/jest-serializer-vue'],
};
I've tried so far to add it to the esModules variable I have in my jest config but no success
I came with a reasonable answer: mock the component:
In jest.init.ts, which I refer to in my jest.config.ts above, I put the following:
jest.mock('vue-pdf-embed', () => () => '<mock-vue-pdf-embed/>');
and now everything works just fine ;)

Jest: Ignoring a dependency

Currently I have a Mixin that is called on mounted() hook in my Vue Component:
Component file:
mixins: [testMixin],
mounted () {
this.getMixin()
}
Mixin (testMixin) file:
// Test fails at the import because of `SyntaxError: Unexpected Identifier`
({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,global,jest){import isEmpty from 'lodash.isempty';
import DependencyThatImportsLodash from ''
export default {
created () {
this.getMixin()
},
}
I have tried:
const wrapper = shallowMount(Component, {
localVue,
mocks: {
getMixin: () => {}
}
})
and
const localVue = createLocalVue()
localVue.mixin(testMixin)
But I still get the lodash error, how can I get past it? Thanks
This is my jest.config.js
module.exports = {
moduleNameMapper: {
'^#/(.*)$': '<rootDir>/$1',
'^~/(.*)$': '<rootDir>/$1',
'^vue$': 'vue/dist/vue.common.js'
},
moduleFileExtensions: ['js', 'vue', 'json'],
transform: {
'^.+\\.js$': 'babel-jest',
'.*\\.(vue)$': 'vue-jest'
},
testPathIgnorePatterns: [
'/node_modules/',
'/vendors/'
]
}

Why vue-jest doesn't install Bootstrap-vue correctly in my test file?

Thank you by advance for your answer !!
Here my problem : i want to do unit test with Jest.
And i test my component ( a login form) and i use in this component : Bootstrap-vue.
So in my test file, i include Bootrap-vue, createLocalVue from vue-test-utils.
But i get an error :
TypeError: Cannot read property 'installed' of undefined
12 | Vue.use(Vuetify);
13 | localVue.use(VueAxios,axios);
> 14 | localVue.use(BootstrapVue);
| ^
15 | localVue.use(IconsPlugin);
16 |
17 |
at Function.use (node_modules/#vue/test-utils/dist/vue-test-utils.js:13742:16)
at Object.<anonymous> (test/unit/specs/components/form/login.spec.js:14:10)
Here my file login.spec.js (test file) :
import 'es6-promise/auto';
import Vue from 'vue';
import Vuetify from 'vuetify';
import axios from 'axios';
import VueAxios from 'vue-axios';
import { shallowMount, createLocalVue, mount } from '#vue/test-utils';
import { BootstrapVue, IconsPlugin } from 'bootstrap-vue';
import FormLogin from '#/components/Form/login';
const localVue = createLocalVue();
Vue.use(Vuetify);
localVue.use(VueAxios,axios);
localVue.use(BootstrapVue);
localVue.use(IconsPlugin);
let wrapper = null;
describe('FormLogin', () => {
beforeEach(() => {
const vuetify = new Vuetify();
wrapper = shallowMount(FormLogin,{
localVue,
vuetify
});
});
afterEach(() => {
wrapper.destroy();
});
it('contains input text',() => {
const type = wrapper.find('input').map((node) =>node.props().type);
const text = type.filter((e) => e === 'text').length === 1;
expect(text).toBe(true);
});
it('contains input password',() => {
const type = wrapper.find('input').map((node) =>node.props().type);
const password = type.filter((e) => e === 'password').length === 1;
expect(password).toBe(true);
});
it('contains button', () => {
expect(wrapper.find('button').toBe(true));
})
});
And my config files :
jest.config.js :
const path = require('path')
module.exports = {
rootDir: path.resolve(__dirname, '../../'),
setupFiles: ['setup.js'],
moduleFileExtensions: [
'js',
'json',
'vue',
'ts'
],
moduleNameMapper: {
'^#/(.*)$': '<rootDir>/src/$1',
'vue$': "vue/dist/vue.common.js",
},
transform: {
'^.+\\.js$': 'babel-jest',
'.*\\.(vue)$': 'vue-jest'
},
testPathIgnorePatterns: [
'<rootDir>/test/coverage/',
'<rootDir>/dist/',
'<rootDir>/node_modules/'
],
//snapshotSerializers: ['<rootDir>/node_modules/jest-serializer-vue'],
setupFiles: ['<rootDir>/test/unit/setup'],
coverageDirectory: '<rootDir>/test/unit/coverage',
collectCoverageFrom: [
'src/**/*.{js,vue}',
'!src/main.js',
'!src/router/index.js',
'!**/node_modules/**'
],
transformIgnorePatterns: ['node_modules/core.js','node_modules/babel-runtime','node_modules/vue',"node_modules/(?!bootstrap-vue)"]
}
.babelrc
{
"presets": [
"#babel/preset-flow",
"#babel/preset-react",
[
"#babel/preset-env",
{
"targets": {
"node": "current"
}
}
/*{
"modules": false,
"targets": {
"browsers": [
"> 1%",
"last 2 versions",
"not ie <= 8"
]
}
}*/
]
],
"plugins": [
"transform-vue-jsx",
[
"#babel/plugin-transform-runtime",
{
"corejs": 2
}
],
"babel-plugin-styled-components",
"#babel/plugin-syntax-dynamic-import",
"#babel/plugin-syntax-import-meta",
"#babel/plugin-proposal-class-properties",
"#babel/plugin-proposal-json-strings",
"transform-class-properties",
"syntax-class-properties",
[
"#babel/plugin-proposal-decorators",
{
"legacy": true
}
],
"#babel/plugin-proposal-function-sent",
"#babel/plugin-proposal-export-namespace-from",
"#babel/plugin-proposal-numeric-separator",
"#babel/plugin-proposal-throw-expressions"
]
}
I searched for more 1 week but i did'nt find the same problem of mine. Or it wasn't the good solution. I tried to delete my node_modules and to re-install and it is the same problem.
I thank you for your answer.
Have a good day !!

Vue SFC styles not being extracted in webpack production build

Trying to add vue (and SFCs) to my webpack app. The <template> and <script> blocks work fine, but for some reason the styles in the <style> block are not being extracted for production build.
In the dev build, it's extracting the .vue <style> block to a separate css file (named for the entrypoint). Which is OK but I'd prefer they went into my main stylesheet.
But no matter what I try, I can't get any .vue styles to show up (in any file) for the production build.
This is an abbreviated version of my webpack config:
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const VueLoaderPlugin = require("vue-loader/lib/plugin");
...
module.exports = (env) => {
return {
entry: {
app: ["./src/polyfills.js", "./src/scss/styles.scss", "./src/app.js"],
...
testview: "./src/js/views/TestView.js"
},
output: {
path: assets,
filename: "[name].[hash].js",
publicPath: "/static/"
},
resolve: {
modules: ["node_modules", "src"],
alias: {
vue$: "vue/dist/vue.esm.js"
},
extensions: ["*", ".js", ".vue"]
},
module: {
rules: [
{
test: /\.vue$/,
loader: "vue-loader"
},
{
test: /\.js?$/,
exclude: /node_modules/,
use: [
{
loader: "babel-loader",
options: {
presets: [
[
"#babel/preset-env",
{
targets: {
browsers: ["> 1%", "last 2 versions", "ie >= 11"]
}
}
]
],
plugins: ["#babel/plugin-proposal-class-properties"],
code: true,
comments: true,
cacheDirectory: true,
babelrc: false
}
}
]
},
{
test: /\.s?[ac]ss$/,
use: [
"vue-style-loader",
MiniCssExtractPlugin.loader,
{
loader: "css-loader",
options: {
sourceMap: ifNotProduction()
}
},
{
loader: "postcss-loader",
options: {
ident: "postcss",
sourceMap: ifNotProduction(),
plugins: () =>
ifProduction([
require("autoprefixer")({
preset: "default"
}),
require("cssnano"),
require("css-mqpacker")
])
}
},
{
loader: "sass-loader",
options: {
sourceMap: ifNotProduction()
}
}
]
}
]
},
optimization: {
splitChunks: {
cacheGroups: {
commons: {
name: "commons",
chunks: "initial",
minChunks: 2,
minSize: 0
},
styles: {
name: "styles",
test: /\.css$/,
chunks: "all",
enforce: true
}
}
},
occurrenceOrder: true
},
plugins: [
new VueLoaderPlugin(),
new MiniCssExtractPlugin({
filename: "style.[hash].css"
}),
new HtmlWebpackPlugin({
hash: true,
inject: false,
template: "./src/jinja-templates/base.html.j2",
filename: `${templates}/base.html.j2`,
})
]
};
};
The .vue file I'm using is this demo one. I'm trying to import it into the entrypoint called 'testview' which contains simply:
import Vue from "vue";
import MainContent from "../components/main-content";
let MainComponent = Vue.extend(MainContent);
new MainComponent().$mount("#mainContent");
Did figure it out. I had to remove sideEffects: false from my package.json. This issue explains it further.
Still would like to know how to extract the .vue styles to my main stylesheet As it is now, the .vue styles are extracting to a separate stylesheet (dev and production).

Cannot Find Children When Using Enzyme JS Mount

My expectation is that when I use Enzyme's mount function, I should be able to not only query for nodes in the top level element, but also nodes/elements that are rendered in child components.
Here are the tests exhibited in the matter:
import React from 'react';
import chai from 'chai'
import chaiEnzyme from 'chai-enzyme'
chai.use(chaiEnzyme());
const expect = chai.expect;
import {
shallow,
mount
} from 'enzyme';
class Child extends React.Component {
render() {
return <div id='child'>
CHILD
<button onClick={() => console.log('hit me')}>HIT ME</button>
</div>
}
}
class Parent extends React.Component {
render() {
return <div id='root'>
<Child aprop={1}/>
</div>
}
}
describe('example', function() {
describe('mounted', function() {
it('should find the button', function() {
const wrapper = mount(<Parent/>);
expect(wrapper.find('button').length).to.equal(1);
});
it('should find Child component', function() {
const wrapper = mount(<Parent/>);
console.log(wrapper.debug());
expect(wrapper.find(Child).length).to.equal(1);
});
});
describe('shallow', function() {
it('should not find the button', function() {
const wrapper = shallow(<Parent/>);
expect(wrapper.find('button').length).to.equal(0);
});
it('should find Child component', function() {
const wrapper = shallow(<Parent/>);
expect(wrapper.find(Child).length).to.equal(1);
});
});
});
The output is:
example
mounted
✗ should find the button
expected 0 to equal 1
AssertionError#webpack:///~/assertion-error/index.js?6193**:74:0 <- test/components/mount.spec.js:373:25
assert#webpack:///~/chai/lib/chai/assertion.js?fee1**:107:0 <- test/components/mount.spec.js:4705:32
assertEqual#webpack:///~/chai/lib/chai/core/assertions.js?b343**:487:0 <- test/components/mount.spec.js:5222:19
webpack:///~/chai/lib/chai/utils/addMethod.js?ca90**:41:0 <- test/components/mount.spec.js:4292:31
webpack:///test/components/mount.spec.js:35:53 <- test/components/mount.spec.js:153:54
LOG LOG: '<Parent />'
✗ should find Child component
expected 0 to equal 1
AssertionError#webpack:///~/assertion-error/index.js?6193**:74:0 <- test/components/mount.spec.js:373:25
assert#webpack:///~/chai/lib/chai/assertion.js?fee1**:107:0 <- test/components/mount.spec.js:4705:32
assertEqual#webpack:///~/chai/lib/chai/core/assertions.js?b343**:487:0 <- test/components/mount.spec.js:5222:19
webpack:///~/chai/lib/chai/utils/addMethod.js?ca90**:41:0 <- test/components/mount.spec.js:4292:31
webpack:///test/components/mount.spec.js:41:50 <- test/components/mount.spec.js:159:51
shallow
✓ should not find the button
✓ should find Child component
Either mount is not working as expected, or my understanding isn't correct.
Here is my Karma/Webpack file if it helps:
const webpack = require('webpack');
var argv = require('yargs').argv;
var path = require('path');
let srcPath = path.join(__dirname, '/../src/');
const webpackConfig = {
devtool: 'inline-source-map',
resolve: {
// allow us to import components in tests like:
// import Example from 'components/Example';
root: path.resolve(__dirname, './src'),
// allow us to avoid including extension name
extensions: ['', '.js', '.jsx', '.css', '.scss', '.json'],
// required for enzyme to work properly
alias: {
'sinon': 'sinon/pkg/sinon',
}
},
module: {
// don't run babel-loader through the sinon module
noParse: [
/node_modules\/sinon\//
],
isparta: {
embedSource: true,
noAutoWrap: true,
// these babel options will be passed only to isparta and not to babel-loader
babel: {
presets: ['es2015', 'stage-0', 'react']
}
},
// run babel loader for our tests
loaders: [
{
test: /\.(js|jsx)$/,
loader: 'babel',
exclude: path.resolve(__dirname, 'node_modules'),
query: {
plugins: ['transform-decorators-legacy'],
presets: ['es2015', 'airbnb', 'stage-1', 'react']
}
},
{
test: /\.json$/, loader: 'json'
},
{
test: /\.scss$/,
exclude: /[\/\\](node_modules|bower_components|public)[\/\\]/,
loaders: [
'style?sourceMap',
'css?modules&importLoaders=1&localIdentName=[local]',
'postcss',
'sass'
]
},
{
test: /\.css$/,
exclude: /[\/\\](node_modules|bower_components|public)[\/\\]/,
loaders: [
'style?sourceMap',
'css?modules&importLoaders=1&localIdentName=[local]'
]
}
],
preLoaders: [ { //delays coverage til after tests are run, fixing transpiled source coverage error
test: /\.(jsx|js)$/,
include: path.resolve('src/'),
loader: 'isparta',
} ]
},
plugins: [
new webpack.IgnorePlugin(/node-fetch/)
],
// required for enzyme to work properly
externals: {
'jsdom': 'window',
'react/lib/ExecutionEnvironment': true,
'react/lib/ReactContext': 'window',
'react/addons': true
},
};
module.exports = function(config) {
config.set({
browsers: ['PhantomJS'],
singleRun: !argv.watch,
frameworks: ['mocha', 'chai'],
reporters: ['spec', 'coverage'],
// include some polyfills for babel and phantomjs
files: [
'node_modules/whatwg-fetch/fetch.js',
'node_modules/babel-polyfill/dist/polyfill.js',
'./node_modules/phantomjs-polyfill/bind-polyfill.js',
'test/**/*.js'
],
preprocessors: {
// add webpack as preprocessor
'src/**/*.js': ['webpack', 'sourcemap'],
'test/**/*.js': ['webpack', 'sourcemap']
},
// A lot of people will reuse the same webpack config that they use
// in development for karma but remove any production plugins like UglifyJS etc.
// I chose to just re-write the config so readers can see what it needs to have
webpack: webpackConfig,
webpackMiddleware: {
noInfo: true
},
coverageReporter: {
dir: 'coverage/',
reporters: [
{ type: 'html' },
{ type: 'text' }
]
},
// tell karma all the plugins we're going to be using to prevent warnings
plugins: [
'karma-mocha',
'karma-chai',
'karma-webpack',
'karma-phantomjs-launcher',
'karma-spec-reporter',
'karma-sourcemap-loader',
'karma-coverage'
]
});
};