I'm getting familiar with jest and vue and I wanted to see how to make sure a method fired when a prop changed. In this particular scenario it's trivial and this seemed straight forward. But it's not working.
Components Watcher
@Watch("id")
public async idChanged() {
this.calculateStatus();
}
beforeEach - this initilizes the wrapper for each test
beforeEach(async () => {
var httpClient = new PemHttpClient(vue);
var v3ReferenceDatumService = new V3ReferenceDatumService(httpClient, "");
var contractService = new V3ContractService(httpClient, "", v3ReferenceDatumService);
wrapper = mount(AmendmentIdDisplay, {
provide: {
v3ContractService: contractService,
},
propsData: {
id: "82.5.1"
}
});
await wrapper.vm.$nextTick();
})
Jest Test
let calculateFired = jest.spyOn(wrapper.vm, "calculateStatus");
wrapper.setProps({
...wrapper.props(),
id: "1"
})
await wrapper.vm.$nextTick();
expect(calculateFired).toBeCalled();
I would expect the spy to have incremented the call counter but it does not. It remains at zero. If I manually call wrapper.vm.calculateStatus(), the spy works correctly. So the setProps is either not firing the watcher at all, or some weird reference thing is happening which is causing the method that is called within the watcher, to not be the method I'm spying on. I'm not sure which.
I hope it is not too late. Yes there is a problem with jest.spyOn() and vue watchers. I have a trick that patch the problem for now (tested on sync function only) :
const insertSpyWatcher = (vueInstance: any, watcherExpression: string, spyInstance: jest.SpyInstance) => {
let oldWatcherIndex = -1;
let deep = false; // pass the deep option value from the original watcher to the spyInstance
// find the corresponding watcher
vueInstance._watchers.forEach((watcher: any, index: number) => {
if (watcher.expression === watcherExpression) {
oldWatcherIndex = index;
deep = watcher.deep;
}
});
// remove the existing watcher
if (oldWatcherIndex >= 0) {
vueInstance._watchers.splice(oldWatcherIndex, 1);
} else {
throw new Error(`No watchers found with name ${watcherExpression}`);
}
// replace it with our watcher
const unwatch = vueInstance.$watch(watcherExpression, spyInstance, { deep });
return unwatch;
};
Then in your test :
it('test the watcher call', () => {
let calculateFired = jest.spyOn(wrapper.vm, "calculateStatus");
insertSpyWatcher(wrapper.vm, "id", calculateFired) // Yes 'id' is the name of the watched property
wrapper.setProps({
...wrapper.props(),
id: "1"
})
await wrapper.vm.$nextTick();
expect(calculateFired).toBeCalled();
});
If the immmediate
property is needed, you can always add it as argument of insertSpyWatcher
. I did not find a way to get the immediate
property of the original watcher.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With