or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

async-testing.mdasync.mdcomponent-rendering.mdconfiguration.mdelement-queries.mdevent-simulation.mdevents.mdhook-testing.mdhooks.mdindex.mdqueries.mdquick-reference.mdrendering.md

queries.mddocs/

0

# Querying Elements

1

2

Find elements using accessible patterns. Inherited from @testing-library/dom.

3

4

## Query Types

5

6

Three variants for each query method:

7

8

- **getBy\***: Throws if not found → Use when element should exist

9

- **queryBy\***: Returns null if not found → Use for negative assertions

10

- **findBy\***: Returns Promise → Use for async elements

11

12

Plural forms (**getAll\***, **queryAll\***, **findAll\***) return arrays.

13

14

## Query Priority (Best to Worst)

15

16

1. **getByRole** - Most accessible, encourages proper ARIA

17

2. **getByLabelText** - For form inputs

18

3. **getByPlaceholderText** - If no label exists

19

4. **getByText** - For non-interactive content

20

5. **getByDisplayValue** - For filled inputs

21

6. **getByAltText** - For images

22

7. **getByTitle** - When other options unavailable

23

8. **getByTestId** - Last resort, not user-visible

24

25

## Common Queries

26

27

### getByRole (Primary)

28

29

**Primary query method.** Finds elements by their ARIA role. Encourages accessible markup and tests behavior users can actually see.

30

31

```typescript { .api }

32

/**

33

* Find element by ARIA role

34

* @param role - ARIA role (button, heading, textbox, etc.)

35

* @param options - Additional constraints

36

* @returns Matching HTMLElement

37

* @throws When no element or multiple elements found

38

*/

39

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

40

41

/**

42

* Find all elements by ARIA role

43

* @returns Array of matching HTMLElements

44

* @throws When no elements found

45

*/

46

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

47

48

interface ByRoleOptions {

49

/**

50

* Filter by accessible name (label, aria-label, text content)

51

*/

52

name?: string | RegExp;

53

54

/**

55

* Filter by accessible description (aria-describedby, title)

56

*/

57

description?: string | RegExp;

58

59

/**

60

* Include hidden elements (default: false)

61

*/

62

hidden?: boolean;

63

64

/**

65

* Filter by selected state (for options, tabs, etc.)

66

*/

67

selected?: boolean;

68

69

/**

70

* Filter by checked state (for checkboxes, radio buttons)

71

*/

72

checked?: boolean;

73

74

/**

75

* Filter by pressed state (for toggle buttons)

76

*/

77

pressed?: boolean;

78

79

/**

80

* Filter by current state (for navigation items)

81

*/

82

current?: boolean | string;

83

84

/**

85

* Filter by expanded state (for accordions, dropdowns)

86

*/

87

expanded?: boolean;

88

89

/**

90

* Filter by heading level (for heading role)

91

*/

92

level?: number;

93

94

/**

95

* Enable exact matching (default: true)

96

*/

97

exact?: boolean;

98

99

/**

100

* Custom text normalizer function

101

*/

102

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

103

104

/**

105

* Enable query fallbacks

106

*/

107

queryFallbacks?: boolean;

108

}

109

```

110

111

**Common Roles:**

112

```typescript

113

screen.getByRole('button'); // <button>, role="button"

114

screen.getByRole('link'); // <a href>

115

screen.getByRole('heading', { level: 1 }); // <h1>

116

screen.getByRole('textbox'); // <input type="text">, <textarea>

117

screen.getByRole('checkbox'); // <input type="checkbox">

118

screen.getByRole('radio'); // <input type="radio">

119

screen.getByRole('combobox'); // <select>

120

screen.getByRole('img'); // <img>

121

screen.getByRole('dialog'); // Modal/dialog

122

screen.getByRole('alert'); // Alert message

123

screen.getByRole('navigation'); // <nav>

124

screen.getByRole('list'); // <ul>, <ol>

125

screen.getByRole('listitem'); // <li>

126

```

127

128

**Examples:**

129

```typescript

130

// By role and name

131

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

132

133

// By state

134

screen.getByRole('checkbox', { checked: true });

135

screen.getByRole('button', { pressed: true });

136

137

// Heading level

138

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

139

```

