vue test url fails on the clicked Button - vuejs2

I wanted to create a simple Test on Vuejs.
The purpose is to click a router-link and prove that the pathname s correct.
Here is the Code:
describe("Menu.vue", () => {
const localVue = createLocalVue();
localVue.use(VueRouter);
test("click on first routes to ./first", async () => {
const wrapper = shallowMount(Menu, {
localVue,
router,
stubs: {
RouterLink: RouterLinkStub,
First
}
});
await wrapper.find("#testFirst").trigger("click");
await Vue.nextTick();
expect(wrapper.find("#testFirst").exists()).toBe(true);
await flushPromises();
expect(location.pathname).toBe("/first")
});
});
The main component is a Menu.vue with router link id testFirst, the error comes from the last line where the expect fails.
The other component is named first.vue.
The first expect passes proving that the #testFirst exists.
it expects"/first" and received "/"
Thanks

Instead of checking location pathname you can test
expect(wrapper.vm.$route.push).toHaveBeenCalledWith({YOUR ROUTE})
For this, you have to mock the $route push method that you can do in stubs while creating a wrapper for your componenet.
mocks: {
$router:{
push:jest.fn()
}
}

Related

How to test Vue Component method call within an async method

I believe I am struggling to properly mock my methods here. Here is my situation, I have a component with two methods;
name: 'MyComponent',
methods: {
async submitAction(input) {
// does await things
// then ...
this.showToastMessage();
},
showToastMessage() {
// does toast message things
},
}
And I want to write a test that will assert that showToastMessage() is called when submitAction(input) is called. My basic test looking something like this;
test('the toast alert method is called', () => {
let showToastMessage = jest.fn();
const spy = jest.spyOn(MyComponent.methods, 'showToastMessage');
const wrapper = shallowMount(MyComponent, { localVue });
const input = // some input data
wrapper.vm.submitAction(input); // <--- this calls showToastMessage
expect(spy).toHaveBeenCalled();
};
NOTE: localVue is declare as such at the top of the file const localVue = createLocalVue();
I confirmed that both submitAction() and showToastMessage() methods are being called during the tests, by sneaking a couple of console.log()'s and observing it in the test output, however the test still fails;
expect(jest.fn()).toHaveBeenCalledWith(...expected)
Expected: called with 0 arguments
Number of calls: 0
566 | const wrapper = shallowMount(MyComponent, { localVue } );
567 | wrapper.vm.submitAction(input);
> 568 | expect(spy).toHaveBeenCalledWith();
I've tried spying on both methods as well
const parentSpy = jest.spyOn(MyComponent.methods, 'submitAction');
const spy = jest.spyOn(MyComponent.methods, 'showToastMessage');
// ...
expect(spy).toHaveBeenCalled()
same results, test fail.
What am I missing?
Tech Stack: vue 3, jest, node 14
#TekkSparrow you can pass a heap of stuff into the shallowMount function. It accepts an object as a second argument which can look something like
import { shallowMount, createLocalVue } from '#vue/test-utils'
import Vuex from 'vuex'
const localVue = createLocalVue()
localVue.use(Vuex)
let mocks = {
// this could be something like the below examples
// I had in a previous project
$route: {
query: '',
path: '/some-path'
},
$router: [],
$validator: {
validateAll: jest.fn()
},
$toast: {
show: jest.fn(),
error: jest.fn()
},
}
let propsData = {
// some props you want to overwrite or test.
// needs to be called propsData
}
let methods = {
showToastMessage: jest.fn()
}
let store = new Vuex.Store({
actions: {
UPLOAD_ASSET: jest.fn(),
},
})
const wrapper = shallowMount(MyComponent, { mocks, propsData, methods, store, localVue })
I believe that by doing similar to the above, your mocked function will run and be recorded by the Jest spy.
Took me a minute to realize/try this, but looks like since my calling function is async that I was suppose to make my test async, and await the main method call. This seems to have done the trick. Here's what ended up being my solution:
test('the toast alert method is called', async () => {
let showToastMessage = jest.fn();
const spy = jest.spyOn(MyComponent.methods, 'showToastMessage');
const wrapper = shallowMount(MyComponent, { localVue });
const input = // some input data
await wrapper.vm.submitAction(input);
expect(spy).toHaveBeenCalled();
};

Cannot use Vue-Router to get the parameters in the URL

Today, when trying to use Vue-Router (in Vue-CLI) to get URL parameters, I encountered difficulties ($route.query is empty), the code is as follows.
Code purpose: Get the parameters carried after the URL (such as client_id in "http://localhost:8080/#/?client_id=00000000000077")
Project file structure:
router/index.js:
App.vue(Get part of the code for URL parameters):
The running result of this part of the code:
I'm not sure why $router.currentRoute and $route aren't matching up, but you could simply use $router.currentRoute.query.client_id if you need it in mounted().
Another workaround is to use a $watch on $route.query.client_id:
export default {
mounted() {
const unwatch = this.$watch('$route.query.client_id', clientId => {
console.log({ clientId })
// no need to continue watching
unwatch()
})
}
}
Or watch in the Composition API:
import { watch } from 'vue'
import { useRoute } from 'vue-router'
export default {
mounted() {
console.log({
route: this.$route,
router: this.$router,
})
},
setup() {
const route = useRoute()
const unwatch = watch(() => route.query.client_id, clientId => {
console.log({ clientId })
// no need to continue watching
unwatch()
})
}
}

Vue testing beforeRouteEnter navigationGuard with JEST / Vue-test-utils

i have a component which uses
beforeRouteEnter(to, from, next) {
return next((vm) => {
vm.cacheName = from.name
})
},
it takes previous route name and saves it into current component variable calld - cacheName
how can i test that with JEST? i was trying to
test('test', async () => {
await wrapper.vm.$options.beforeRouteEnter(null, null, () => {
return (wrapper.vm.cacheName = 'testname')
})
console.log(wrapper.vm.cacheName)
})
but it doesnt rly cover the test.. i guess i have to mock next function somehow but i just dont know how, please help me :<
Testing beforeRouteEnter:
I was trying with jest, In my use case, it worked with the below code.
vue-testing-handbook Reference Link
it('should test beforeRouteEnter', async () => {
const wrapper = shallowMount(Component, {
stubs,
localVue,
store,
router,
i18n,
});
const next = jest.fn();
Component.beforeRouteEnter.call(wrapper.vm, undefined, undefined, next);
await wrapper.vm.$nextTick();
expect(next).toHaveBeenCalledWith('/');
});

How to test VueX state change in the component using vuex-test-utils?

I have a very simple component that relies on the data from the backend being loaded in the store, and I wan to write a unit test for this flow.
Basically, my template code is:
<div class="my-component">
<div class="loading-screen" v-if="loading"></div>
<div class="content" v-if="!loading"></div>
</div
Loading is a computed value that comes from the store.
I want to test it with the following test scenario:
describe('My Component', () => {
let wrapper;
let actions;
let store;
let state;
let mutations;
beforeEach(() => {
actions = {};
state = {
loading: true,
};
mutations = {
finishLoading: (state) => { state.loading = false },
};
store = new Vuex.Store({
modules: {
myModule: {
namespaced: true,
state,
actions,
mutations,
}
}
});
});
test('Calls store action for data and then shows the page', () => {
wrapper = mount(MyComponent, { store, localVue });
expect(wrapper.find('.loading-screen').isVisible()).toEqual(true);
expect(wrapper.find('.content').exists()).toEqual(false);
store.commit('finishLoading');
expect(wrapper.find('.loading-screen').exists()).toEqual(false);
expect(wrapper.find('.content').isVisible()).toEqual(true);
});
});
The part after store.commit('finishLoading') fails. How can I trigger the component to update based on the store data?
Try to add this line after store.commit('finishLoading').
await wrapper.vm.$nextTick();
And remember to make your function async.
test('Calls store action for data and then shows the page', async () => {
I found out later that I also missed one thing!
My store is namespaced, so naturally as I don't create a NamespacedHelper for it, I need to call the following mutation:
store.commit('applicationApply/finishLoading');
This is an addition to the valid fix above, which resolves the main question.

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)
});