or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

tessl/npm-testing-library--react

Simple and complete React DOM testing utilities that encourage good testing practices

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/@testing-library/react@16.3.x

To install, run

npx @tessl/cli install tessl/npm-testing-library--react@16.3.1

0

# React Testing Library

1

2

Testing utility for React emphasizing user-centric testing. Renders components in a test environment and provides queries for finding elements by accessible roles, labels, and text content.

3

4

## Package Information

5

6

- **Package Name**: @testing-library/react

7

- **Package Type**: npm

8

- **Language**: JavaScript/TypeScript

9

- **Installation**: `npm install --save-dev @testing-library/react @testing-library/dom`

10

11

## Peer Dependencies

12

13

- `react` (^18.0.0 || ^19.0.0)

14

- `react-dom` (^18.0.0 || ^19.0.0)

15

- `@testing-library/dom` (^10.0.0)

16

- `@types/react` (optional, ^18.0.0 || ^19.0.0)

17

- `@types/react-dom` (optional, ^18.0.0 || ^19.0.0)

18

19

## Core Imports

20

21

```typescript

22

import { render, screen, fireEvent, waitFor, within } from '@testing-library/react';

23

import { renderHook, act } from '@testing-library/react';

24

```

25

26

**Pure version** (without auto-cleanup):

27

```typescript

28

import { render, screen } from '@testing-library/react/pure';

29

```

30

31

**CommonJS:**

32

```javascript

33

const { render, screen, fireEvent } = require('@testing-library/react');

34

```

35

36

## Quick Reference

37

38

### Essential Pattern

39

```typescript

40

// Render component

41

render(<Component />);

42

43

// Find element (prefer role queries)

44

const button = screen.getByRole('button', { name: /submit/i });

45

46

// Interact

47

fireEvent.click(button);

48

49

// Assert

50

expect(screen.getByText(/success/i)).toBeInTheDocument();

51

52

// Async wait

53

await screen.findByText(/loaded/i);

54

```

55

56

### Test Hook

57

```typescript

58

const { result } = renderHook(() => useCustomHook());

59

act(() => result.current.action());

60

expect(result.current.value).toBe(expected);

61

```

62

63

## Architecture

64

65

React Testing Library is built around several key concepts:

66

67

- **Rendering Utilities**: Core `render()` and `renderHook()` functions for mounting React components and hooks in a test environment

68

- **Query System**: Inherited from @testing-library/dom, provides accessible queries (getBy*, queryBy*, findBy*) that encourage testing from a user's perspective

69

- **Event System**: Enhanced `fireEvent` with React-specific behaviors for simulating user interactions

70

- **act() Wrapper**: Automatic wrapping of renders and updates in React's act() to ensure proper state updates

71

- **Auto-cleanup**: Automatic unmounting and cleanup between tests (can be disabled via pure import)

72

- **Configuration**: Global and per-test configuration for strict mode, queries, wrappers, and error handling

73

74

## Core Capabilities

75

76

### Rendering: [rendering.md](./rendering.md)

77

78

```typescript { .api }

79

function render(ui: React.ReactNode, options?: RenderOptions): RenderResult;

80

81

interface RenderOptions {

82

container?: HTMLElement; // Custom container (e.g., for <tbody>)

83

baseElement?: HTMLElement; // Base element for queries (defaults to container)

84

wrapper?: React.ComponentType<{ children: React.ReactNode }>; // Provider wrapper

85

hydrate?: boolean; // SSR hydration mode

86

legacyRoot?: boolean; // React 18 only, not supported in React 19+

87

queries?: Queries; // Custom query set

88

reactStrictMode?: boolean; // Enable StrictMode

89

onCaughtError?: (error: Error, errorInfo: { componentStack?: string }) => void; // React 19+ only

90

onRecoverableError?: (error: Error, errorInfo: { componentStack?: string }) => void; // React 18+

91

}

92

93

interface RenderResult {

94

container: HTMLElement; // DOM container element

95

baseElement: HTMLElement; // Base element for queries

96

rerender: (ui: React.ReactNode) => void; // Re-render with new UI

97

unmount: () => void; // Unmount and cleanup

98

debug: (element?, maxLength?, options?) => void; // Pretty-print DOM

99

asFragment: () => DocumentFragment; // Snapshot testing helper

100

// All query functions bound to baseElement:

101

getByRole: (role: string, options?: ByRoleOptions) => HTMLElement;

102

getAllByRole: (role: string, options?: ByRoleOptions) => HTMLElement[];

103

queryByRole: (role: string, options?: ByRoleOptions) => HTMLElement | null;

104

queryAllByRole: (role: string, options?: ByRoleOptions) => HTMLElement[];

105

findByRole: (role: string, options?: ByRoleOptions) => Promise<HTMLElement>;

106

findAllByRole: (role: string, options?: ByRoleOptions) => Promise<HTMLElement[]>;

107

// Plus: getBy/queryBy/findBy LabelText, PlaceholderText, Text, DisplayValue, AltText, Title, TestId

108

}

109

```