140

141

### getByLabelText

142

143

Finds form elements by their associated label text. Best for form inputs.

144

145

```typescript { .api }

146

/**

147

* Find form element by label text

148

* @param text - Label text (string or regex)

149

* @param options - Matching options

150

* @returns Matching HTMLElement

151

*/

152

getByLabelText(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement;

153

getAllByLabelText(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement[];

154

155

interface SelectorMatcherOptions {

156

/**

157

* Custom CSS selector to filter results

158

*/

159

selector?: string;

160

161

/**

162

* Enable exact matching (default: true)

163

*/

164

exact?: boolean;

165

166

/**

167

* Custom text normalizer function

168

*/

169

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

170

}

171

```

172

173

**Examples:**

174

```typescript

175

// <label htmlFor="email">Email</label><input id="email" />

176

screen.getByLabelText('Email');

177

178

// <input aria-label="Search" />

179

screen.getByLabelText('Search');

180

181

// <label><input /> Remember me</label>

182

screen.getByLabelText('Remember me');

183

```

184

185

### getByText

186

187

Finds elements by their text content. Good for non-interactive text elements.

188

189

```typescript { .api }

190

/**

191

* Find element by text content

192

* @param text - Text content (string or regex)

193

* @param options - Matching options

194

* @returns Matching HTMLElement

195

*/

196

getByText(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement;

197

getAllByText(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement[];

198

```

199

200

**Examples:**

201

```typescript

202

screen.getByText('Hello World');

203

screen.getByText(/hello/i); // Case insensitive

204

screen.getByText('Hello', { exact: false }); // Partial match

205

screen.getByText('Submit', { selector: 'button' }); // Filter by tag

206

```

207

208

### getByPlaceholderText

209

210

Finds elements by placeholder text. Use as a last resort as placeholders are not accessible replacements for labels.

211

212

```typescript { .api }

213

/**

214

* Find element by placeholder text

215

* @param text - Placeholder text (string or regex)

216

* @param options - Matching options

217

* @returns Matching HTMLElement

218

*/

219

getByPlaceholderText(text: string | RegExp, options?: MatcherOptions): HTMLElement;

220

getAllByPlaceholderText(text: string | RegExp, options?: MatcherOptions): HTMLElement[];

221

222

interface MatcherOptions {

223

/**

224

* Enable exact matching (default: true)

225

*/

226

exact?: boolean;

227

228

/**

229

* Custom text normalizer function

230

*/

231

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

232

}

233

```

234

235

### getByDisplayValue

236

237

Finds form elements by their current value. Useful for testing filled forms.

238

239

```typescript { .api }

240

/**

241

* Find form element by current value

242

* @param value - Current display value (string or regex)

243

* @param options - Matching options

244

* @returns Matching HTMLElement

245

*/

246

getByDisplayValue(value: string | RegExp, options?: MatcherOptions): HTMLElement;

247

getAllByDisplayValue(value: string | RegExp, options?: MatcherOptions): HTMLElement[];

248

```

249

250

### getByAltText

251

252

Finds images and other elements by their alt text.

253

254

```typescript { .api }

255

/**

256

* Find element by alt text

257

* @param text - Alt text (string or regex)

258

* @param options - Matching options

259

* @returns Matching HTMLElement

260

*/

261

getByAltText(text: string | RegExp, options?: MatcherOptions): HTMLElement;

262

getAllByAltText(text: string | RegExp, options?: MatcherOptions): HTMLElement[];

263

```

264

265

### getByTitle

266

267

Finds elements by their title attribute.

268

269

```typescript { .api }

270

/**

271

* Find element by title attribute

272

* @param title - Title text (string or regex)

273

* @param options - Matching options

274

* @returns Matching HTMLElement

275

*/

276

getByTitle(title: string | RegExp, options?: MatcherOptions): HTMLElement;

277

getAllByTitle(title: string | RegExp, options?: MatcherOptions): HTMLElement[];

278

```

279

280

### getByTestId

281

282

Finds elements by data-testid attribute. Use as a last resort when other queries are not applicable.

