Vuex mock store undefined in Jest unit test - vue.js

I'm new to jest and am trying to write a unit test that relies on the value of a store property.
I tried to create a new mock store in my test (I already have a mock store for a different test), but for some reason, I keep getting the error
TypeError: Cannot read property 'state' of undefined
My tests are:
import { shallowMount, createLocalVue,} from '#vue/test-utils';
import userButtons from '#components/user-profile/UserButtons.vue';
import Vuex from 'vuex';
import { defaultValues, } from '#store/api/userButtons.js';
const localVue = createLocalVue();
localVue.use(Vuex);
describe('UserButtons', () => {
let actions;
let store;
let storeIsAdded;
beforeEach(() => {
actions = {
getUserInfo: jest.fn(),
};
store = new Vuex.Store({
modules: {
userButtons: {
namespaced: true,
actions,
state: {
userButton: defaultValues,
},
mutations: {
setUserButton: jest.fn(),
},
},
},
});
storeIsAdded = new Vuex.Store({
state: {
isItemAdded: true,
},
});
});
test('vuex called on create', () => {
shallowMount(UserButtons, { store, localVue, propsData: { id: 3406304, }, });
expect(actions.getUserInfo).toHaveBeenCalled();
});
test('renders correctly', () => {
const wrapper = shallowMount(UserButtons, { store, localVue, propsData: { id: 3406304, }, });
expect(wrapper.element).toMatchSnapshot();
});
test('indicates remove if isItemAdded is true', () => {
const wrapper = shallowMount(UserButtons, { storeIsAdded, localVue, propsData: { id: 3406304, }, });
expect(wrapper.find('.button-action.my-items').text()).toBe('- Remove from My Items');
});
});
The first two tests, which just use defaultValues for my store, pass.
The last test, 'indicates remove if isItemAddedis true, is the one that fails, and is using the mock store,storeIsAdded`.
If anyone has any insight, it would be much appreciated!!!
EDIT:
Even if I modify my mock store to be more similar to the store that seems to be working, like this:
storeIsInList = new Vuex.Store({
modules: {
userButton: {
namespaced: true,
actions,
state: {
userButton: {
isItemAdded: true,
},
},
},
},
});
I get the same error.

It seems the error is when accessing the store state in the component. So, my guess is that the store maybe need to be module/namespace, i.e.
storeIsInList = new Vuex.Store({
modules: {
userButtons: {
namespaced: true,
actions,
state: {
userButton: {
isItemAdded: true,
},
},
},
},
});
And as #il0v3d0g stated, maybe the namespace name is wrong.

Related

Is there a way to make my vue jest test with mock vuex instead of the app store

const localVue = createLocalVue();
localVue.use(Vuex);
describe('Dashboard component', () => {
let store;
let userDataStore;
beforeEach(() => {
userDataStore = {
namespaced: true,
state: {
sessionId: 'k5gv7lc3jvol82o91tddjtoi35kv16c3',
},
};
store = new Vuex.Store({
modules: {
userDataStore: userDataStore,
},
});
});
it('it renders the header component if there is a session id', () => {
const wrapper = shallowMount(DashboardPage, {
store,
localVue,
});
const headerComponent = wrapper.findComponent({ name: 'DashboardPage' });
expect(headerComponent.exists()).toBe(true);
});
});
but it keeps trying to access the app's main vuex and it should instead make use of the test mockup.

errors: `unknown mutation type: setLoggedIn` & `unknown local mutation type: setLoggedIn, global type: auth/setLoggedIn`

two errors help me build with vuex modules
errors: unknown mutation type: setLoggedIn & unknown local mutation type: setLoggedIn, global type: auth/setLoggedIn
vuex version "vuex": "^4.0.0"
the problem occurs in the setLoggedInState(ctx) function
index.js
import Vuex from 'vuex'
import middleware from "./modules/middleware.js";
import auth from "./modules/auth";
export default new Vuex.Store({
namespaced: true,
modules: {
auth,
middleware
}
})
auth.js
const state = {
isLoggedIn: true,
};
const mutation = {
setLoggedIn(state, payload, ) {
state.isLoggedIn = payload;
},
};
const actions = {
setLoggedInState(ctx) {
return new Promise((resolve) => {
if (localStorage.getItem('token')) {
ctx.commit('setLoggedIn', true, {root: true});
resolve(true)
} else {
ctx.commit('setLoggedIn', false, {root: true});
resolve(false)
}
});
},
}
const getters = {
loggedIn(state) {
return state.isLoggedIn;
},
export default {
namespaced: true,
state,
mutation,
actions,
getters
}
Dashboard
import {mapActions} from 'vuex'
export default {
name: "Dashboard",
data: () => ({}),
created() {
this.checkUserState();
},
methods: {
...mapActions({
checkUserState: 'auth/setLoggedInState',
}),
I don’t understand how to fix errors I tried many ways I hope for your help
When you learn something new, please check for missing '; , .' etc.
And be sure that you write 'const mutations' not 'const mutation', following the documentation saving hours))

How to Mock a store with global variable

I have a file that I'm using to store a global variable that gets changed by 'login' or 'logout' functions. I want to write a unit test that has the value of 'isLoggedIn' set to true or false, then checks for expected behaviour. I can't figure out what I need to do to be able to use the value, this is my file:
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
loggedIn: false,
},
mutations: {
login(state) {
state.loggedIn = true;
},
logout(state) {
state.loggedIn = false;
state.userID = null;
},
},
actions: {
login({ commit }) {
commit('login');
},
logout({ commit }) {
commit('logout');
},
},
getters: {
isLoggedIn: (state) => state.loggedIn,
},
});
And this is the test I'm trying to create:
import { expect } from 'chai';
import { shallowMount } from '#vue/test-utils';
import Home from '#/views/images.vue';
describe('Images.vue', () => {
it('shows that you are logged in', () => {
const welcome_text = 'You are logged in.';
this.$store.dispatch('login');
const wrapper = shallowMount(Home, {});
expect(wrapper.text()).to.include(welcome_text);
});
});
Your getter method isn't returning anything.
https://vuex.vuejs.org/guide/getters.html#property-style-access
Once you change your getter to:
getters: {
isLoggedIn: (state) => return state.loggedIn,
},
You should be able to retrieve this value using:
this.$store.getters.isLoggedIn

