Vue test utils with nuxt - committing a mutation - vue.js

I am using vue test utils with nuxt, I have a standard mutation
this.$store.commit(
'users/UPDATE_NAME',
this.username
)
In my .spec I am trying to access this mutation but I get the error
TypeError: Cannot read property 'commit' of undefined
I have tried mocking it too
it("It should commit the change name mutation", () => {
const wrapper = shallowMount(ChangeName, {
mocks: {
$store: {
// what goes here
}
but don't know how to setup the wrapper so it has access to the mutation inside the module 'users'
this.$store.commit('users/UPDATE_NAME',this.username)
How do I setup the .spec to have access to this mutation?

Related

Using the NuxtJS Strapi Module in Vuex

I am using #nuxt/strapi as a wrapper around Strapi for NuxtJS.
For Vuex inside Nuxt I am using the namespaced modules mode, so my structure looks as an example like this:
store
├── user.js
├── posts.js
└── index.js
In a Vue project I would import Vue, Vuex, maybe Axios and then create a store instance const store = new Vuex.Store(storeOptions) and dispatch my initial actions or make some API calls from inside store/index.js.
Example for index.js in a vue project:
import Vuex from 'vuex';
import Vue from 'vue';
import user from './modules/user'
import posts from './modules/posts'
Vue.use(Vuex);
const storeOptions = { ... }
const store = new Vuex.Store(storeOptions)
if (user) {
...
store.dispatch('startPageLoading', 'store');
store.commit('SET_USER', user);
await store.dispatch("getSomeData");
...
}
But in a Nuxt project the store and the strapi instances already were created in root_project/.nuxt/.... So what is the best way to use $strapi and $store inside store/index.js? Neither $strapi or $store are available in store/index.js
there is this nuxtServerInit action that gets executed on the server side after you open / refresh page.
You can only do it in index.js file
This is how your index.js could look like:
//index.js
export const state = () => ({
someState: false
})
export const mutations = {
SOME_MUTATION(state, payload) {
...
}
}
export const actions = {
SOME_ACTION({ state, commit }, payload) {
...
},
nuxtServerInit({ commit }, { $strapi }){
//do here something on the server side...
}
}
The documentation says if you added the strapi module to your project, you should be able to access it from the context. The context is the second argument from nuxtServerInit
Thats it. This is how a store could look like, nothing fancy.
You could dispatch / commit something from any component with
this.$store.commit("SOME_MUTATION")
They say its global available with this.$strapi
This module globally injects $strapi instance, meaning that you can access it anywhere using this.$strapi. For plugins, asyncData, fetch, nuxtServerInit and Middleware, you can access it from context.$strapi.

Can I call storyapi from vuex?

I'm using storyblok-nuxt module. I plugged it in nuxt.cofig.js and it works fine in page when I call it directly in the asyncData method as such:
asyncData({ app }) {
return app.$storyapi.get("cdn/stories/articles", {
version: "draft"
})
In order to call it from vuex I'm importing it:
import storyapi from 'storyapi'
But Nuxt gives me an error:
Cannot find module 'storyapi'
Can I use this module in vuex, and if yes - what's solution?
Using storyapi with Nuxt is very easy. In your asyncData you can dispatch your action like:
asyncData ({ store }) {
store.dispatch('loadSettings', {version: "draft"})
}
And in your store actions, you can go for this.$storyapi directly. There is no need to import anything. Nuxt take cares of everything for you:
export const actions = {
loadSettings({commit}, context) {
return this.$storyapi.get("cdn/stories/articles", {
version: context.version
}).then((res) => {
// execute your action and set data
commit('setSettings', res.data)
})
}
}
For more info:
How to use the nuxt context in an vuex store?

How to write Vue test cases for Vue method in a component using vue-test-utils and jest which has store dispatch function

i am writing jest test cases for my vue component
i have a method which calls store dispatch function
dismissed() {
this.$store.dispatch("alert/hideAlert");
},
in the test cases i am triggering the dismissed event like below and expecting the store action method to call
wrapper.vm.dismissed();
await wrapper.vm.$nextTick();
expect(actions.hideAlert).toHaveBeenCalled();
but the dismissed method call "wrapper.vm.dismissed();" throws below error while running test cases
Cannot read property 'dispatch' of undefined
How can i test this this vue method ?
I think this is the simplest way to do it.
const mockStore = { dispatch: jest.fn() }
const wrapper = shallowMount(YourComponent, {
mocks: {
$store: mockStore
}
}
wrapper.vm.dismissed();
await wrapper.vm.$nextTick();
expect(mockStore.dispatch).toHaveBeenCalledWith('alert/hideAlert');
But you can also do it in different ways.
Check this article

Nuxt Error: [vuex] Do not mutate vuex store state outside mutation handlers when mutating from plugin

I'm using Firebase alongside a Nuxt project, in the following plugin, I call onAuthStateChanged to check if the user is already logged in, if he is I set the user state and redirect him to the dashboard like so:
import firebase from 'firebase'
export default ({ store, redirect }) => {
if (!firebase.apps.length) {
const config = {
apiKey: 'AIzaSyDFS8Wk6B7ontvZeargY3z7k0u92EJvlN0',
authDomain: 'jammer-bd4bc.firebaseapp.com',
databaseURL: 'https://jammer-bd4bc.firebaseio.com',
projectId: 'jammer-bd4bc',
storageBucket: 'jammer-bd4bc.appspot.com',
messagingSenderId: '156254683024'
}
firebase.initializeApp(config)
}
firebase.auth().onAuthStateChanged((user) => {
if (user) {
store.commit('auth/setUser', user)
redirect('/dashboard')
} else {
redirect('/')
}
})
}
The plugin is referenced in my nuxt.config.js like so:
plugins: [
'~/plugins/firebase'
],
But the following error appear when we reach the store commit:
Error: [vuex] Do not mutate vuex store state outside mutation handlers
As if I was mutating the state directly in the plugin (when as you can see I am not).
What could be causing this problem?
Its because you are comming firebase user object to your vuex store. And it later can be changed by firestore itself. So the solution is to clone it before commiting into vuex
Yes you are mutating the store.
commit() fires a mutation.
dispatch() fires an action
So the easy workaround is to use dispatch('auth/setUser') which is a very simple action that calls the commit('auth/setUser')

Vue test-utils how to test a router.push()

In my component , I have a method which will execute a router.push()
import router from "#/router";
// ...
export default {
// ...
methods: {
closeAlert: function() {
if (this.msgTypeContactForm == "success") {
router.push("/home");
} else {
return;
}
},
// ....
}
}
I want to test it...
I wrote the following specs..
it("should ... go to home page", async () => {
// given
const $route = {
name: "home"
},
options = {
...
mocks: {
$route
}
};
wrapper = mount(ContactForm, options);
const closeBtn = wrapper.find(".v-alert__dismissible");
closeBtn.trigger("click");
await wrapper.vm.$nextTick();
expect(alert.attributes().style).toBe("display: none;")
// router path '/home' to be called ?
});
1 - I get an error
console.error node_modules/#vue/test-utils/dist/vue-test-utils.js:15
[vue-test-utils]: could not overwrite property $route, this is usually caused by a plugin that has added the property asa read-only value
2 - How I should write the expect() to be sure that this /home route has been called
thanks for feedback
You are doing something that happens to work, but I believe is wrong, and also is causing you problems to test the router. You're importing the router in your component:
import router from "#/router";
Then calling its push right away:
router.push("/home");
I don't know how exactly you're installing the router, but usually you do something like:
new Vue({
router,
store,
i18n,
}).$mount('#app');
To install Vue plugins. I bet you're already doing this (in fact, is this mechanism that expose $route to your component). In the example, a vuex store and a reference to vue-i18n are also being installed.
This will expose a $router member in all your components. Instead of importing the router and calling its push directly, you could call it from this as $router:
this.$router.push("/home");
Now, thise makes testing easier, because you can pass a fake router to your component, when testing, via the mocks property, just as you're doing with $route already:
const push = jest.fn();
const $router = {
push: jest.fn(),
}
...
mocks: {
$route,
$router,
}
And then, in your test, you assert against push having been called:
expect(push).toHaveBeenCalledWith('/the-desired-path');
Assuming that you have setup the pre-requisities correctly and similar to this
Just use
it("should ... go to home page", async () => {
const $route = {
name: "home"
}
...
// router path '/home' to be called ?
expect(wrapper.vm.$route.name).toBe($route.name)
});