110

111

**Key Patterns:**

112

```typescript

113

// Basic render

114

const { container, rerender } = render(<App />);

115

116

// With providers

117

render(<App />, {

118

wrapper: ({ children }) => <Provider>{children}</Provider>

119

});

120

121

// Re-render with props

122

rerender(<App count={2} />);

123

```

124

125

### Querying: [queries.md](./queries.md)

126

127

**Query Priority:** Role > Label > Placeholder > Text > DisplayValue > AltText > Title > TestId

128

129

```typescript { .api }

130

// Synchronous queries

131

getByRole(role: string, options?: ByRoleOptions): HTMLElement;

132

getAllByRole(role: string, options?: ByRoleOptions): HTMLElement[];

133

134

queryByRole(role: string, options?: ByRoleOptions): HTMLElement | null;

135

queryAllByRole(role: string, options?: ByRoleOptions): HTMLElement[];

136

137

// Async queries (wait for element to appear)

138

findByRole(role: string, options?: ByRoleOptions): Promise<HTMLElement>;

139

findAllByRole(role: string, options?: ByRoleOptions): Promise<HTMLElement[]>;

140

141

// Available query types: Role, LabelText, PlaceholderText, Text, DisplayValue, AltText, Title, TestId

142

```

143

144

**Common Patterns:**

145

```typescript

146

// Prefer role queries (most accessible)

147

screen.getByRole('button', { name: /submit/i });

148

screen.getByRole('heading', { level: 1 });

149

150

// Form inputs

151

screen.getByLabelText('Email');

152

screen.getByPlaceholderText('Enter email');

153

154

// Text content

155

screen.getByText(/hello world/i);

156

157

// Negative assertions

158

expect(screen.queryByRole('dialog')).not.toBeInTheDocument();

159

160

// Scoped queries

161

const modal = screen.getByRole('dialog');

162

within(modal).getByRole('button', { name: /close/i });

163

```

164

165

### Events: [events.md](./events.md)

166

167

```typescript { .api }

168

// Common events (automatically wrapped in act())

169

fireEvent.click(element, options?: MouseEventInit);

170

fireEvent.change(element, { target: { value: 'text' } });

171

fireEvent.submit(form);

172

fireEvent.focus(element);

173

fireEvent.blur(element);

174

fireEvent.keyDown(element, { key: 'Enter', code: 'Enter' });

175

```

176

177

**Patterns:**

178

```typescript

179

// Click interaction

180

fireEvent.click(screen.getByRole('button'));

181

182

// Input change

183

fireEvent.change(screen.getByLabelText('Email'), {

184

target: { value: 'user@example.com' }

185

});

186

187

// Keyboard

188

fireEvent.keyDown(input, { key: 'Enter', code: 'Enter' });

189

190

// Hover

191

fireEvent.mouseEnter(element);

192

fireEvent.mouseLeave(element);

193

```

194

195

### Async Operations: [async.md](./async.md)

196

197

```typescript { .api }

198

// Wait for condition

199

function waitFor<T>(callback: () => T | Promise<T>, options?: {

200

timeout?: number; // default: 1000ms

201

interval?: number; // default: 50ms

202

}): Promise<T>;

203

204

// Wait for removal

205

function waitForElementToBeRemoved<T>(

206

element: T | (() => T),

207

options?: { timeout?: number }

208

): Promise<void>;

209

210

// Async queries (shorthand for waitFor + getBy)

211

findByRole(role: string, options?: ByRoleOptions): Promise<HTMLElement>;

212

```

213

214

**Patterns:**

215

