simulating an onClick method with a parameter using Enzyme in React - javascript

I'm trying to simulate an onClick method in my unit tests using Enzyme for React. I've found many guides to simulating an onClick that takes some event e, such as:
handleClick(e) {
// Does something
}
....
<MyComponent
onClick = {handleClick}
></MyComponent>
However I want to be able to simulate my onClick which does not take the event as a parameter but takes something else instead, ie:
onClick = {() => handleClick(myParam)}
I've tried using .simulate('click', [myParam]); but it did not pass the parameter as I expected.
How would I go about simulating a click that sends a specific parameter to the handler?

according to the documentaton it states that:
.simulate(event[, mock]) => Self Simulate events
Arguments
event (String): The event name to be simulated mock (Object
[optional]): A mock event object that will be merged with the
event object passed to the handlers.
so you need to fix your code and pass an object:
.simulate('click', {myParam});
You can also take a look at the implementaion and see how it is passed to the event handler here:
simulate(event, ...args) {
const handler = this.prop(propFromEvent(event));
if (handler) {
withSetStateAllowed(() => {
// TODO(lmr): create/use synthetic events
// TODO(lmr): emulate React's event propagation
performBatchedUpdates(this, () => {
handler(...args);
});
this.root.update();
});
}
return this;
}

Related

How to listen to an event coming from a component?

I would like to llisten to an event that I can see in Chrome DevTools Vue, but which I do not know how to address.
For a Root event I use
this.$root.$on("note_id", (note_id) => (this.note_id = note_id));
How should I address an event that comes from a specific component? In the example below, I would like to act upon the hide event:
To take the $root example below, I would like to use
this.<here I do not know what to use>.$on("hide", () => this.someVariable = true);
Add a reference to the component (<your-component ref="yourComponent" />) and then access its events via
this.$refs.yourComponent.$on(...)?

Does event object / argument get passed automatically to React event handlers?

The answer is probably, yes. Because it works, and we use it all day, I know. I am asking to be %100 sure and for future learners of React. I couldn't see it in the official docs of React, except giving an example for passing multiple arguments to event handlers alongside the event object. So for example: As you can see onFormSubmit, although not having an event argument inside the JSX reference, it has access to the event object to do stuff (preventing page refresh on this example) at the execution.
If you write the onFormSubmit as an inline arrow function like onChange handler, you need to pass the event object, then it is not automatic.
class SearchBar extends React.Component {
constructor(props) {
super(props)
this.state = { term:''}
this.onFormSubmit = this.onFormSubmit.bind(this)
}
onFormSubmit(event) {
event.preventDefault()
console.log(this.state.term)
}
render() {
return (
<div className="ui segment" >
<form onSubmit={this.onFormSubmit} className="ui form" >
<div className="field" >
<label>Search</label>
<input type="text"
value = {this.state.term}
onChange={event => this.setState({ term: event.target.value })} />
</div>
</form>
</div>
)
}
}
export default SearchBar
You have defined the function to your onChange event handlers which calls the submit method passing the necessary arguments implicity.
There is nothing special about event handlers in React. Every function, if defined works this way.
const a = [1, 2, 3];
function print(elem, idx) {
console.log(elem, idx);
}
// here you have defined the print function.
// the elem and idx are passed implicitly to run the function.
a.forEach(print);
or
// now, rather than defining the function here.
// you have used another arrow function.
// then you need to pass the arguments explicitly.
a.forEach((elem, idx) => print(elem, idx));
React approaches the event handling a little bit differently, using Synthetic Events but this is how callback handlers work generally. If you use a function reference there, the event object is the only argument passed to your function. So, if the event object is the only argument you want to get then you don't need to use an arrow function.
If you want to pass other variables alongside with the event object, then you can use an arrow function.
<form onSubmit={e => this.onFormSubmit(e, otherVar)} className="ui form" >
So, your callback gets the event parameter and you pass this to your handler function with your other variables.
onFormSubmit(event, otherVar) {
event.preventDefault()
console.log(otherVar)
}

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

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
.
.
.

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.

Append Native Event Handler to React Component

In my app, I wish to not use React's own event system. As per the docs:
If you find that you need the underlying browser event for some reason, simply use the nativeEvent attribute to get it.
However, the following doesn't work and returns an error.
// onclick is DOM native event-handler; React's alternative is onClick
<div id = "main" onclick = {( e ) => this.onclickHandler( e )}...
// console error: Unknown event handler property onclick...
Edit:
I understand I can do something along the lines of the following, however, I wonder why the above wouldn't work. Is the following way of binding the handler via addEventListener the only way?
componentDidMount(){
window.addEventListener("click", this.handleblahblah);
...
}
handleblahblah(){
...
}
After thinking about it, I have come to realise my mistake. In case someone else wonders about this, here is the reason this above binding of onclick doesnt work.
React Components are not the same as DOM elements.
In the above code, we are delegating binding of methods over to React. React doesn't recognise onclick, which is a DOM keyword for handling click events.
An alternative to above solution (using addEventListener) could be to access the element via React's refs method. refs gives you access to the underlying DOM element. Then bind onclick method.
class exampleComponent extends Component{
...
componentDidMount(){
this.mainInterface.onclick = (e) => this.onclickEventHandler(e);
}
render(){
...
<div id = "main" refs = {( main ) => this.mainInterface = main }
...

Categories