RTL await waitFor vs await findBy*

Viewed 275

I'm not really understand how to use async method in RTL. In the docs they said findBy method is combination of getBy and waitFor. Is there any specific case when to use one of it?

My case is:

  1. Show input elements after success fetching data
  2. Fire event change of each input
  3. Fire event click on submit button

With this approach, my test always failed and return Received length: 0

render(<Component {...mockProps} />);
const inputs = await screen.findAllByRole('textbox');
expect(inputs).toHaveLength(5); //failed

But with this approach, my test get passed

render(<Component {...mockProps} />);
await waitFor(() => {
   const inputs = screen.getAllByRole('textbox');
   expect(inputs).toHaveLength(5); //passed
});

How to get passed test with the first approach?

Because i want to call async method again after the inputs shows, should i do this?

await waitFor(async () => {
   const inputs = screen.getAllByRole('textbox');
   expect(inputs).toHaveLength(5);

   const [input1, input2, ...etc] = inputs;
   fireEvent.change(input1, {target: {value: 'first'}};
   ...etc

  await waitFor(() => {
     expect(onSubmit).toHaveBeenCalled();
  })
});
1 Answers

You can have separate waitFor()s like this:

await waitFor(async () => {
   const inputs = screen.getAllByRole('textbox');
   expect(inputs).toHaveLength(5);

   const [input1, input2, ...etc] = inputs;
   fireEvent.change(input1, {target: {value: 'first'}};
   ...etc


});
await waitFor(() => {
  expect(onSubmit).toHaveBeenCalled();
})

If you want to stick with the first approach, you can use a findBy along with and getBy so you wait for the element to exist before "getting" it:

render(<Component {...mockProps} />);
await screen.findAllByRole('textbox');
const inputs = screen.getAllByRole('textbox');
expect(inputs).toHaveLength(5);

const [input1, input2, ...etc] = inputs;
fireEvent.change(input1, {target: {value: 'first'}};
...etc

await waitFor(() => {
   expect(onSubmit).toHaveBeenCalled();
})
Related