```typescript

216

// Wait for element to appear

217

const element = await screen.findByText(/loaded/i);

218

219

// Wait for condition

220

await waitFor(() => {

221

expect(screen.getByText(/success/i)).toBeInTheDocument();

222

}, { timeout: 3000 });

223

224

// Wait for disappearance

225

await waitForElementToBeRemoved(() => screen.getByText(/loading/i));

226

```

227

228

### Hook Testing: [hooks.md](./hooks.md)

229

230

```typescript { .api }

231

function renderHook<Result, Props>(

232

callback: (props: Props) => Result,

233

options?: RenderHookOptions<Props>

234

): RenderHookResult<Result, Props>;

235

236

interface RenderHookResult<Result, Props> {

237

result: { current: Result };

238

rerender: (props?: Props) => void;

239

unmount: () => void;

240

}

241

```

242

243

**Patterns:**

244

```typescript

245

// Test hook

246

const { result } = renderHook(() => useCounter(0));

247

248

// Trigger updates (wrap in act)

249

act(() => result.current.increment());

250

expect(result.current.count).toBe(1);

251

252

// With context

253

const { result } = renderHook(() => useUser(), {

254

wrapper: ({ children }) => <UserProvider>{children}</UserProvider>

255

});

256

257

// Re-render with props

258

const { result, rerender } = renderHook(

259

({ id }) => useFetch(id),

260

{ initialProps: { id: 1 } }

261

);

262

rerender({ id: 2 });

263

```

264

265

### Configuration: [configuration.md](./configuration.md)

266

267

```typescript { .api }

268

function configure(config: Partial<Config>): void;

269

270

interface Config {

271

reactStrictMode: boolean; // default: false

272

testIdAttribute: string; // default: 'data-testid'

273

asyncUtilTimeout: number; // default: 1000ms

274

}

275

```

276

277

**Setup Pattern:**

278

```typescript

279

// test-setup.js

280

import { configure } from '@testing-library/react';

281

282

configure({

283

reactStrictMode: true,

284

asyncUtilTimeout: 2000,

285

});

286

```

287

288

## Common Type Definitions

289

290

```typescript { .api }

291

interface ByRoleOptions {

292

name?: string | RegExp; // Accessible name filter

293

description?: string | RegExp; // Accessible description filter

294

hidden?: boolean; // Include hidden elements (default: false)

295

selected?: boolean; // Filter by selected state

296

checked?: boolean; // Filter by checked state

297

pressed?: boolean; // Filter by pressed state (toggle buttons)

298

current?: boolean | string; // Filter by current state (navigation)

299

expanded?: boolean; // Filter by expanded state

300

level?: number; // Filter by heading level (1-6)

301

exact?: boolean; // Enable exact matching (default: true)

302

normalizer?: (text: string) => string; // Custom text normalizer

303

queryFallbacks?: boolean; // Enable query fallbacks

304

}

305

306

interface MatcherOptions {

307

exact?: boolean; // Enable exact matching (default: true)

308

normalizer?: (text: string) => string; // Custom text normalizer

309

}

310

311

interface SelectorMatcherOptions extends MatcherOptions {

312

selector?: string; // CSS selector to filter results

313

}

314

315

interface Queries {

316

[key: string]: (...args: any[]) => any;

317

}

318

```

319

320

## Production Patterns

321

322

### Custom Render with Providers

323

```typescript

324

import { render, RenderOptions } from '@testing-library/react';

325

import { ThemeProvider } from './theme';

326

import { QueryClientProvider } from '@tanstack/react-query';

327

328

export function renderWithProviders(

329

ui: React.ReactElement,

330

options?: Omit<RenderOptions, 'wrapper'>

331

) {

332

return render(ui, {

333

wrapper: ({ children }) => (

334

<QueryClientProvider client={queryClient}>

335

<ThemeProvider>{children}</ThemeProvider>

336

</QueryClientProvider>

337

),

338

...options,

339

});

340

}

341

```

342

343

### Testing Async Data Fetching

344

```typescript

345

test('loads and displays data', async () => {

346

render(<DataComponent />);

347

348

expect(screen.getByText(/loading/i)).toBeInTheDocument();

349

350

const data = await screen.findByText(/data loaded/i);

351

expect(data).toBeInTheDocument();

352

353

expect(screen.queryByText(/loading/i)).not.toBeInTheDocument();

354

});

355

```

356

357

