User-centric React component testing. Trigger: When testing React components with RTL.
Tests components the way users interact with them -- querying by accessible roles and text, not implementation details.
Don't use for:
// CORRECT: use screen for all queries
render(<LoginForm />);
const button = screen.getByRole('button', { name: /submit/i });
// WRONG: destructuring queries from render
const { getByRole } = render(<LoginForm />);
import userEvent from '@testing-library/user-event';
// CORRECT: realistic user simulation
const user = userEvent.setup();
await user.type(screen.getByRole('textbox', { name: /email/i }), 'ada@test.com');
// WRONG: skips intermediate events
fireEvent.change(input, { target: { value: 'ada@test.com' } });
Prefer: getByRole > getByLabelText > getByText > getByTestId.
// CORRECT: role query with accessible name
screen.getByRole('heading', { name: /welcome/i });
// WRONG: test-id as first resort
screen.getByTestId('welcome-heading');
// CORRECT: findByRole waits for element to appear
await screen.findByRole('alert', { name: /success/i });
// WRONG: getByRole throws immediately if not in DOM
screen.getByRole('alert', { name: /success/i });
// CORRECT: assert on visible output
await user.click(screen.getByRole('button', { name: /add to cart/i }));
expect(screen.getByText(/1 item in cart/i)).toBeInTheDocument();
// WRONG: reaching into component internals
expect(wrapper.state('cartCount')).toBe(1);
Use queryBy* (never getBy*) for negative DOM assertions — getBy* throws if absent, making .not assertions unreliable.
// ✅ CORRECT: queryBy* returns null when absent
expect(screen.queryByRole('alert')).not.toBeInTheDocument();
expect(screen.queryByText(/error/i)).toBeNull();
// After dismissing a modal:
await user.click(screen.getByRole('button', { name: /close/i }));
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
// ❌ WRONG: getBy* throws before .not can evaluate
expect(screen.getByRole('alert')).not.toBeInTheDocument(); // always throws
See unit-testing skill for the broader strategy of testing both presence and absence.
Element present now?
→ getByRole / getByText
Appears after async?
→ findByRole / findByText
Should NOT exist?
→ queryByRole (returns null)
User input?
→ userEvent.setup() then user.type(), user.click()
No accessible query?
→ Add aria-label; getByTestId last resort
Custom hook?
→ renderHook(() => useMyHook())
Side effects?
→ waitFor(() => expect(...))
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { ContactForm } from './ContactForm';
describe('ContactForm', () => {
it('should submit and show success', async () => {
const onSubmit = jest.fn().mockResolvedValue({ ok: true });
const user = userEvent.setup();
render(<ContactForm onSubmit={onSubmit} />);
await user.type(screen.getByRole('textbox', { name: /name/i }), 'Ada');
await user.type(screen.getByRole('textbox', { name: /email/i }), 'ada@test.com');
await user.click(screen.getByRole('button', { name: /send/i }));
expect(onSubmit).toHaveBeenCalledWith({ name: 'Ada', email: 'ada@test.com' });
expect(await screen.findByRole('alert')).toHaveTextContent(/thank you/i);
});
});
screen queries since portals render outside parent DOMwaitFor when state updates after await or setTimeoutfindBy* handles automaticallyrenderWithProviders wrapper for context (theme, router, store)cleanup automatically with Jest; do not call manuallyuserEvent.setup() used instead of fireEventfindBy* or waitFor, never manual delaysrenderWithProviders wraps components needing contextSearch for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer