I'm quite stuck on how I test components that use vuex.
The test i'm trying to run simply tests that when run to set edit to true. The method is as follows:
editMovie: function(movie){
console.log(movie)
this._originalMovie = Object.assign({}, movie);
movie.edit = true;
},
Some methods and on mount dispatches to the store.
I was getting errors about it can't find dispatch so followed https://vue-test-utils.vuejs.org/guides/using-with-vuex.html and added in the Mock store (I think...), with this:
Error:
console.error node_modules/vuex/dist/vuex.common.js:899 [vuex] unknown
getter: movies
console.error node_modules/vue/dist/vue.runtime.common.dev.js:621 [Vue
warn]: Error in render: "TypeError: Cannot read property '0' of
undefined"
Code:
import { shallowMount, createLocalVue } from '#vue/test-utils'
import Vuex from 'vuex'
//import Actions from '../../src/store/Actions'
import MoviesList from '#/components/MoviesList.vue'
const localVue = createLocalVue()
localVue.use(Vuex)
describe('MoviesList.vue', () => {
let actions
let store
beforeEach(() => {
actions = {
getMovies: jest.fn(),
}
store = new Vuex.Store({
actions
})
})
it('Edit should be true', () => {
const wrapper = shallowMount(MoviesList, { store, localVue })
wrapper.setData({
movies: { 0: {
deleted:false,
edit:false,
name:"Full Metal Jacket",
released_on:"1987",
}}
})
console.log(wrapper.vm.movies[0])
wrapper.vm.editMovie(wrapper.vm.movies[0])
expect(wrapper.vm.movies[0].edit).toBe(true)
})
})
Related
I am trying my hand at unit testing and here is where I am stuck. The refs for inputs are saved in a global context and am trying to mock the context so I can test the components properly.
Issue is once of my components calls the focus() property from the ref and I am not sure how to mock the ref properly so even my test can use the focus() property.
This is my testing code:
import { render as rtlRender, screen } from "#testing-library/react";
import "#testing-library/jest-dom";
import { Provider } from "react-redux";
import store from "../redux/store";
import { GlobalContextProvider } from "../common/GlobalContext";
import { SalesContextProvider } from "../Sales/SalesContext";
import PatientAutocomplete from "../Sales/PatientAutocomplete";
const dummySalesContext = {
patientRef: null,
};
const dummyGlobalContext = {
currentComponent: "demo",
setEnableGlobalNavigation: jest.fn(),
};
const render = (component) =>
rtlRender(<Provider store={store()}>{component}</Provider>);
test("renders learn react link", () => {
render(
<GlobalContextProvider value={dummyGlobalContext}>
<SalesContextProvider value={dummySalesContext}>
<PatientAutocomplete
searchTerm="Demo"
patientSelected={null}
close={null}
/>
</SalesContextProvider>
</GlobalContextProvider>
);
const linkElement = screen.getByText(/Add patient/i);
expect(linkElement).toBeInTheDocument();
});
This is the error that is thrown:
TypeError: Cannot read property 'focus' of undefined
}, [searchTerm, debounceFetchPatients]);
useEffect(() => {
if (!showEditPatient) patientRef.current.focus({ preventScroll: true });
^
}, [showEditPatient]);
I'm trying to set up unit testing for my Vue application.
I'm using Jest. I have mounted a component and I want to run tests on it. This component uses a global function (Vue.prototype), called aao, which fails to run in my tests.
Error message:
console.error node_modules/vue/dist/vue.runtime.common.dev.js:621
[Vue warn]: Error in beforeMount hook: "TypeError: this.$aao is not a function"
found in
---> <MyProfile>
<Root>
example.spec.ts:
import editUser from '#/components/forms/editUser.vue';
import TestComponent from '#/pages/user/UserMyProfile.vue';
import { shallowMount } from '#vue/test-utils';
import { expect } from 'chai';
import 'jest';
describe('AppLoadingScreen', () => {
let component;
beforeEach(() => {
component = shallowMount(TestComponent);
});
it('should render Spinner on mount', () => {
expect(component.find(editUser).exists()).to.be.true;
});
});
AAO function:
export function dbRequest(
method: 'get' | 'put' | 'post' | 'delete',
endpoint: string,
data: any,
headers?: any,
responseType?: 'blob' | 'json'
) {
return new Promise<any>((resolve, reject) => {
...
});
}
Vue.prototype.$aao = dbRequest;
How can I make sure that the test utils knows about this.$aao?
SOLVED!
Changed my .spec.ts file to .spec.js, and changed the content to something like this:
import { mount, createLocalVue, shallowMount } from '#vue/test-utils';
import * as All from 'quasar';
import dbRequest from 'src/boot/aao';
const { Quasar, date } = All;
const components = Object.keys(All).reduce((object, key) => {
const val = All[key];
if (val && val.component && val.component.name != null) {
object[key] = val;
}
return object;
}, {});
describe('Mount Quasar', () => {
const localVue = createLocalVue();
localVue.use(Quasar, { components });
// Here's the solution, the global functions need to be used by the local vue component
localVue.use(dbRequest);
const wrapper = mount(UserMyProfile, {
localVue,
});
const vm = wrapper.vm;
// Tests here
}
Read more here:
https://vue-test-utils.vuejs.org/guides/common-tips.html#applying-global-plugins-and-mixins
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()
})
My src/store/modules/authToken.js file is like this:
const authToken = {
state: {
token: localStorage.getItem('user-token') || '',
status: '',
},
mutations: {
authSuccess(state, token) {
console.log('hellllo')
state.token = token
state.status = 'success'
},
authFail(state) {
state.status = 'error'
}
},
getters: {
isAuthenticated: state => {
return !!state.token
},
authStatus: state => {
return state.status
}
}
}
My src/store/store.js file is like this:
import Vue from 'vue'
import Vuex from 'vuex'
import authToken from './modules/authtoken'
Vue.use(Vuex)
Vue.config.devtools = true
export const store = new Vuex.Store({
modules: {
authToken
}
})
In my main.js file, I am using the store as below:
import { store } from './store/store'
new Vue({
render: h => h(App),
store,
router,
}).$mount('#app')
Now, when I try to access the autoken module in a component file, I am unable to access it. I'm doing this.$store.state.authToken.getters.isAuthenticated
but I'm getting the following error when I try to use it.
Error in mounted hook: "TypeError: Cannot read property
'isAuthenticated' of undefined"
This is because you forgot to export your object in your file src/store/modules/authToken.js. Since nothing is exported, the authToken variable you feed the store will be undefined.
Just add this at the end of your file :
export default authToken;
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 }
})