Jest Unit test cant determine Vuetify components visibility - vuejs2

I have a Vue2 project with Vuetify, and i am using Jest for unit testing my code. I am starting out testing some sample code and i simple cannot get Jest to determine if a Vuetify v-alert component is visible or not. I have tried the built in Jest methods as well as adding Jest-dom and using the toBeVisible() method and nothing is working so far.
If you look at the Test.vue component, the v-alert component is hidden by default.(Its style is set to display: none;)
The unit test says expect(alert).not.toBeVisible() which should pass, but it always fails regardless of what the v-alert model is set to. If i change the test to expect(alert).toBeVisible() it passes regardless of the v-alert model is set to true/false.
If i change the test to be expect(alert).toHaveStyle({ display: 'none' }); it fails regardless of if i have the model set to true/false.
So as far as i can tell the Jest unit test CANNOT determine the visibility of the v-alert component at all. These same test work fine on the v-btn component just fine so why does the v-alert break? This is just my first unit test sample that ive been trying to get working for 2 days now. I have an entire application to write tests for and so far Jest is not working very well with Vuetify...any suggestions?
// Test.vue component
<template>
<div>
<v-btn ref="btn" depressed tile #click="showAlert">Show Alert</v-btn>
<v-alert
v-model="showError"
ref="error-msg"
type="error"
transition="scale-transition"
width="410"
tile
dense
dismissible
#input="clearError"
>
{{ errorText }}
</v-alert>
</div>
</template>
<script>
export default {
data() {
return {
showError: false,
errorText: ''
};
},
methods: {
showAlert() {
this.errorText = 'Test Error message';
this.showError = true;
},
clearError() {
this.errorText = '';
}
}
};
</script>
// Jest Unit test
// Libraries
import Vue from 'vue';
import Vuetify from 'vuetify';
// Components
import Test from '#/components/Login/Test.vue';
// Utilities
import { createLocalVue, shallowMount } from '#vue/test-utils';
// Import Jest Dom test utils.
import '#testing-library/jest-dom';
const localVue = createLocalVue();
Vue.use(Vuetify);
describe('Test Page', () => {
let vuetify;
beforeEach(() => {
vuetify = new Vuetify();
});
it('Check visibility of button', () => {
const wrapper = shallowMount(Test, {
localVue,
vuetify
});
const btn = wrapper.findComponent({ ref: 'btn' }).element;
expect(btn).toBeVisible();
});
it('Error Message hidden on page load', () => {
const wrapper = shallowMount(Test, {
localVue,
vuetify
});
const alert = wrapper.findComponent({ ref: 'error-msg' }).element;
expect(alert).not.toBeVisible();
});
});
// Package.json
"dependencies": {
"vue": "^2.6.11",
"vue-click-outside": "^1.1.0",
"vue-debounce": "^2.5.7",
"vue-router": "^3.3.4",
"vuetify": "^2.2.11",
"vuex": "^3.4.0"
},
"devDependencies": {
"#babel/plugin-transform-runtime": "^7.10.3",
"#babel/polyfill": "^7.10.1",
"#fortawesome/fontawesome-free": "^5.13.1",
"#testing-library/jest-dom": "^5.10.1",
"#vue/cli-plugin-babel": "^4.4.5",
"#vue/cli-plugin-e2e-nightwatch": "^4.4.5",
"#vue/cli-plugin-eslint": "^4.4.5",
"#vue/cli-plugin-unit-jest": "^4.4.5",
"#vue/cli-service": "^4.4.5",
"#vue/eslint-config-prettier": "^4.0.1",
"#vue/test-utils": "^1.0.3",
"babel-eslint": "^10.0.3",
"babel-jest": "^26.1.0",
"eslint": "^5.16.0",
"eslint-plugin-vue": "^6.2.2",
"node-sass": "^4.14.1",
"sass": "^1.26.9",
"sass-loader": "^8.0.2",
"vue-cli-plugin-vuetify": "^2.0.6",
"vue-template-compiler": "^2.6.11",
"vuetify-loader": "^1.5.0"
}

