I'm working on a React project using Semantic UI React. I'm doing unit testing with Jest and React Testing Library (RTL).
A common situation when testing React components is simulating the user entering text into an input component. React Testing Library uses the fireEvent.change() method. fireEvent.change() takes two parameters. The first is the target node, the second is an object with the value.
fireEvent.change(element, { target: { value: 'desired text' } });
The RTL documentation has an example of how to use fireEvent to change the value of an input.
This works for the normal < input /> element. But we are using Semantic UI React's Input component. The Semantic UI React input doesn't just render an input on the page. It renders an input inside a div. Take a look at the difference:
Testing a Normal input
The way to set and test a normal input is to render the component, use a query to find the input, then fireEvent.change on the input.
it('can change the value of an input', () => {
const { getByTestId } = render(<input data-testid='input'/>);
const element = getByTestId('input');
fireEvent.change(element, { target: { value: 'desired text' } });
expect(element).toHaveValue('desired text');
});
Doesn't Work on a Semantic UI React Input
That works fine for a normal input. But try this on the Semantic UI Input:
import { Input } from 'semantic-ui-react';
it('can change the value of a Semantic UI React Input', () => {
const { getByTestId } = render(<Input data-testid='input'/>);
const element = getByTestId('input');
fireEvent.change(element, { target: { value: 'desired text' } });
expect(element).toHaveValue('desired text');
});
- The given element does not have a value setter
What's going on here?
The query is returning the < div > that Semantic UI React wraps around the input. It's not getting the input itself. fireEvent.change is telling you that you can't set the value of the div, which makes sense. So how do you get hold of the input inside the div?
The Solution
The variable element has a property children, which is an array of the children of element. The input we want to change is the first child of element, so it's element.children[0]. Now you've got the reference to the input you want to change and test.
import { Input } from 'semantic-ui-react';
it('can change the value of a Semantic UI React Input', () => {
const { getByTestId } = render(<Input data-testid='input'/>);
//this is a reference to <div><input/><div/>
const element = getByTestId('input');
//get a reference to the first child of element
const elementInput = element.children[0];
fireEvent.change(elementInput, { target: { value: 'desired text' } });
expect(elementInput).toHaveValue('desired text');
});
That's how you can target and test the Semantic UI React Input component.
Other Methods
You can use other queries to find the inner input in Input directly. If you assign placeholder text to your input, you can use the query getByPlaceholderText to find the inner input by finding the placeholder text. But in my situation I was looking for an Input that didn't have placeholder text.
import { Input } from 'semantic-ui-react';
it('can get the inner input of a Semantic UI React Input directly', () => {
const placeholderText = 'placeholder';
const { debug, getByPlaceholderText } = render(
<div>
Here's a div with some text content
Then a Semantic UI React Input component
<Input data-testid='input' placeholder={placeholderText}/>
More text here
</div>
);
const element = getByPlaceholderText(placeholderText);
const newText = 'new text';
fireEvent.change(element, { target: { value: newText }});
expect(element).toHaveValue(newText);
});
Top comments (5)
Nice Article!
I have like many imports from 'semantic-ui-react'.
Do I have to write functions for all of them?
Is there an easier way to do this?
Okay, I did that the following way:
Hey Jacob, nice explanation. I'm struggling to do the same with semantic ui dropdown, but can't find anything. Do you perhaps have anything on that one? Thanks.
Sure. I didn't take screenshots, but I did a writeup of it on my github pages blog here:
jacobwicks.github.io/2020/05/16/te...
Hope that helps.
It worked, thanks a lot.