Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to wait for a component to render? React Testing Library/Jest

I have a component. It has a button. Upon pressing the button, I am changing the style of the button text (color) using setState function. When I am testing the changed component, the test is failing because the change happens asynchronously. I want to do something as is given here (https://testing-library.com/docs/dom-testing-library/api-async/)

const button = screen.getByRole('button', { name: 'Click Me' })
fireEvent.click(button)
await screen.findByText('Clicked once')
fireEvent.click(button)
await screen.findByText('Clicked twice')

But rather than waiting for the text to change. I want to wait for the text color to change. Thanks

This is the code for my button

<Button onPress = {() => {this.setState({state : 1});}}>
<Text style = {style}>Button Text</Text>
</Button>

So when this button is pressed. state is set to 1. And in render :

if(this.state.state === 1) style = style1
else style = style2;

But it can be seen from logs that render is called after the test checks for the styles. So How can I wait for the render to complete before checking if the font color has been changed?

Here is the testing code

test('The button text style changes after press', () => {
  const {getByText} = render(<Component/>);
  fireEvent.press(getByText('button'));
  expect(getByText('button')).toHaveStyle({
    color : '#ffffff'
  });
})
like image 489
Prateek Mishra Avatar asked Sep 12 '25 20:09

Prateek Mishra


1 Answers

It looks like you have a custom button, not a native button. I'm guessing your component is something like this:

import React from "react";
import {Text, TouchableOpacity} from "react-native";

const Button = ({pressHandler, children}) => (
  <TouchableOpacity onPress={pressHandler}>
    {children}
  </TouchableOpacity>
);

const ColorChangingButton = ({text}) => {
  const [color, setColor] = React.useState("red");
  const toggleColor = () => setTimeout(() => 
    setColor(color === "green" ? "red" : "green"), 1000
  );
  return (
    <Button pressHandler={toggleColor}>
      <Text style={{color}}>{text}</Text>
    </Button>
  );
};
export default ColorChangingButton;

If so, you can test it with waitFor as described here:

import React from "react";
import {
  fireEvent, 
  render,
  waitFor,
} from "@testing-library/react-native";
import ColorChangingButton from "../src/components/ColorChangingButton";

it("should change the button's text color", async () => {
  const text = "foobar";
  const {getByText} = render(<ColorChangingButton text={text} />);
  fireEvent.press(getByText(text));
  await waitFor(() => {
    expect(getByText(text)).toHaveStyle({color: "green"});
  });
});

For a native button which has rigid semantics for changing colors and doesn't accept children, instead using title="foo", a call to debug() shows that it expands to a few nested elements. You can use

const text = within(getByRole("button")).getByText(/./);
expect(text).toHaveStyle({color: "green"});

inside the waitFor callback to dip into the button's text child and wait for it to have the desired color.

I used the same packages/versions for this post as shown in React Testing Library: Test if Elements have been mapped/rendered.

like image 184
ggorlen Avatar answered Sep 16 '25 08:09

ggorlen