How to cover lines inside event handler in jest testing with vuejs - vue.js

I am writing unit tests for my vuejs component which adds an event listener on mounting a component like below
mounted() {
window.$on('test_event', () => {
console.log('test_event was triggered');
this.testCount++;
}
}
But I dont get the console line covered when run the mount for component in the unit test.
How to cover the lines inside arrow event handler function?

Related

Using the mitt-library in Vue with Webpack hot-reload causes problems?

In my Vue3 app, I'm using the mitt eventbus library to emit and receive events between components.
I put this in onMounted of a list component that needs to refresh:
mitt.on("list_refresh", (evt) => {
refresh();
});
In another component that contains the list-component as a child (or grandchild), I do this in a method:
mitt.emit("list_refresh", {});
This works ok, but while developing with hot-reload on, the events seem to be emitted multiple times, as if they're created extra each time the app reloads, instead of overwriting the old ones.
When I reload the entire page in the browser, it works fine again.
Any idea to prevent this?
It looks like your component is missing a corresponding off() call to remove the event listener. During hot reload, the current component instances unmount, and new ones mount; so if you're not removing current event listeners, you'll just pile on new event listeners. To resolve the issue, use the onUnmounted hook to remove the event listener when the component is removed from the DOM.
Also, make sure to pass cached function references (instead of inline functions) to mitt.on() and mitt.off() to ensure the given event listener lookup succeeds in mitt.off():
// mitt.on('list_refresh', () => refresh()) ❌
mitt.on('list_refresh', refresh) ✅
mitt.off('list_refresh', refresh)
Your setup() should look similar to this:
import { onMounted, onUnmounted } from 'vue'
export default {
setup() {
const refresh = () => { /*...*/ }
onMounted(() => mitt.on('list_refresh', refresh))
onUnmounted(() => mitt.off('list_refresh', refresh)) 👈
}
}

Vue Testing Library won't rerender DOM when state changes

I have a component with a strait forward Edit button. The Edit button calls a method that sets isEditing to true.
There are a few input elements with v-if="isEditing", so I'm testing that those input elements are visible after the Edit button is clicked.
When my test runs fireEvent.click(screen.getByRole('link', {name: 'Edit'})), it is updating isEditing to true (based on my console.log messages before/after the .click event), but it doesn't seem to re-render the components within the test (based on the DOM rendered in my terminal after getByRole fails).
It works as expected in the browser, but doesn't seem to update the DOM for the spec. I'm using Vue2, Vue Testing Library, and Jest.
Implementation:
<template>
<a #click.prevent="startEdit" v-if="!isEditing">Edit</a>
<input :v-if="isEditing" />
</template>
...
methods: {
startEdit: () => {
this.isEditing = true
}
}
Spec:
describe('FormComponent', () => {
beforeEach(() => {
render(FormComponent)
})
it('displays input tags' () => {
fireEvent.click(screen.getByRole('link', {name: 'Edit'}))
expect(screen.getByRole('input')).toBeInTheDocument()
})
})
The problem is your expect is running before the DOM has had a chance to update. From the testing library documentation:
Because Vue applies DOM updates asynchronously during re-renders, the fireEvent tools are re-exported as async functions. To ensure that the DOM is properly updated in response to an event in a test, it's recommended to always await fireEvent.
You should update your test to await the fireEvent promise like so:
it('displays input tags' async () => {
await fireEvent.click(screen.getByRole('link', {name: 'Edit'}))
expect(screen.getByRole('input')).toBeInTheDocument()
})
You also have a typo in your second v-if as Nicole pointed out in her answer.
It doesn't work because you wrote :v-if when it should be v-if. I guess this was simply a typo since you did it correctly the first time (v-if="!isEditing")

Vue.js: Global event bus is failing to catch the event

I am trying to create a global event bus to be emitted from Source.vue to be caught in Destination.vue. The latter component has to run a function when the event is caught.
Source.vue:
.then(() => {
eventBus.$emit("fireMethod");
})
Destination.vue:
updated() {
eventBus.$on("fireMethod", () => {
this.reqMethod();
});
}
main.js:
export const eventBus = new Vue();
I have also imported the main.js file in both of the components. But that doesn't seem to work as the event is not caught at all. What's wrong here?
Thanks in advance
The updated hook runs after data changes on your component and the DOM re-renders. Hence on the initial render your event listener is not registered as the event is not triggered at that time.
Kindly go through this fiddle to get a clear idea about vue components life cycle.
Hence in Destination.vue:
Your event hook should be mounted and not updated.
mounted() {
eventBus.$on("fireMethod", () => {
this.reqMethod();
});
}
To register global event bus you can use this in your main.js
Vue.prototype.$eventHub = new Vue(); //global event bus
And then in your components use it like
//on emit
this.$eventHub.$on('your-event',function(){});
//to emit
this.$eventHub.$emit('your-event',data);

How to run code on App Start-up in React-Native

So I have a react-native application and want to run some code on app start-up;
The application has background task handlers(android) which (to the best of my knowledge) does not mount any views so initializing stuff in the root constructor or componentDidMount may not work.
I want to add certain database listeners to my application which get triggered even while the app is being run in background.
Any help on the same would be highly appreciated.
Thanks regards.
Amol.
In functional components, you want to use the useEffect() method with an empty dependency array:
useEffect(() => {
// this code will run once
}, [])
When an empty dependency array ([]) is used, the useEffect() callback is called only once, right after the component renders for the first time.
Use like this:
import React, { useEffect } from 'react'
export default function App () {
useEffect(() => {
// this code will run once
}, [])
// ...
}
React-Native has a function super() which is same as constructor() that will work when your application get started. For example if you write a alert message on your super() function('When a user open your app, an alert message will be display'. You are able to get data using super() function when your app is opened)
super(){
alert('app started')
}

How do I execute a function in Vue whenever the window is active?

Vue.js has mounted, created, updated etc. as states I can execute functions on the first load of the page - however how I execute for example a console.log('hi'); whenever the user navigates to a particular page/route without reloading the whole app?
Vue router exposes lifecycle hooks where you can execute you code: docs
beforeEach is the way to go if you need to execute a code upon every route navigation.
Alternatively, you can watch a current route inside your main (wrapper) component and execute code inside a watcher: docs, section Reacting to Params Changes
watch: {
'$route' (to, from) {
// react to route changes...
}
}