Is there a way to detect query changes using Vue-Router and successfully get new data?

I am simply making an asynchronous request to get data about a MLB player but am failing to get new data by manually changing the query parameters in the URL. When I use watch, the from and the to are the same for some reason upon debugging with Vue dev tools. However, all works well when I manually click a link to navigate routes as the from and the to correctly represent the from and the to routes.
PitcherForm.vue
export default {
name: "PitcherForm",
components: {
PlayerForm,
},
watch: {
$route() {
this.handleSubmit({ firstName: this.$route.query.firstName, lastName: this.$route.query.lastName });
}
},
methods: {
handleSubmit: function(formValues) {
// this.$store.dispatch("fetchPlayer", { formValues, router: this.$router, player: "pitching" });
this.$store
.dispatch("fetchPlayer", { formValues, player: "pitching" })
.then((promiseObject) => {
console.log(promiseObject)
this.$router.push({
// name: 'PitcherData',
path: "pitching/player",
query: {
firstName: promiseObject.firstName,
lastName: promiseObject.lastName,
},
});
})
.catch((error) => {
console.log(error);
});
},
},
//store.js
import Vue from 'vue';
import Vuex from 'vuex';
import VuexPersist from 'vuex-persist';
import axios from 'axios';
Vue.use(Vuex);
// VuexPersist stuff
const vuexLocalStorage = new VuexPersist({
key: 'vuex',
storage: window.localStorage,
});
export const store = new Vuex.Store({
plugins: [vuexLocalStorage.plugin],
state: {
playerStats: []
},
// mutations, getters, excluded for convenience
actions: {
fetchPlayer({ commit }, data) {
return new Promise((resolve) => {
let firstName = data.formValues.firstName.replace(/\s/g, "").toLowerCase();
let lastName = data.formValues.lastName.replace(/\s/g, "").toLowerCase();
axios.get(`http://localhost:5000/${data.player}/player`,
{
params: {
first: firstName.charAt(0).toUpperCase() + firstName.slice(1),
last: lastName.charAt(0).toUpperCase() + lastName.slice(1),
},
}).then(response => {
commit('setPlayers', response.data);
}).catch((error) => {
console.log(error);
});
resolve({firstName, lastName});
});
}
}
});

How to test namespaced mapAction in Vue

I have a namespaced Vuex store in my Vue 2.6 app one module of which is like this:
//books.js
export default {
state: {
books: null
},
mutations: {
SET_BOOKS(state, books) {
state.books = books;
},
},
actions: {
setBooks: async function ({ commit }) {
//api call
commit('SET_BOOKS', books);
}
},
namespaced: true
};
I want to test a component that calls the setBooks action. I am using mapActions to access the actions. The relevant code is:
//Books.vue
methods: {
...mapActions("books", ["setBooks"]),
}
},
created: async function() {
await this.setBooks();
}
The problem is that my test can't find the action:
import { shallowMount } from '#vue/test-utils';
import Books from '#/views/Books';
import Vuex from 'vuex';
import flushPromises from 'flush-promises';
import { createLocalVue } from '#vue/test-utils'
const localVue = createLocalVue();
localVue.use(Vuex);
describe('Books', () => {
let actions;
let store;
let wrapper;
beforeEach(() => {
store = new Vuex.Store({
state: {
books: {
books: null
}
},
actions: {
setBooks: jest.fn()
}
});
wrapper = shallowMount(Books, { store, localVue })
});
it("renders the books", async () => {
await flushPromises();
expect(actions.setBooks).toHaveBeenCalled();
});
});
I get an error [vuex] module namespace not found in mapActions(): books/
if I try to namespace the actions code in the my test to:
actions: {
books: {
setBooks: jest.fn()
}
}
I get TypeError: Cannot read property 'setBooks' of undefined
Please help, thank you!
The docs for vue-test-utils include an example of testing with modules.
Change your beforeEach:
beforeEach(() => {
actions = { setBooks: jest.fn() }
store = new Vuex.Store({
modules: {
books: {
state: { books: null },
actions
}
}
})
...
})
Your test calls actions.setBooks, but in your original code actions was simply declared, but not used in the creation of your store.