How to call a component's onClick prop for testing purposes? - javascript

I'm using Jest and Enzyme to test some React components (written using TypeScript). My old method was to use Enzyme's .simulate() function to simulate a click, but this is being deprecated in favor of using instance prop functions (i.e. just using the component's onClick() prop). However, I'm not sure how exactly to call the onClick() function directly. Below is my code:
// Get the onClick function
const buttonOnClick = wrapper.find('#diffpicker-button').first().props().onClick;
// buttonOnClick could be a function or undefined, make sure its a function
if (!buttonOnClick) return;
// Assignment needed here, but to what?
let event: React.MouseEvent<Element, MouseEvent>;
// How to call it?
buttonOnClick(event);
I need to assign my event variable to pass to buttonOnClick(), but what should I assign it to? What does an onClick event actually look like? Or, am I going about this all wrong?

You can call onClick directly using a mock event definition like below:
// arrange
const event = {
preventDefault: jest.fn(),
};
// act
wrapper.find("MobileLink").last().props().onClick(event);
// assert
.
.
.

Related

How to call a method in a child (or any other) component after an event in the parent?

A question like this:
How is it possible in vue3 after an event to call a method in another component?
Found this solution for vue2
this.$refs.myComponent.myMethod()
But how is it supposed to work without "this" in vue3 + composition api?
If this is not possible then what are the alternatives?
The event itself occurs in the following code:
(for example, we can take the resize event of a component and set a task - after it, activate a method inside another component)
<Vue3DraggableResizable class="chartApp"
#resizing="print('resizing')"
>
How is it possible to implement it?
Not sure about if they are best practices but here we go.
I think you can watch your props change and trigger an event like this
import { watch, toRefs } from 'vue'
const props = defineProps({
yourReactiveProp: Boolean
})
const { yourReactiveProp } = toRefs(props) // this is reactive
watch(
() => yourReactivePropn.value,
(newVal) => {
yourMethod() // you can pass the new value also.
})
You can use mitt package. Literally, you can listen and fire emit from anywhere to anywhere. I.e, parent-child, child-parent, siblings, non-siblings, etc..

I use .bind(this) but it keeps been undefined on the imported function? Javascript React

I have a really big class with a lot of handlers, and the document it's starting to be illegible, I want to define the handlers methods outside my class and grouped on a document by type (all the tools have different handlers, so I catch the event and depending on the selectedTool I redirect it to the correct handler), then import those methods/functions and call them from another method like they were normal methods.
The first log, returns this correctly, but for some reason, the log from inside handleSelectorMouseDown returns "undefined" even tho i use .bind(this)
Try this:
handleSelectorMouseDown.bind(this)(e)
This will first bind the imported function to the correct scope, and then execute the function.
Another option is to bind the function in the class constructor and use it later:
class MyComponent {
constrctor() {
this.handleSelectorMouseDown = handleSelectorMouseDown.bind(this);
}
}
Now you just need to call this.handleSelectorMouseDown(e)
You're using bind wrong, you have to first bind then call, I would create a reference at the class level for your binded handlers:
constructor() {
this.onSelectorMouseDown = handleSelectorMouseDown.bind(this);
}
then you can call them as you like:
this.onSelectorMouseDown(e);

In jest/enzyme, how do you simulate a click on an element with eventListener bound by [element].addEventListener (not window.addEventListener)?

In react, when you have an element with an onClick prop, it's easy to use Enzyme's .simulate("click") method to click it. However, there's an example in my codebase where an element is referenced by React's "ref" prop, and then that element's .addEventListener is invoked to bind an event handler to it.
I've provided a code example:https://codesandbox.io/s/1z4xok048j
The key line is this:
if (this.refs.hey) {
this.refs.hey.addEventListener("click", this.handleClick);
}
In the code example, the eventHandler is not bound until componentDidUpdate is run, so if you click on the "click me to increment counter" div, the child element receives new props and its componentDidUpdate triggers. Then if you click on the Child element, its eventHandler triggers as expected. However, I can't reproduce this behavior in my Enzyme/Jest tests.
I've done the obvious .simulate("click") method from Enzyme and it does not work; when I change from using refs and the event listeners to using an onClick, .simulate("click") works as expected. However, removing the refs causes other bugs to surface and I haven't been able to figure out why, so I'd prefer to find out a way to simulate clicking the button.
The code for .simulate("click") actually looks for the onClick function and passes the params to it, there is no 'actual' click going on. You might have to mock the addEventListener functions with something like
// Set-up event listener mock
const map = {};
window.addEventListener = jest.genMockFn().mockImpl((event, callback) => {
map[event] = callback;
});
The answer is actually really simple. The general idea is to .find the node and get the underlying HTML DOM Node with .getDOMNode(). Once you have it, replace its .addEventListener like so:
const map ={};
const namedByRefComponent = component.find(".some-class-name");
const namedByRefDomNode = namedByRefComponent.getDOMNode();
namedByRefDomNode.addEventListener = jest.fn().mockImplementation((event, cb) => {
map[event] = cb;
});
after that, your DOM node's event handlers can be found in map and you can invoke them in the tests.

React - Cannot read property 'target' of undefined

I'm invoking a function like below and understand this is why im getting this error. Is there a way to not invoke the function but still pass the event property?
onMouseOver={(event) => { this.moveBall(event) }}
The reason for wanting to do this is so I can do a check in the function like so:
const element = event.target ? event.target : event;
As I want to re-use this function to pass an element through on load:
// Below line is in a constructor.
this.navItem = document.querySelector('.navigation__item');
// Being called after my render
this.moveBall(this.props.navItem);
Feels like this should be doable..
I've managed to fix this with the below code but I believe that there must be a better way to achieve this:
window.addEventListener('load', () => {
const activeState = document.querySelector('.navigation__item .active')
this.moveBall(activeState)
});
** Update **
Full component code
https://jsfiddle.net/fvn1pu5r/
According to your last update all you need is just move first call to this.moveBall to react lifecycle hook componentDidMount. This ensures that DOM will have .navigation_item nodes in it. So, remove lines
window.addEventListener('load', () => {
const activeState = document.querySelector('.navigation__item .active')
this.moveBall(activeState)
});
from render method and add componentDidMount method to your class, like this:
componentDidMount() {
const activeState = document.querySelector('.navigation__item .active');
this.moveBall(activeState);
}
This should work.
Your moveBall function is being called with undefined as the argument at some stage. The event.target ? check then crashes with the error you gave.
The onMouseOver is likely always fine, as React supplies that.
Instead, I imagine it's the manual call you gave at the end. Is there a time when your this.props.navItem doesn't have a value?
Worth logging out that property right before calling the function to be sure it's always as you expect.

Simulating ReactJS form tag with TestUtils doesn't trigger `onSubmit`

I'm using Sinon to spy on the method, but the method I'm spying doesn't seem to be called at all when I try to Simulate onSubmit on the form tag.
Here's a JSFiddle.
First of all, this line:
React.addons.TestUtils.Simulate.submit(Instance, form.getDOMNode());
Should be:
React.addons.TestUtils.Simulate.submit(form.getDOMNode());
But that doesn't solve the problem, because of the way your component is structured. You're already giving the current _handleSubmit function to React to call, and React will invoke that function. It won't call Instance._handleSubmit(), which is the one you've replaced. If you change your component to something like:
var that = this;
...
<form onSubmit={function() { that._handleSubmit()}}>
It works because you explicitly call the method on the instance.
But I would advice you to not assert that the method was called, but instead assert that whatever side effect it should perform was actually performed. Like mutating state, calling an external service, etc.

Categories