# Async UI, Forms and Hooks — Jest, Vitest & Testing Library

Source: https://www.skillbyai.com/en/javascript-testing/c-async

> findBy, waitFor and renderHook.

## Wait for the UI, not for time

Components that fetch data or validate asynchronously update after the test's current line. Use **`findBy...`** to wait for something to appear, **`waitForElementToBeRemoved`** to wait for a spinner to disappear, and **`waitFor(() => expect(...))`** for other assertions that become true later; `waitFor` retries the callback until it stops throwing or times out. Keep a single assertion inside `waitFor`, and do not put side effects such as clicks inside it, because it may run many times. Never use arbitrary `sleep`s. For **custom hooks**, `renderHook` (exported by `@testing-library/react` in current versions) renders the hook in a tiny test component and exposes `result.current`; wrap state updates in `act`. Often, though, testing the hook through a component that uses it is more realistic.

## Loading state, data and a hook

API calls are handled by MSW handlers from the setup file.

```tsx
import { act, render, renderHook, screen, waitForElementToBeRemoved } from '@testing-library/react';
import { expect, it } from 'vitest';
import { http, HttpResponse } from 'msw';
import { server } from './test/server';
import { OrderList } from './OrderList';
import { useCounter } from './useCounter';

it('shows orders after loading', async () => {
  render(<OrderList />);
  await waitForElementToBeRemoved(() => screen.queryByText(/loading/i));
  expect(screen.getAllByRole('listitem')).toHaveLength(2);
});

it('shows an error message when the API fails', async () => {
  server.use(http.get('/api/orders', () => new HttpResponse(null, { status: 500 })));
  render(<OrderList />);
  expect(await screen.findByRole('alert')).toHaveTextContent(/could not load orders/i);
});

it('increments the counter hook', () => {
  const { result } = renderHook(() => useCounter(5));
  act(() => result.current.increment());
  expect(result.current.count).toBe(6);
});
```

## Heed the act() warning

A warning that an update was "not wrapped in act(...)" usually means the component updated after your test stopped watching. Await the visible result (`findBy`, `waitFor`) instead of silencing the warning.

**Quiz:** What should go inside a waitFor callback?

- [x] An assertion that will eventually pass, with no side effects such as clicks
- [ ] A user.click call so it retries the click
- [ ] A fixed sleep of 2 seconds
- [ ] The render call

*Answer:* An assertion that will eventually pass, with no side effects such as clicks. waitFor may run the callback many times.