### Testing Form Submission

358

```typescript

359

test('submits form with validation', async () => {

360

const handleSubmit = jest.fn();

361

render(<Form onSubmit={handleSubmit} />);

362

363

const emailInput = screen.getByLabelText(/email/i);

364

const submitButton = screen.getByRole('button', { name: /submit/i });

365

366

fireEvent.change(emailInput, { target: { value: 'user@example.com' } });

367

fireEvent.click(submitButton);

368

369

await waitFor(() => {

370

expect(handleSubmit).toHaveBeenCalledWith({

371

email: 'user@example.com'

372

});

373

});

374

});

375

```

376

377

### Testing Modal Interactions

378

```typescript

379

test('opens and closes modal', async () => {

380

render(<App />);

381

382

const openButton = screen.getByRole('button', { name: /open modal/i });

383

fireEvent.click(openButton);

384

385

const modal = await screen.findByRole('dialog');

386

expect(modal).toBeInTheDocument();

387

388

const closeButton = within(modal).getByRole('button', { name: /close/i });

389

fireEvent.click(closeButton);

390

391

await waitForElementToBeRemoved(modal);

392

});

393

```

394

395

## Best Practices

396

397

1. **Query Priority**: Use getByRole when possible for accessibility

398

2. **Async Queries**: Use findBy* for async elements, waitFor for complex conditions

399

3. **Negative Assertions**: Use queryBy* with .not.toBeInTheDocument()

400

4. **act() Wrapping**: render/fireEvent/waitFor handle this automatically

401

5. **screen vs destructuring**: Prefer screen for better error messages

402

6. **Scoped Queries**: Use within() to limit query scope

403

7. **Real User Behavior**: Test what users see and do, not implementation

404

405

## Entry Points

406

407

### Main Entry (`@testing-library/react`)

408

409

Default import with auto-cleanup and automatic act() environment setup. Use for standard test setups with Jest, Vitest, or similar test runners.

410

411

```typescript

412

import { render, screen } from '@testing-library/react';

413

// Auto-cleanup runs after each test

414

```

415

416

### Pure Entry (`@testing-library/react/pure`)

417

418

No auto-cleanup or automatic setup. Use when you need manual control over cleanup or test lifecycle.

419

420

```typescript

421

import { render, screen, cleanup } from '@testing-library/react/pure';

422

423

afterEach(() => {

424

cleanup(); // Manual cleanup

425

});

426

```

427

428

### Dont-Cleanup-After-Each Entry

429

430

Alternative method to disable auto-cleanup by importing this file before the main import. Sets `RTL_SKIP_AUTO_CLEANUP=true` environment variable.

431

432

```typescript

433

import '@testing-library/react/dont-cleanup-after-each';

434

import { render, screen, cleanup } from '@testing-library/react';

435

436

// Auto-cleanup is now disabled, manual cleanup required

437

afterEach(() => {

438

cleanup();

439

});

440

```

441

442

## Common Utility Functions

443

444

### screen Object

445

446

Pre-bound queries to document.body for convenient access without destructuring render results.

447

448

```typescript { .api }

449

const screen: {

450

// All query functions

451

getByRole: (role: string, options?: ByRoleOptions) => HTMLElement;

452

// ... all other query functions

453

454

// Debug utilities

455

debug: (element?: HTMLElement, maxLength?: number) => void;

456

logTestingPlaygroundURL: (element?: HTMLElement) => void;

457

};

458

```

459

460

### within Function

461

462

Get queries bound to a specific element for scoped searches.

463

464

```typescript { .api }

465

function within(element: HTMLElement): {

466

getByRole: (role: string, options?: ByRoleOptions) => HTMLElement;

467

// ... all other query functions

468

};

469

```

470

471

### Debug Utilities

472

473

```typescript { .api }

474

function prettyDOM(

475

element?: HTMLElement,

476

maxLength?: number,

477

options?: any

478

): string;

479

480

function logRoles(container: HTMLElement): void;

481

```

482

483

### Lifecycle Management

484

485

```typescript { .api }

486

// Manual cleanup (auto-runs by default)

487

function cleanup(): void;

488

489

// Manual act() wrapping (usually automatic)

490

function act(callback: () => void | Promise<void>): Promise<void>;

491

```

492

493

**Note:** Most operations are automatically wrapped in act(), so manual use is rarely needed.

494