I'm writting a unit test that first runs some code outside a component, but a function on a component will eventually be executed because of that call. The code is working correctly but the tests are failing.
I'm writting a test that instantiates a component with var mycomponent = new Component()
Then I create the Spy With var sinon.spy(mycomponent, 'functionname')
When I run the test, I can see on the logs that the function on the component is being called, but my assert says it's not being called.
The component on the test is being run as a shallowMount, I wonder if that might make a difference.
My questions would be:
How can I spy on a function on a component when the function that eventually triggers the function on the component is not in the component
What's the best way to assert with sinon
I was trying to spy on the wrapper that I had on the component. Using .spy on wrapper.vm fixed the issue
Related
I am using lodash debounce inside several forms, which are rendered in Vue's v-for. My problem is the debounce has to be applied to all of them separatedly, but currently I am calling the method on every form created and that function is debounced across all the form components. Only the latest created one does the request.
The function is:
import debounce from 'lodash/debounce';
const debouncedValidateForm = debounce((vm) => {
vm.$nextTick(() => vm.validateForm({ form: vm.form.uuid, data: vm.answers }));
}, 1000);
It gets called in a watcher when each form loads its instance data. All the form components do, but as explained before all the components interfere with each other's debounce.
I am quite new to Vue.js and while developing my first project i have stumbled upon a problem which I think is related to the component's lifecycle.
The context is the following:
I am using Vue 3 with composition API.
I have a "Map" component in which I use d3.js to show a chart.
In my Setup() method I have onBeforeMount() and onMounted().
In onBeforeMount() method, I want to use data from my Firestore database for the values in the chart. The console.log on line 47 shows the data correctly.
In onMounted(), is where I plan to put my d3 chart. But when I try to access the data as in console.log on line 55, I get undefined...
Ideally, from "Map" component, I want to fetch the data from my database and use it in order to create the chart and then have the component render the chart.
Then I would have another component called 'MapSettings" that will be able to alter the data in "Map" such as filters etc...and have the values automatically updated in the chart.
Finally, both components will be siblings and have same parent component. So "Map" and "MapSettings" are on same hierarchical level.
But first I need to understand why I am getting undefined.. Any help or suggestion would be greatly appreciated!
Your lifecycle hooks look nice. The problem that you're facing has to do with code been executed asynchronously and code been executed synchronously.
You're declaring a function that uses async-await feature. This function will be executed asynchronously. In this case, you're getting data from Firestore and storing it in a ref at onBeforeMount().
On the other hand, you're declaring a normal function at onMounted() trying to access a ref's value, which will result in undefined because the function that you define at onBeforeMount() didn't finish its execution (or it's in the event queue) when onMounted is called.
That's why you first see the console.log that comes from onMounted.
One solution would merge both functions in one lifecycle hooks and use async await:
setup() {
const {actorDocs, load} = getActorDocs()
const actorsData = red([])
// load actor data from db
onBeforeMount( async () => {
await load()
actorsData.value = actorDocs
console.log(actorsData.value)
// manipulate it here...
})
}
Keep in mind that you cannot pause with async-await a lifecycle hook. What you're pausing is the function that Vue will execute in that hook. Indeed, this is really nice because pausing a hook wouldn't be efficient at all.
i face that problem, in my case i want use imgsRef.value out of onBeforeMount scope. How to get imgsRef value out of beforeMount scope?
onBeforeMount( async () => {
await axios
.get("http://localhost:3000/ourMoment")
.then((response) => {
imgsRef.value = response.data
// imhsRef get the value from api
console.log(imgsRef.value.photo)
})
})
// i try to concole.log here but the value still empty
console.log(imgsRef.value)
I'm trying to write a unit test for a VueJS 2 component that has a dynamic component that changes as the state changes.
<template>
<div>
<component ref="dynamicComponent" id="dynamicComponent" :is="someDynamicType" #custom-event="handleCustomEvent">
</component>
</div>
</template>
What i'm trying to test is that the parent component is handling the custom-event from the child dynamic component correctly. However, the problem I'm running into is that I cannot seem to get a reference to the child dynamic component.
I've tried
await wrapper.findComponent('#dynamicComponent').vm.$emit("custom-event", {});
and
await wrapper.find({ref: 'dynamicComponent'}).vm.$emit("custom-event", {});
and I also tried using the known type of the dynamic component
await wrapper.findComponent(KnownSubType).vm.$emit("custom-event", {});
but in each case, I get the following error
TypeError: Cannot read properties of undefined (reading '$emit')
Is there another way to obtain the component so that I can emit the custom event or is there another way to trigger the event to test my parent component?
I ran into a similar problem writing tests using dynamic components. To ensure that the child component was loaded before manipulating it in my tests I had to flush promises. This can be either written manually like in this answer: https://stackoverflow.com/a/65665052/11015616
or
the flush-promises npm library can be used.
const wrapper = mount(SomeComponent, {options});
await flushPromises();
// manipulate child component
Say you have a vue component with a method like this:
methods:{
doSomething(someParameter){
//maybe do something with that Parameter
this.$store.commit("storeSomething",someParameter);
let someParameter2 = this.transformToSth(someParameter);
this.$store.commit("storeSomethingElse",someParameter2);
}
}
What do I have to do, so that this kind of test works in Jest?
test("that the commit was correctly called",()=>{
wrapper.vm.doSomething(someParameter);
expect(wrapper.vm.$store.commit).hasBeenCalledWith(someParameter);
expect(wrapper.vm.$store.commit).hasBeenCalledWith(someParameter2);
})
Also note that I want that the mock is also reverted again, so that method uses the same implementation as before. Otherwise, I create a dependency between tests, which I very much like to avoid.
(I also do hope it works the same way with actions and getter Functions)
So I found that one can solve this with spyOn:
test("that the commit was correctly called", () => {
let spy = jest.spyOn(userCardEditingWrapper.vm.$store, "commit");
wrapper.vm.doSomething("test");
expect(spy).toHaveBeenCalledWith("storeSomething", someParameter);
expect(spy).toHaveBeenCalledWith("storeSomethingElse", someParameter2);
});
Credit to #devTea from the Vue Discord Channel that gave me the hint with jest.fn().
I am utilizing a payment component that uses a callback function to show whether or not the payment was successful I am trying to execute a function in that callback method that tells the backend that payment was complete with some details.
Sample of component
<component
:amount="amount"
:ref="ref"
:callback="processPayment"
:close="close"
>Proceed to payment</component>
Payment callback method
processPayment: res => {
//window.alert("Payment recieved");
this.postPayment(res); // <-- this does not execute.
}
Any ideas how I could get it to work ?
This is an issue with this.
this in arrow function does not refer to the owner of the function, rather arrow function takes this from it's context, which will not be the Vue instance
So, avoid using arrow functions as Main methods in Vue component. You can use arrow function as callback.
processPaymentfunction(res){
//window.alert("Payment recieved");
this.postPayment(res); // <-- this will execute.
}