I am using the v3-tour plugin for a Vue 3 frontend, and I access the global $tours property injected by the plugin in my component like so
<script setup lang="ts">
import { ComponentPublicInstance, computed, getCurrentInstance, onMounted } from 'vue';
import { useI18n } from 'vue-i18n';
const app = getCurrentInstance();
const proxy = app?.appContext.config.globalProperties;
...
const startTour = () => {
proxy?.$tours['homeTour'].start();
};
...
This works but a problem occurs in my unit tests when I try to mock $tours like so:
const wrapper = shallowMount(Tour, {
global: {
stubs: ['v-tour', 'v-step'],
mocks: {
$tours: {
homeTour: {
start: jest.fn(),
currentStep: 0,
nextStep: jest.fn(),
},
},
},
plugins: [i18n],
},
});
When I try to test the help tour
it('startTour should start tour', () => {
wrapper.find("#start-tour").trigger("click");
expect(wrapper.vm.$tours['homeTour'].start).toHaveBeenCalled();
});
it fails because $tours in undefined in my component:
Cannot read properties of undefined (reading 'homeTour')
How can should I mount or access $tours so that I can access it in my unit tests ?
Related
I have this simple Vue SFC which renders the component given the prop value.
<template>
<component :is="component" v-bind="stepProps" />
</template>
<script>
import { _ } from 'core'
export default {
name: 'SetupFlow',
props: {
type: {
type: String,
required: true
},
step: {
type: String,
required: true
},
stepProps: Object
},
computed: {
component () {
const camelCaseName = _.camelCase(this.step)
const name = camelCaseName.charAt(0).toUpperCase() + camelCaseName.slice(1)
return () => import(`#/components/ProfileSetup/GMB/${name}`)
}
}
}
</script>
In my test, I just need to make sure that the component imported is being rendered. Here is my test file so far:
import { createLocalVue, shallowMount } from '#vue/test-utils'
import SetupFlow from '#/components/ProfileSetup/SetupFlow'
const localVue = createLocalVue()
describe('SetupFlow.vue', () => {
let propsData
let stubs
beforeEach(() => {
propsData = {
type: 'GMB',
step: 'step-example' // this file does not exist, so I need to mock `import`
}
})
it('renders the given step component', async () => {
const wrapper = shallowMount(SetupFlow, { localVue, propsData })
})
})
This is an error I get when running the test:
Any ideas how to mock import so that step-example returns a mock vue component?
I'm trying to write unit tests for my Dashboard.vue component using factory functions so that I could overwrite the store and wrapper as per needed.
Here is the code
import { mount, createLocalVue } from '#vue/test-utils'
import mergeWith from 'lodash.mergewith'
import mutationobserver from 'mutationobserver-shim'
import Vuex from 'vuex'
import BootstrapVue from 'bootstrap-vue'
import Dashboard from '#/views/dashboard/Dashboard'
import { FontAwesomeIcon } from '#fortawesome/vue-fontawesome'
import { library as faLibrary } from '#fortawesome/fontawesome-svg-core'
import { faUser, faThumbsUp, faSignOutAlt, faBorderAll, faAlignJustify, faTrashAlt, faRandom } from '#fortawesome/free-solid-svg-icons'
import flushPromises from 'flush-promises'
jest.mock('#/services/app.service.js')
faLibrary.add(faUser, faThumbsUp, faSignOutAlt, faBorderAll, faAlignJustify, faTrashAlt, faRandom)
const localVue = createLocalVue()
localVue.use(Vuex)
localVue.use(BootstrapVue)
localVue.use(mutationobserver) // This is a necessary polyfill for Bootstrap-Vue
localVue.component('font-awesome-icon', FontAwesomeIcon)
function customizer (ovjValue, srcValue) {
/*
If the property that takes precedence is an array,
overwrite the value rather than merging the arrays
*/
if (Array.isArray(srcValue)) {
return srcValue
}
/*
If the property that takes precedence is an empty object,
overwrite the property with an empty object
*/
if (srcValue instanceof Object && Object.keys(srcValue).length === 0) {
return srcValue
}
}
describe('DashBoard component tests', () => {
let state
// let actions
// let getters
let store
let wrapper
let dashBoardData = [
{ db_name: 'Jobs', dxp_dashboardref: 1, dxp_hidden: 0, dxp_position: 1, dxp_ref: 926 },
{ db_name: 'Firms', dxp_dashboardref: 2, dxp_hidden: 0, dxp_position: 2, dxp_ref: 927 },
{ db_name: 'CRM', dxp_dashboardref: 5, dxp_hidden: 0, dxp_position: 3, dxp_ref: 987 }
]
// beforeEach(() => {
state = {
auth: {
user: {
auids: '',
md_clock: 0,
md_picture: '',
ps_fname1: '',
ps_surname: '',
psname: 'Test Test',
psref: 0
}
},
app: {
dashBoardData: []
}
}
function createStore (overrides) {
const defaultStoreConfig = {
// state: {
// state
// },
getters: {
getDashBoardData: () => dashBoardData
},
actions: {
loadDashboard: jest.fn(),
updateDashBoardData: jest.fn()
}
}
return new Vuex.Store(
state,
mergeWith(defaultStoreConfig, overrides, customizer)
)
}
function createWrapper (overrrides) {
const defaultMountingOptions = {
localVue,
store: createStore()
}
return mount(
Dashboard,
mergeWith(
defaultMountingOptions,
overrrides,
customizer)
)
}
// START: Testing existence of DOM Elements tests
it('is a Vue instance', () => {
const wrapper = createWrapper({})
expect(wrapper.isVueInstance).toBeTruthy()
})
})
Essentially, I'm trying to use a createWrapper method which has a default store unless overrides or customizer are passed. When I run the test I get the following errors
console.error node_modules/vuex/dist/vuex.common.js:899
[vuex] unknown getter: getDashBoardData
console.error node_modules/vue/dist/vue.runtime.common.dev.js:621
[Vue warn]: Error in render: "TypeError: Cannot read property 'length' of undefined"
Now, I have two questions:
Why am I being thrown unknown getter when I have declared it in the defaultStoreConfig ?
The second error comes from the state. For some reason it doesn't recognize the state variable I'm passing. Any ideas why ?
If I simply declare the wrapper inside a beforeEach like so I can pass some of my test but for others which I need to overwrite either getters or actions, I'm not able to do that unless I have the factory functions
getters = {
getDashBoardData: () => dashBoardData
},
actions = {
loadDashboard: jest.fn(),
updateDashBoardData: jest.fn()
}
store = new Vuex.Store({
state,
actions,
getters
})
})
Any help will be highly appreciated!
Solved this by passing the state inside defaultStoreConfig rather than separately when creating the store
Code:
const defaultStoreConfig = {
state: {
auth: {
user: {
auids: '',
md_clock: 0,
md_picture: '',
ps_fname1: '',
ps_surname: '',
psname: 'Test Test',
psref: 0
}
},
app: {
dashBoardData: []
}
},
getters: {
getDashBoardData: () => dashBoardData
},
actions: {
loadDashboard: jest.fn(),
updateDashBoardData: jest.fn()
}
}
Test:
it('is a Vue instance', () => {
const wrapper = createWrapper()
expect(wrapper.isVueInstance).toBeTruthy()
})
I am working on nuxt.js project and getting an error Cannot read property '$nuxt' of undefined when trying to access an event from plugin.
In ~/plugins/myPlugin.js
import Vue from 'vue';
this.$nuxt.$on('emit-height', (payload) => {
Vue.prototype.$bannerHeight = payload;
});
Importing in ~/plugins/nuxt.config.js
plugins: [
'~/plugins/get-main-banner-height.js',
]
this.$nuxt.$on works if I use it in any components but doesn't work in plugin as mentioned above.
In my component I am emitting the height.
methods: {
getMainBannerHeight() {
this.$nextTick(() => {
this.$nuxt.$emit('emit-main-banner-height', this.bannerHeight);
});
},
}
So, my question is "How to listen/capture event in plugins"?
You can reference app in context of nuxt plugin. Docs https://nuxtjs.org/api/context/
import Vue from 'vue';
export default ({ app }) => {
app.$on('emit-height', (payload) => {
Vue.prototype.$bannerHeight = payload;
});
}
I'm trying to use Jest to test a Vue component which makes use of a getter in Vuex. The getter returns a function which in turn returns an array:
questions: state => pageNumber => state.pages[pageNumber].questions
I make use of it in my component like so:
computed: {
inputs() {
return this.$store.getters.questions(this.pageNumber);
},
},
This seems to work fine in terms of rendering the UI, but when trying to test the component I get Cannot read property 'questions' of undefined
My test is a pretty simple one, but I've not used Jest with Vuex before so I could be misunderstanding how you would test components which use getters:
import Vuex from 'vuex';
import { mount, createLocalVue } from '#vue/test-utils';
import SurveyQuestionBuilder from '../components/SurveyQuestionBuilder.vue';
import store from '../store';
const localVue = createLocalVue();
localVue.use(Vuex);
describe('SurveyQuestionBuilder.vue', () => {
it('renders a value from $store.state', () => {
const wrapper = mount(SurveyQuestionBuilder, { store, localVue });
expect(wrapper.exists()).toBeTruthy();
});
});
I'm presuming it's to do with pages[pageNumber] in the getter, but not sure what to do to resolve it.
Store.js imports a couple of modules:
import Vue from 'vue';
import Vuex from 'vuex';
import surveyBuilderStore from './survey_builder';
import matrixStore from './matrix';
Vue.use(Vuex);
export default new Vuex.Store({
modules: {
survey: surveyBuilderStore,
matrix: matrixStore,
},
});
The module in question is surveyBuilderStore:
const surveyBuilderStore = {
state: {
pages: [],
},
getters: {
pages: state => state.pages,
questions: state => pageNumber => state.pages[pageNumber].questions,
config: state => (pageNumber, questionNumber) =>
state.pages[pageNumber].questions[questionNumber].config,
},
mutations: {
// my mutations
}
};
In your questions getter, you search at probably unknown index in the pages array.
So questions: state => pageNumber => state.pages[pageNumber] is undefined because state.pages is empty and pageNumber is above 0.
To avoid this you can do:
questions: state => pageNumber => {
return state.pages[pageNumber]
? state.pages[pageNumber].questions
: [] // <-- here your default value
}
You can set in your test the value of pageNumber but I don't know if it's a props or data of the component:
For data:
mount(SurveyQuestionBuilder, {
store,
localVue,
data:() => ({ pageNumber: 0 })
})
For props:
mount(SurveyQuestionBuilder, {
store,
localVue,
propsData: { pageNumber: 0 }
})
Hey I have been working in the configuration of karma + jasmine + enzyme to start working in the unit tests of my project, then at exec my first test I got this error
TypeError: undefined is not a constructor (evaluating '(0, _jasmine.expect)(addLoan.length)') in src/app/modules/Login/LoginComponent.spec.js (line 80581)
src/app/modules/Login/LoginComponent.spec.js:80581:29
loaded#http://localhost:9876/context.js:151:17
then here is my test code:
import React from 'react';
import { expect } from 'jasmine';
import { shallow } from 'enzyme';
import ServicerComponent from './LoginComponent';
function setup() {
const props = {
error: {},
onClick: () => {},
emailOnChange: () => {},
passwordOnChange: () => {},
};
return shallow(<ServicerComponent{...props} />);
}
describe('<ServicerComponent />', () => {
const displayNames = {
login: 'login',
};
let wrapper;
beforeEach(() => {
wrapper = setup();
});
it('should have a Login button', () => {
const addLoan = wrapper.find({ name: displayNames.login });
expect(addLoan.length).toBe(1);
});
});
also I am using :
jasmine: 2.5.3
enzyme: 2.7.1
You need to create setup instance of the class
beforeEach(() => {
wrapper = new setup();
});
I already found the answer of the question, I just remove the jasmine importer and add a global variable in the .eslintrc.json named expect equal true