DEV Community

jacobwicks
jacobwicks

Posted on • Updated on

Testing a Semantic UI React Input with React Testing Library

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' } });
Enter fullscreen mode Exit fullscreen mode

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:

normal input

React Semantic UI Input

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');
});
Enter fullscreen mode Exit fullscreen mode

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');
});
Enter fullscreen mode Exit fullscreen mode

and you get an error message:
Error Message

  • 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');
});
Enter fullscreen mode Exit fullscreen mode

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);
});
Enter fullscreen mode Exit fullscreen mode

Top comments (5)

Collapse
 
instinct profile image
Instinct

Nice Article!

I have like many imports from 'semantic-ui-react'.

import { Modal, ModalHeader, ModalContent, ModalActions, Button } from 'semantic-ui-react';
Enter fullscreen mode Exit fullscreen mode

Do I have to write functions for all of them?
Is there an easier way to do this?

Collapse
 
instinct profile image
Instinct

Okay, I did that the following way:

jest.mock("semantic-ui-react", () => {
    const ui = jest.requireActual("semantic-ui-react");
    return { ...ui };
})
Enter fullscreen mode Exit fullscreen mode
Collapse
 
paukman profile image
ALEKSANDAR TRKULJA • Edited

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.

Collapse
 
jacobwicks profile image
jacobwicks

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.

Collapse
 
paukman profile image
ALEKSANDAR TRKULJA

It worked, thanks a lot.