283

284

```typescript { .api }

285

/**

286

* Find element by test ID

287

* @param testId - Test ID value (string or regex)

288

* @param options - Matching options

289

* @returns Matching HTMLElement

290

*/

291

getByTestId(testId: string | RegExp, options?: MatcherOptions): HTMLElement;

292

getAllByTestId(testId: string | RegExp, options?: MatcherOptions): HTMLElement[];

293

```

294

295

## Query Variants

296

297

### queryBy* Queries

298

299

Non-throwing variants that return `null` when element is not found. Use for asserting elements don't exist.

300

301

```typescript { .api }

302

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

303

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

304

305

queryByLabelText(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement | null;

306

queryAllByLabelText(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement[];

307

308

queryByPlaceholderText(text: string | RegExp, options?: MatcherOptions): HTMLElement | null;

309

queryAllByPlaceholderText(text: string | RegExp, options?: MatcherOptions): HTMLElement[];

310

311

queryByText(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement | null;

312

queryAllByText(text: string | RegExp, options?: SelectorMatcherOptions): HTMLElement[];

313

314

queryByDisplayValue(value: string | RegExp, options?: MatcherOptions): HTMLElement | null;

315

queryAllByDisplayValue(value: string | RegExp, options?: MatcherOptions): HTMLElement[];

316

317

queryByAltText(text: string | RegExp, options?: MatcherOptions): HTMLElement | null;

318

queryAllByAltText(text: string | RegExp, options?: MatcherOptions): HTMLElement[];

319

320

queryByTitle(title: string | RegExp, options?: MatcherOptions): HTMLElement | null;

321

queryAllByTitle(title: string | RegExp, options?: MatcherOptions): HTMLElement[];

322

323

queryByTestId(testId: string | RegExp, options?: MatcherOptions): HTMLElement | null;

324

queryAllByTestId(testId: string | RegExp, options?: MatcherOptions): HTMLElement[];

325

```

326

327

### findBy* Queries

328

329

Async queries that return a promise resolving when element is found. Use for elements that appear asynchronously.

330

331

```typescript { .api }

332

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

333

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

334

335

findByLabelText(text: string | RegExp, options?: SelectorMatcherOptions): Promise<HTMLElement>;

336

findAllByLabelText(text: string | RegExp, options?: SelectorMatcherOptions): Promise<HTMLElement[]>;

337

338

findByPlaceholderText(text: string | RegExp, options?: MatcherOptions): Promise<HTMLElement>;

339

findAllByPlaceholderText(text: string | RegExp, options?: MatcherOptions): Promise<HTMLElement[]>;

340

341

findByText(text: string | RegExp, options?: SelectorMatcherOptions): Promise<HTMLElement>;

342

findAllByText(text: string | RegExp, options?: SelectorMatcherOptions): Promise<HTMLElement[]>;

343

344

findByDisplayValue(value: string | RegExp, options?: MatcherOptions): Promise<HTMLElement>;

345

findAllByDisplayValue(value: string | RegExp, options?: MatcherOptions): Promise<HTMLElement[]>;

346

347

findByAltText(text: string | RegExp, options?: MatcherOptions): Promise<HTMLElement>;

348

findAllByAltText(text: string | RegExp, options?: MatcherOptions): Promise<HTMLElement[]>;

349

350

findByTitle(title: string | RegExp, options?: MatcherOptions): Promise<HTMLElement>;

351

findAllByTitle(title: string | RegExp, options?: MatcherOptions): Promise<HTMLElement[]>;

352

353

findByTestId(testId: string | RegExp, options?: MatcherOptions): Promise<HTMLElement>;

354

findAllByTestId(testId: string | RegExp, options?: MatcherOptions): Promise<HTMLElement[]>;

355

```

356

357

## Utility Functions

358

359

### screen Object

360

361

Pre-bound queries to `document.body` for convenient access.

362

363

```typescript { .api }

364

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

365

366

const screen: {

367

// All query functions

368

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

369

// ... all other queries

370

371

// Debug utility

372

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

373

374

// Log roles utility

375

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

376

};

377

```

378