I ran into a similar issue, so I decided to use exists from #vue/test-utils instead.
Docs for exists: https://vue-test-utils.vuejs.org/api/wrapper/#exists
I also decided to use v-if (instead of v-model) on the v-alert element to hide / show the component.
It looks like if v-if receives a value of false, the component/element in the document is replaced with <!---->, which is great for checking if your component/element is hidden or displayed.
See v-if test spec: https://github.com/vuejs/vue/blob/52719ccab8fccffbdf497b96d3731dc86f04c1ce/test/unit/features/directives/if.spec.js
SFC
Template:
<template>
<v-container>
<v-btn
#click='showError()'
ref="showErrorButton">
Show Error
</v-btn>
<v-alert
v-if="errorEncountered"
ref="errorAlert"
colored-border
type="error"
elevation="2"
>
Oops! Something went wrong!
</v-alert>
<v-container>
<template>
Javascript:
export default {
methods: {
showError() {
this.errorEncountered = true;
}
}
data() {
return {
errorEncountered: false,
};
},
};
Whenever errorEncountered is updated, the v-alert component will show/hide depending on whether the value is true/false.
Tests
describe('Component', () => {
let wrapper;
beforeEach(() => {
wrapper = mount(Component, {
localVue,
vuetify,
});
});
describe('When component is mounted', () => {
it('Then the default value for errorEncountered should be false', () => {
expect(wrapper.vm.errorEncountered).toBeFalse();
});
it('Then the default state for the error alert should be hidden', async () => {
const errorAlert = wrapper.find({ ref: 'errorAlert' });
expect(errorAlert.exists()).toBeFalse();
});
describe('When an error is encountered', () => {
it('Then errorEncountered should be set to true', async () => {
const showErrorButton = wrapper.find({ ref: 'showErrorButton' });
showErrorButton.trigger('click');
await Vue.nextTick();
expect(wrapper.vm.errorEncountered).toBeTrue();
});
it('Then error alert should be visible', async () => {
const showErrorButton = wrapper.find({ ref: 'showErrorButton' });
showErrorButton.trigger('click');
await Vue.nextTick();
const errorAlert = wrapper.find({ ref: 'errorAlert' });
expect(errorAlert.exists()).toBeTrue();
});
});
});

Related

Testing vue component with v-dialog using vitest throws TypeError: globalStack.at is not a function

I have configured vue project with vuetify and vitest. I am using happy-dom (but also tried jsdom) to run unit tests. I am able to run unit tests unless my component has a v-dialog component. I get the following error:
TypeError: globalStack.at is not a function
at ......./node_modules/vuetify/src/composables/stack.ts:58:3
at callWithErrorHandling (......./node_modules/#vue/runtime-core/dist/runtime-core.cjs.js:157:22)
at callWithAsyncErrorHandling (......./node_modules/#vue/runtime-core/dist/runtime-core.cjs.js:166:21)
at ReactiveEffect.getter [as fn] (......./node_modules/#vue/runtime-core/dist/runtime-core.cjs.js:1724:24)
at ReactiveEffect.run (......./node_modules/#vue/reactivity/dist/reactivity.cjs.js:191:25)
at doWatch (......./node_modules/#vue/runtime-core/dist/runtime-core.cjs.js:1839:16)
at Module.globalTop (......./node_modules/#vue/runtime-core/dist/runtime-core.cjs.js:1644:12)
at Module.useStack (......./node_modules/vuetify/src/composables/stack.ts:56:24)
at Object.setup [as _setup] (......./node_modules/vuetify/src/components/VOverlay/VOverlay.tsx:160:41)
at setup (/......./node_modules/vuetify/src/util/defineComponent.tsx:83:27)
At first I was getting this error running the app as well, but after I got newer version of Chrome this error went away. But now I am having this running a unit test. Here is my sample component:
<template>
<div>
<v-dialog>hello world</v-dialog>
</div>
</template>
and here is my sample unit test which shows this error
import { mount } from '#vue/test-utils';
import DialogSample from '#/components/DialogSample.vue'
import { createVuetify } from "vuetify";
describe('DialogSample', () => {
let wrapper;
let vuetify;
beforeEach(() => {
vuetify = createVuetify();
});
afterEach(() => {
});
describe('logged in tests', () => {
beforeEach(() => {
wrapper = mount(DialogSample, {
global: {
plugins: [vuetify],
},
props: {},
});
});
test('sample test', async () => {
expect(true).toBeTruthy();
});
});
});
here some versions from package.json:
"dependencies": {
"#mdi/font": "7.1.96",
"#pinia/testing": "^0.0.14",
"axios": "^1.2.0",
"dotenv": "^16.0.3",
"happy-dom": "^8.1.1",
"jsdom": "^20.0.3",
"lodash": "^4.17.21",
"pinia": "^2.0.27",
"roboto-fontface": "*",
"vue": "^3.2.45",
"vuetify": "3.0.6",
"webfontloader": "^1.0.0"
},
"devDependencies": {
"#vitejs/plugin-vue": "^4.0.0",
"#vue/test-utils": "^2.2.6",
"vite": "^4.0.3",
"vite-plugin-vuetify": "^1.0.0-alpha.12",
"vitest": "^0.26.2"
}
here is test section from vite.config.js:
test: {
// https://vitest.dev/config/
globals:true,
environment: 'happy-dom',
setupFiles: "vuetify.config.js",
deps: {
inline: ["vuetify"],
},
},
and here is vuetify.config.js (without this setting I was getting CSS errors running tests):
global.CSS = { supports: () => false };
I was able to change my unit test to use shallowMount() instead of mount() just to see if unit test would work and it did. However, I do need mount() working so that I can test my dialogs. Other vuetify components I tried so far (I only tried a few so far) worked fine.
Any ideas how to get passed this would be greatly appreciated.
thanks

TypeError: Cannot read properties of undefined (reading 'html')

I am trying to introduce Jest to my current project.
However, during the initial setup, I encountered this error and it is not running properly.
How can I solve this?
I am currently using vue2 from vue-cli.
● Test suite failed to run
TypeError: Cannot read properties of undefined (reading 'html')
at new JSDOMEnvironment (node_modules/jest-environment-jsdom/build/index.js:72:44)
at async TestScheduler.scheduleTests (node_modules/#jest/core/build/TestScheduler.js:317:13)
at async runJest (node_modules/#jest/core/build/runJest.js:407:19)
at async _run10000 (node_modules/#jest/core/build/cli/index.js:338:7)
at async runCLI (node_modules/#jest/core/build/cli/index.js:190:3)
This is my test code.
import SettlementProcessing from "#/views/calculate/SettlementProcessing.vue";
import { shallowMount } from "#vue/test-utils";
import Vuetify from "vuetify";
describe("Settlement Component", () => {
let vuetify;
beforeEach(() => {
vuetify = new Vuetify();
});
it("정산 처리 타이틀이 나와야 한다.", () => {
const sc = shallowMount(SettlementProcessing, { vuetify });
expect(true).toBe(true);
});
});
Here is my package.json.
"devDependencies": {
"#vue/cli-plugin-babel": "~4.5.0",
"#vue/test-utils": "^2.0.0-rc.21",
"babel-core": "^7.0.0-bridge.0",
"babel-jest": "^28.1.0",
"jest": "^28.1.0",
"vue-cli-plugin-vuetify": "~2.4.0",
"vue-jest": "^3.0.7",
}
Here is my jest.config.json.
// jest.config.js
module.exports = {
preset: "#vue/cli-plugin-unit-jest",
moduleFileExtensions: [
"js",
"json",
"vue",
],
transform: {
"^[^.]+.vue$": "vue-jest",
"^.+\\.js$": "babel-jest",
},
moduleNameMapper: {
"^#/(.*)$": "<rootDir>/src/$1",
},
testMatch: [
"**/__tests__/**/*.[jt]s?(x)",
"**/?(*.)+(spec|test).[jt]s?(x)",
],
testPathIgnorePatterns: ["/node_modules/", "/dist/"],
collectCoverage: false,
collectCoverageFrom: ["**/*.{js,vue}", "!**/node_modules/**"],
};
How can I solve this??
I had the same issue when updating my react app to jest 28. The issue was the missing jest-environment-jsdom package which was not yet necessary in jest 27.
See https://jestjs.io/docs/28.x/upgrading-to-jest28
You should create a localVue instance and use Vuetify on it. This can be achieved either in a tests/setup.js file (which runs for all jest tests) or separately in each unit test that uses Vuetify.
Sample code without setup.js (if you use setup.js, the code will be slightly different, you can check the documentation below)
import SettlementProcessing from "#/views/calculate/SettlementProcessing.vue";
import { createLocalVue, shallowMount } from "#vue/test-utils";
import Vuetify from "vuetify";
const localVue = createLocalVue()
localVue.use(Vuetify)
describe("Settlement Component", () => {
it("정산 처리 타이틀이 나와야 한다.", () => {
const sc = shallowMount(SettlementProcessing, { localVue } );
expect(true).toBe(true);
});
});
The documentation is here:
https://vuetifyjs.com/en/getting-started/unit-testing/#bootstrapping-vuetify
It was fixed for me by adding jest-environment-jsdom.

Jest Unit Test: wrapper.vm.$refs.editForm.validate is not a function

When I write test case for form submit, i m getting issue with 1wrapper.vm.$refs.editForm.validate is not a function
I am unable to figure out the problem.. please Help me.
"#vue/cli-plugin-babel": "^3.11.0", "#vue/cli-plugin-eslint": "^3.11.0", "#vue/cli-plugin-pwa": "^3.11.0", "#vue/cli-plugin-unit-jest": "^3.11.0", "#vue/cli-service": "^3.11.0", "#vue/eslint-config-prettier": "^5.0.0", "#vue/test-utils": "^1.0.0-beta.29", "babel-core": "^7.0.0-bridge.0", "babel-eslint": "^10.0.1", "babel-jest": "^23.6.0"
==== EditProperty.vue======
<v-form ref="editForm" lazy-validation>
<v-flex>
<v-text-field label="Label Text" v-model="labelName" :rules="[v => !!v || 'Label Text is required']"
/>
</v-flex>
</v-form>
<script>
export default {
data() {
return {
labelName: ''
}
},
methods: {
save() {
if (this.$refs.editForm.validate()) {
this.$emit('updateLable', this.labelName)
}
}
}
}
</script>
======EditProperty.spec.js =====
import { shallowMount, createLocalVue } from '#vue/test-utils'
import EditProperty from '#/components/EditProperty.vue'
import Vuetify from 'vuetify'
const localVue = createLocalVue()
localVue.use(Vuetify)
let wrapper
describe('EditProperty.vue', () => {
beforeEach(() => {
wrapper = shallowMount(EditProperty, {
localVue,
data() {
return {
labelName: 'Username'
}
}
})
})
it('should save called correctly', () => {
wrapper.vm.save()
})
})
expected => test should be pass
getting => wrapper.vm.$refs.editForm.validate is not a function
When I write test case for form submit, i m getting issue with 1wrapper.vm.$refs.editForm.validate is not a function
I am unable to figure out the problem.. please Help me.
shallowMount does not render the child components. I.E. in your case, v-form won't be rendered in the test. In fact if you call html from your wrapper, you will see a HTML comment in place of the <edit-form>.
The rationale behind that vue test utils feature is that, when you're unit testing a component, you test only the logic of such component in isolation, and don't rely on code from other modules.
Now you could manually pass an object as stub and provide any test double to allow the validate() call, via the stubs option:
import { shallowMount, createLocalVue } from '#vue/test-utils'
import EditProperty from '#/components/EditProperty.vue'
import Vuetify from 'vuetify'
const localVue = createLocalVue()
localVue.use(Vuetify)
let wrapper
describe('EditProperty.vue', () => {
beforeEach(() => {
const EditFormStub = {
render: () => {},
methods: {
validate: () => true,
}
};
wrapper = shallowMount(EditProperty, {
localVue,
stubs: {
'edit-form': EditFormStub,
}
data() {
return {
labelName: 'Username'
}
}
})
})
it('should save called correctly', () => {
wrapper.vm.save()
})
})
So we are passing a fake editForm as a stub, with a fake validate() method which always returns true.
Then you can test your component code. For instance, you could test that your label is emitted as updateLabel (in your original snippet it was 'updateLable', be wary):
it('should save called correctly', () => {
wrapper.vm.save();
expect(wrapper.emitted('updateLabel')[0][0]).toBe(whatever the label should be)
})
if you got in a situation where you don't need to stub. You could easily do this:
import { nextTick } from 'vue'
it('test', async () => {
wrapper.vm.$refs.editForm.validate = jest.fn()
await nextTick()
})

TestWindow is not a constructor in Stenciljs Unit test

Getting errors when I run unit test for the component
1) TestWindow is not a constructor
2) Cannot read property 'textContent' of undefined
Not able to understand how to proceed further. When I try to console element and testWindow, both are coming as undefined.
**tsx file**
import { Component } from '#stencil/core';
#Component({
tag: 'my-header',
styleUrl: 'my-header.css'
})
export class MyHeader {
render() {
return (
<div>
<p>Hello MyHeader!</p>
</div>
);
}
}
**Spec file**
import { TestWindow } from '#stencil/core/testing';
import { MyHeader } from './my-header';
describe('my-header', () => {
it('should build', () => {
expect(new MyHeader()).toBeTruthy();
});
describe('rendering', () => {
let element: HTMLMyHeaderElement;
let testWindow: TestWindow;
beforeEach(async () => {
testWindow = new TestWindow();
element = await testWindow.load({
components: [MyHeader],
html: '<my-header></my-header>'
});
});
console.log("element ",element);
console.log("testWindow ",testWindow);
it('should show content', () => {
expect(element.textContent).toEqual('');
});
});
});
package.json
"devDependencies": {
"#stencil/core": "~0.16.4",
"#stencil/sass": "^0.1.1",
"#types/jest": "23.3.11",
"#types/puppeteer": "1.6.4",
"jest": "^23.6.0",
"jest-cli": "23.6.0",
"puppeteer": "1.8.0",
"workbox-build": "3.4.1"
}
how can I get rid of those errors or I'm missing something to include.
The entire Stencil unit testing changed in latest versions.
TestWindow is now deprecated in favor of the combination of Jest and Puppeteer.
You should consult the documentation for further explanations about how to test your code: end to end testing in Stencil

Import vue package in laravel

What is the corect way to import vue packages in laravel 5.6? It comes with vue and bootstrap preinstall. I see they are all compile in app.js from public directory but I can figure out how to import https://github.com/moreta/vue-search-select and use it. After I tried to import it on my own:
Error:
ncaught TypeError: Vue.component is not a function
At line:
Vue.component('search-user', __webpack_require__(42));
Until now I tried this:
assets/js/bootstrap.js:
import { BasicSelect } from 'vue-search-select';
window.BasicSelect = BasicSelect;
assets/js/app.js:
require('./bootstrap');
window.Vue = require('vue');
window.Vue = require('vue-search-select');
Vue.component('search-user', require('./components/SearchUser.vue'));
var app = new Vue({
el: '#app'
})
components
<template>
<basic-select :options="options"
:selected-option="item"
placeholder="select item"
#select="onSelect">
</basic-select>
</template>
<script>
export default {
data() {
return {
keywords: null,
options: []
};
},
watch: {
keywords(after, before) {
if (this.keywords.length > 0)
this.fetch();
}
},
methods: {
fetch() {
axios.get('/api/search', {params: {keywords: this.keywords}})
.then(response => this.options = response.data)
.catch(error => {
});
},
onSelect (item) {
this.item = item
},
reset () {
this.item = {}
},
selectOption () {
// select option from parent component
this.item = this.options[0]
},
components: {
BasicSelect
}
}
}
</script>
I ran: npm install and npm run watch:
"devDependencies": {
"ajv": "^6.0.0",
"bootstrap": "^4.0.0",
"cross-env": "^5.1",
"laravel-mix": "^2.0",
"lodash": "^4.17.4",
"popper.js": "^1.12",
"uikit": "^3.0.0-beta.35",
"vue": "^2.5.7",
"vue-search-select": "^2.5.0"
},
"dependencies": {
"axios": "^0.17.1",
"jquery": "^3.3.1"
}
I think that the simple will do
window.Vue = require('vue');
require('vue-search-select');
Then in your components you can import what you need on top:
import { BasicSelect } from 'vue-search-select';
export default {
data() {
return {
keywords: null,
options: [],
item: null
};
},
...
One missing detail that tricked me with this one, you need to register the components like this, otherwise it won't be found:
components: {
ModelSelect,
BasicSelect
},