Here is my problem that I can't solve:
I would like to test the yearsDropdownItems function of my MonthlyFoodCostModal component.
However, I keep getting this error:
TypeError: Cannot read properties of undefined (reading 'outlet_start_date')
How can I set a value to activeOutlet?
Here is my component code :
import { createNamespacedHelpers } from "vuex";
const { mapGetters: userGetters } = createNamespacedHelpers("user");
export default {
computed: {
yearsDropdownItems,
...userGetters([
"activeOutlet"
])
},
};
function yearsDropdownItems() {
const outletStartYear = moment(this.activeOutlet.outlet_start_date).year(),
outletEndYear = moment(this.activeOutlet.outlet_data_validity).year(),
years = this.$utils.range(outletStartYear, outletEndYear);
return this.$utils.sortDesc(years);
}
Here is my test code :
import { mount } from "#vue/test-utils"
import MonthlyFoodCostModal from "#/components/monthlyFoodCostModal/monthlyFoodCostModal.js"
import User from "#/store/Module/User/userModule.js"
import Vue from "vue"
import Vuex from "vuex"
Vue.use(Vuex)
let state;
let store;
describe('Unit test MonthlyFoodCostModal.vue', () => {
beforeEach(() => {
state = {}
store = new Vuex.Store({
modules: {
User: {
state,
getters: User.getters,
namespaced: true
}
}
})
})
it('test yearsDropdownItems method', async() => {
const wrapper = mount(MonthlyFoodCostModal, {store})
expect(wrapper.vm.yearsDropdownItems).toBe("?")
})
})
I didn't put anything in the "expect" part because the error happens before the test is viewed.
My module behaves as follows:
const initialState = () => ({
user: {
firstName: "",
lastName: "",
role: ""
},
properties: [],
activeProperty: null,
activeOutlet: null,
selectedDevicesFilter: [],
});
export default {
namespaced: true,
state: initialState(),
getters: {
activeOutlet: state => state.activeOutlet
}
}
Related
I have a Vue2 sample app and want to add a Vuex store with a todos module. Inside the store folder I changed the index.js file to
import Vue from "vue";
import Vuex from "vuex";
import * as todos from "./modules/todos/index.js";
Vue.use(Vuex);
export default new Vuex.Store({
modules: {
todos
},
});
Inside
/store/modules/todos
I created the following files
index.js
import * as state from "./state.js";
import * as getters from "./getters.js";
export const module = {
namespaced: true,
state,
getters,
};
state.js
export const state = {
todos: []
}
getters.js
export const getters = {
todos(state) {
return state.todos;
}
};
Inside my component I want to access the getter:
<script>
import { mapGetters } from "vuex";
export default {
computed: {
...mapGetters("todos", ["todos"]), // namespace.getter
},
};
</script>
Unfortunately I get this error when loading the component
[vuex] module namespace not found in mapGetters(): todos/
Does someone know what's wrong or missing here? Thanks for help
I think your import/export statements are the culprit.
Try this:
/store.js
import Vue from "vue";
import Vuex from "vuex";
import todos from "./modules/todos";
Vue.use(Vuex);
export default new Vuex.Store({
modules: {
todos
},
});
/store/modules/todos/index.js
import state from "./state";
import getters from "./getters";
const todos = {
namespaced: true,
state,
getters,
};
export default todos;
The other files seem fine.
Work for me
Base API URL
project/src/api/common.js
import axios from 'axios'
export const HTTP = axios.create({
baseURL: 'http://api-url',
})
Base element project/src/api/element.js
import {HTTP} from './common'
function createHTTP(url) {
return {
async post(config) {
return HTTP.post(`${url}`, config).then(response => {
console.log(response)
return response.data
})
},
async get(element) {
return HTTP.get(`${url}${element.id}/`)
},
async patch(element) {
console.log(element)
return HTTP.patch(`${url}${element.id}/`, element).then(response => {
console.log(response)
return response.data
})
},
async delete(id) {
HTTP.delete(`${url}${id}/`)
return id
},
async list(queryParams = '') {
return HTTP.get(`${url}${queryParams}`).then(response => {
return response.data.results
})
}
}
}
export const Todos = createHTTP(`/todos/`)
Your store project/src/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import todos from "#/store/modulse/todos";
Vue.use(Vuex)
export default new Vuex.Store({
modules: {
todos,
}
})
Your mutations types project/src/store/mutation-types.js
export const SET_TODOS ='SET_TODOS'
export const PATCH_TODO ='PATCH_TODO'
export const DELETE_TODO ='DELETE_TODO'
export const CREATE_TODO ='CREATE_TODO'
Your module project/src/store/modulse/todos.js
import {
Todos,
} from '#/api/elements'
import {
SET_TODOS, PATCH_TODO, DELETE_TODO, CREATE_TODO
} from '../mutation-types'
// Getters
export default {
state: {
todos: []
},
getters: {
getTodos(state) {
return state.todos
},
},
// Mutations
mutations: {
[SET_TODOS](state, todos) {
state.todos = todos
},
[PATCH_TODO](state, todos) {
let id = todos.id
state.todos.filter(todos => {
return todos.id === id
})[0] = todos
},
[CREATE_TODO](state, todo) {
state.todos = [todo, ...state.todos]
},
[DELETE_TODO](state, {id}) {
state.todos = state.todos.filter(todo =>{
return todo.id !==id
})
},
},
// Actions
actions: {
async setTodos({commit}, queryParams) {
await Todos.list(queryParams)
.then(todos => {
commit(SET_TODOS, todos)
}).catch((error) => {
console.log(error)
})
},
async patchTodo({commit}, todoData) {
await Todos.patch(todoData)
.then(todo => {
commit(PATCH_TODO, todo)
}).catch((error) => {
console.log(error)
})
},
async deleteTodo({commit}, todo_id) {
await Todos.delete(todo_id)
.then(resp => {
commit(DELETE_TODO, todo_id)
}).catch((error) => {
console.log(error)
})
},
async createTodo({commit}, todoData) {
await Todos.create(todoData)
.then(todo => {
commit(CREATE_TODO, todo)
}).catch((error) => {
console.log(error)
})
},
}
In your project/src/main.js
import Vue from 'vue'
import store from './store'
import App from './App.vue'
import Axios from 'axios'
Vue.prototype.$http = Axios;
new Vue({
store,
render: h => h(App),
}).$mount('#app')
In your project/src/App.vue
import {mapActions, mapGetters} from "vuex";
export default {
name: 'App',
components: {},
data() {
return {}
},
methods: {
...mapActions(['setTodos','patchTodo','createTodo','deleteTodo']),
},
computed: {
...mapGetters(['getTodos']),
},
async mounted() {
await this.setTodos()
},
}
This is my code. Can some please help me figure out the error.I am using jest to test out my frontend which I have built using Vue.The line const localVue = createLocalVue(); is giving out the error TypeError: (0 , _testUtils.createLocalVue) is not a function
import { createLocalVue,shallowMount } from '#vue/test-utils'
import Vuex from 'vuex'
import getters from '../../src/store/module/auth/getters.js'
import TheHeader from '#/components/layout/TheHeader.vue'
// const store = new Vuex.Store({
// state: {
// user:null,
// token:'',
// expiresIn:null,
// isUserLoggedIn:false,
// isAdminLoggedIn:false,
// }
// })
describe('TheHeader', () => {
const localVue = createLocalVue();
localVue.use(Vuex);
let store
let state
it('Checks whether the login is correctly displayed', () => {
const cmp = shallowMount(TheHeader, { store,localVue})
expect(cmp.name()).toMatch('TheHeader')
expect(cmp.vm.isLoggedIn()).toBe(false)
})
})
createLocalVue was removed in version 2 of #vue/test-utils, which explains why it's undefined in your example.
To install a Vue plugin (such as Vuex), use the global.plugins mounting option
To mock instance APIs (such as this.$store), use the global.mocks mounting option
import Vuex from 'vuex'
import { shallowMount } from '#vue/test-utils'
import TheHeader from '#/components/TheHeader.vue'
const store = /* Vuex store */
const cmp = shallowMount(TheHeader, {
global: {
plugins: [Vuex],
// OR:
mocks: {
$store: store,
}
}
})
import { mount } from '#vue/test-utils'
import { createApp } from 'vue'
import { createStore } from 'vuex'
import App from '#/views/Home'
creating a fake store
const store = createStore({
state() {
return {
count: 0,
user: {},
}
},
mutations: {
increment(state) {
state.count += 1
},
},
})
Creating the Component
const app = createApp(App)
app.use(store)
let wrapper
beforeEach(() => {
wrapper = mount(App, {
global: {
plugins: [store],
},
computed: { showAlert: () => false },
})
})
now you can do the test
test('Home', async () => {
expect(wrapper.vm.showAlert).toBe(false)
})
I would like to test if my getter is called in my component or view, but I don't understand how can it.
My method in the component :
computed: {
...mapGetters({ item: 'moduleA/getData' }),
},
And this is my unit test declaration :
beforeEach(() => {
store = new Vuex.Store({
modules: {
moduleA: {
state: {},
getters: {
getData() {
return { item: { name: 'test' } };
},
},
},
},
});
wrapper = shallowMount(MyComponent, {
store,
});
});
And I try to test if the data is loaded in my template:
it('should check if name is thruthy', () => {
expect(wrapper.find('.classA').text()).toEqual('test');
});
my component :
<template>
<v-content>
<ComponentA v-if="item.name"
key="keyA" ></ComponentA>
<ComponentB v-else key="keyA"></ComponentB>
</v-content>
</template>
<script>
import { mapGetters } from 'vuex';
import ComponentA from '#/components/ComponentA.vue';
import ComponentB from '#/components/ComponentB.vue';
export default {
/**
* Component's name :
*/
name: 'ViewA',
/**
* Component's used :
*/
components: {
ComponentA,
ComponentB,
},
/**
* computed's used :
*/
computed: {
...mapGetters({ item: 'moduleA/getData' }),
},
};
</script>
and my getter in the moduleA :
const getters = {
getData: state => state.data,
};
But I have this message:
TypeError: Cannot read property 'name' of undefined
Why? Thank you for your help.
As shown in docs, shallowMount's options has field named localVue. To solve your problem you should create a localVue, using createLocalVue and pass it to shallowMount.
createLocalVue returns a Vue class for you to add components, mixins
and install plugins without polluting the global Vue class.
import Vuex from 'vuex'
import { createLocalVue, shallowMount } from '#vue/test-utils'
beforeEach(() => {
const localVue = createLocalVue()
localVue.use(Vuex)
store = new Vuex.Store({
modules: {
moduleA: {
state: {},
getters: {
getData() {
return { item: { name: 'test' } }
},
},
},
},
})
wrapper = shallowMount(MyComponent, {
localVue,
store,
})
})
I'm trying to split up my Nuxt Vuex store files into separate files. And NOT have all Vuex getters, mutations and actions into one huge file. This demo project is on Github by the way.
I'v read this official Nuxt Vuex Store documentation; but can't seem to get it working. It's a bit vague on where to put stuff.
I have the following in these files:
Below is my: store/index.js
import Vue from "vue";
import Vuex from "vuex";
import Auth from "./modules/auth";
Vue.use(Vuex);
export const store = () => {
return new Vuex.Store({
state: {
},
modules: {
Auth
}
})
}
This is in my: store/auth.js
const state = () => {
username: null
};
const getters = {
username: state => {
return state.username;
},
isAuthenticated: state => {
return state.username != null;
}
};
const mutations = {
login: (vuexContext, username) => {
vuexContext.username = username;
this.$router.push("/dashboard");
},
logout: vuexContext => {
vuexContext.username = null;
this.$router.push("/");
}
};
const actions = {
};
export default {
state,
getters,
mutations,
actions,
};
And finally in my: pages/index.vue
This is where I'm calling that login mutation:
<script>
export default {
layout: "non-logged-in",
data() {
return {
username: null
}
},
methods: {
onSubmit() {
this.$store.commit("login", this.username);
}
}
}
</script>
The error I'm getting:
[vuex] unknown mutation type: login
What am I doing wrong here? I thought i'm importing all the stuff correctly in the store/index.js
You have to export your store constant like this inside your store/index.js file:
export default store
Put this code line at the end of your file.
So as #jeremy.raza described this is what I changed in order to get it working:
store/index.js
import Vue from "vue";
import Vuex from "vuex";
import Auth from "./modules/auth";
Vue.use(Vuex)
const store = () => {
return new Vuex.Store({
state: {
},
modules: {
Auth
}
})
}
export default store;
Changes in the store/auth.js
Note the changes in how I wrote the state, getters and mutations method notation.
const state = () => ({
username: null
});
const getters = {
username(state) {
return state.username;
},
isAuthenticated(state) {
return state.username != null;
}
};
const mutations = {
login(vuexContext, username) {
vuexContext.username = username;
this.$router.push("/dashboard");
},
logout(vuexContext) {
vuexContext.username = null;
this.$router.push("/");
}
};
const actions = {
};
export default {
state,
getters,
mutations,
actions,
};
I'm using Vuex in my project with vue.js 2.0. My app.js looks like this:
import VueRouter from 'vue-router';
import Login from './components/Login.vue';
import Home from './components/Home.vue';
import VModal from 'vue-js-modal';
import Vuex from 'vuex';
import store from './store';
window.Vue = require('vue');
Vue.use(VueRouter);
Vue.use(VModal);
Vue.use(Vuex);
window.Bus = new Vue();
const routes = [
{ path: '/', component: Login, name: 'login' },
{ path: '/home', component: Home, name: 'home', beforeEnter: requireAuth },
];
const router = new VueRouter({
routes // short for `routes: routes`
});
const app = new Vue({
router,
store
}).$mount('#app');
function requireAuth() {
return this.$store.state.isLoggedIn;
}
My store looks like this:
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const LOGIN = "LOGIN";
const LOGIN_SUCCESS = "LOGIN_SUCCESS";
const LOGOUT = "LOGOUT";
const store = () => {
return new Vuex.Store({
state: {
isLoggedIn: !!localStorage.getItem("token"),
user: null
},
mutations: {
[LOGIN] (state) {
state.pending = true;
},
[LOGIN_SUCCESS] (state) {
state.isLoggedIn = true;
state.pending = false;
},
[LOGOUT](state) {
state.isLoggedIn = false;
}
},
actions: {
login({state, commit, rootState}) {
commit(LOGIN_SUCCESS);
},
setUser({state, commit, rootState}, user) {
//todo
}
}
});
}
export default store;
However when I try to access a value from the state in my requireAuth function:
return this.$store.state.isLoggedIn;
or
return this.store.state.isLoggedIn;
I get the error:
Cannot read property '$store' of undefined
What could be wrong here?
--EDIT--
When I console.log(store); I see:
store() {
var _mutations;
return new __WEBPACK_IMPORTED_MODULE_1_vuex__["a" /* default */].Store({
state: {
isLoggedIn: !!localStorage.getItem("token"),
You have the function declared in the global scope like this:
function requireAuth() {
return this.$store.state.isLoggedIn;
}
So when you call this function this is bound by default to global object. But since ES6 this will be undefined instead of the global object. So you get the error cannot read $store of undefined
Since you are importing the store in app.js you can directly use:
function requireAuth() {
return store.state.isLoggedIn;
}
EDIT
export the created store instance itself, not a function that returns a store instance as follows:
store.js
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const LOGIN = "LOGIN";
const LOGIN_SUCCESS = "LOGIN_SUCCESS";
const LOGOUT = "LOGOUT";
const store = new Vuex.Store({
state: {
isLoggedIn: !!localStorage.getItem("token"),
user: null
},
mutations: {
[LOGIN] (state) {
state.pending = true;
},
[LOGIN_SUCCESS] (state) {
state.isLoggedIn = true;
state.pending = false;
},
[LOGOUT](state) {
state.isLoggedIn = false;
}
},
actions: {
login({state, commit, rootState}) {
commit(LOGIN_SUCCESS);
},
setUser({state, commit, rootState}, user) {
//todo
}
}
});
export default store;
The requireAuth function should be:
function requireAuth() {
return store.state.isLoggedIn;
}
requireAuth is just a function defined in your app.js and this.$store is how you would refer to the store inside a Vue method. Instead, you can just refer to it in the function as store.