379

### within Function

380

381

Get queries bound to a specific element for scoped searches.

382

383

```typescript { .api }

384

/**

385

* Get queries scoped to a specific element

386

* @param element - Element to scope queries to

387

* @returns Object with all query functions bound to element

388

*/

389

function within(element: HTMLElement): {

390

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

391

// ... all other query functions

392

};

393

```

394

395

## Query Patterns

396

397

### Synchronous Queries

398

```typescript

399

// Element exists

400

const button = screen.getByRole('button');

401

402

// Element may not exist

403

const dialog = screen.queryByRole('dialog');

404

if (dialog) {

405

// Handle dialog

406

}

407

408

// Negative assertion

409

expect(screen.queryByText('Error')).not.toBeInTheDocument();

410

411

// Multiple elements

412

const items = screen.getAllByRole('listitem');

413

expect(items).toHaveLength(5);

414

```

415

416

### Async Queries

417

```typescript

418

// Wait for element to appear

419

const message = await screen.findByText('Loaded');

420

421

// Wait for multiple

422

const buttons = await screen.findAllByRole('button');

423

424

// With custom timeout (default: 1000ms)

425

const slow = await screen.findByText('Slow', {}, { timeout: 3000 });

426

```

427

428

### Scoped Queries

429

```typescript

430

// Query within specific element

431

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

432

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

433

434

// Multiple scopes

435

const nav = screen.getByRole('navigation');

436

const homeLink = within(nav).getByRole('link', { name: /home/i });

437

```

438

439

## Query Access

440

441

```typescript

442

// 1. screen object (recommended)

443

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

444

screen.getByRole('button');

445

446

// 2. render result

447

const { getByRole } = render(<Component />);

448

getByRole('button');

449

450

// 3. within for scoped queries

451

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

452

within(container).getByRole('button');

453

```

454

455

## Testing Patterns

456

457

### Form Testing

458

```typescript

459

test('form submission', () => {

460

render(<LoginForm />);

461

462

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

463

const passwordInput = screen.getByLabelText(/password/i);

464

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

465

466

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

467

fireEvent.change(passwordInput, { target: { value: 'password123' } });

468

fireEvent.click(submitButton);

469

});

470

```

471

472

### List Testing

473

```typescript

474

test('displays items', () => {

475

render(<TodoList items={['Task 1', 'Task 2']} />);

476

477

const items = screen.getAllByRole('listitem');

478

expect(items).toHaveLength(2);

479

expect(items[0]).toHaveTextContent('Task 1');

480

});

481

```

482

483

### Modal Testing

484

```typescript

485

test('modal interactions', async () => {

486

render(<App />);

487

488

// Open modal

489

fireEvent.click(screen.getByRole('button', { name: /open/i }));

490

491

// Wait for modal

492

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

493

expect(modal).toBeInTheDocument();

494

495

// Query within modal

496

const title = within(modal).getByRole('heading');

497

expect(title).toHaveTextContent('Modal Title');

498

});

499

```

500

501

### Conditional Rendering

502

```typescript

503

test('shows error on failure', async () => {

504

render(<Component />);

505

506

// Initially no error

507

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

508

509

// Trigger error

510

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

511

512

// Error appears

513

const alert = await screen.findByRole('alert');

514

expect(alert).toHaveTextContent('Error occurred');

515

});

516

```

517

518

## Debug Helpers

519

520

```typescript

521

// Print DOM

522

screen.debug();

523

screen.debug(screen.getByRole('button'));

524

525

// Print available roles

526

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

527

logRoles(container);

528

529

// Get suggested queries

530

// Error messages suggest better queries when getBy* fails

531

```

532

533

## Best Practices

534

535

1. **Prefer getByRole** - Most accessible and resilient to changes

536

2. **Use regex for flexibility** - `/submit/i` instead of "Submit Form"

537

3. **Avoid testid** - Use semantic queries unless absolutely necessary

538

4. **Use within()** - Scope queries to avoid ambiguity

539

5. **Async with findBy** - Don't use getBy in setTimeout, use findBy

540

6. **queryBy for absence** - Only use queryBy for negative assertions

541