or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

action-chains.mdbrowser-configuration.mdelement-interaction.mdindex.mdwaits-conditions.mdwebdriver-classes.md

waits-conditions.mddocs/

0

# WebDriverWait and Expected Conditions

1

2

This document covers WebDriverWait and Expected Conditions in Python Selenium WebDriver, which provide mechanisms for handling dynamic web content by waiting for specific conditions to be met before proceeding with test execution.

3

4

## WebDriverWait Class

5

6

{ .api }

7

```python

8

from selenium.webdriver.support.ui import WebDriverWait

9

10

class WebDriverWait(Generic[D]):

11

def __init__(

12

self,

13

driver: Union[WebDriver, WebElement],

14

timeout: float,

15

poll_frequency: float = 0.5,

16

ignored_exceptions: Optional[WaitExcTypes] = None,

17

)

18

```

19

20

**Description**: WebDriverWait implements explicit waits that pause execution until a certain condition is met or a timeout occurs.

21

22

**Parameters**:

23

- `driver`: Instance of WebDriver (Chrome, Firefox, etc.) or a WebElement

24

- `timeout`: Number of seconds before timing out

25

- `poll_frequency`: Sleep interval between calls (default: 0.5 seconds)

26

- `ignored_exceptions`: Iterable of exception classes to ignore during calls (default: NoSuchElementException)

27

28

**Default Values**:

29

- `POLL_FREQUENCY`: 0.5 seconds

30

- `IGNORED_EXCEPTIONS`: (NoSuchElementException,)

31

32

**Example**:

33

```python

34

from selenium.webdriver.support.ui import WebDriverWait

35

from selenium.webdriver.support import expected_conditions as EC

36

from selenium.webdriver.common.by import By

37

38

# Basic wait setup

39

wait = WebDriverWait(driver, 10)

40

41

# Wait with custom poll frequency

42

fast_wait = WebDriverWait(driver, 5, poll_frequency=0.1)

43

44

# Wait with custom ignored exceptions

45

from selenium.common.exceptions import ElementNotVisibleException

46

custom_wait = WebDriverWait(

47

driver,

48

10,

49

ignored_exceptions=(ElementNotVisibleException, NoSuchElementException)

50

)

51

```

52

53

## WebDriverWait Methods

54

55

### until() Method

56

57

{ .api }

58

```python

59

def until(

60

self,

61

method: Callable[[D], Union[Literal[False], T]],

62

message: str = ""

63

) -> T

64

```

65

66

**Description**: Waits until the method returns a value that is not False.

67

68

**Parameters**:

69

- `method`: A callable that takes a WebDriver instance and returns a truthy value when the condition is met

70

- `message`: Optional message for TimeoutException

71

72

**Returns**: The result of the last call to `method`

73

74

**Raises**:

75

- `TimeoutException`: If 'method' does not return a truthy value within the timeout

76

77

**Example**:

78

```python

79

from selenium.webdriver.support.ui import WebDriverWait

80

from selenium.webdriver.support import expected_conditions as EC

81

82

# Wait for element to be visible

83

wait = WebDriverWait(driver, 10)

84

element = wait.until(

85

EC.visibility_of_element_located((By.ID, "myElement"))

86

)

87

88

# Wait with custom message

89

element = wait.until(

90

EC.element_to_be_clickable((By.ID, "submit-btn")),

91

"Submit button did not become clickable"

92

)

93

```

94

95

### until_not() Method

96

97

{ .api }

98

```python

99

def until_not(

100

self,

101

method: Callable[[D], T],

102

message: str = ""

103

) -> Union[T, Literal[True]]

104

```

105

106

**Description**: Waits until the method returns a value that evaluates to False.

107

108

**Parameters**:

109

- `method`: A callable that takes a WebDriver instance as an argument

110

- `message`: Optional message for TimeoutException

111

112

**Returns**: The result of the last call to `method` or True if an ignored exception occurred

113

114

**Raises**:

115

- `TimeoutException`: If 'method' does not return False within the timeout

116

117

**Example**:

118

```python

119

# Wait for element to disappear

120

wait = WebDriverWait(driver, 10)

121

is_disappeared = wait.until_not(

122

EC.visibility_of_element_located((By.ID, "loading-spinner"))

123

)

124

125

# Wait for element to become stale

126

old_element = driver.find_element(By.ID, "dynamic-content")

127

# Trigger page refresh or DOM change

128

refresh_button.click()

129

wait.until_not(EC.staleness_of(old_element))

130

```

131

132

## Expected Conditions Module

133

134

Expected Conditions provide pre-built condition functions for common waiting scenarios.

135

136

```python

137

from selenium.webdriver.support import expected_conditions as EC

138

```

139

140

## Page and URL Conditions

141

142

### Title Conditions

143

144

{ .api }

145

```python

146

def title_is(title: str) -> Callable[[WebDriver], bool]

147

```

148

149

**Description**: An expectation for checking the title of a page.

150

151

**Parameters**:

152

- `title`: The expected title (exact match)

153

154

**Returns**: True if the title matches, False otherwise

155

156

{ .api }

157

```python

158

def title_contains(title: str) -> Callable[[WebDriver], bool]

159

```

160

161

**Description**: An expectation for checking that the title contains a case-sensitive substring.

162

163

**Parameters**:

164

- `title`: The fragment of title expected

165

166

**Returns**: True when the title contains the substring, False otherwise

167

168

**Example**:

169

```python

170

# Wait for exact title

171

wait.until(EC.title_is("Welcome to My Site"))

172

173

# Wait for title to contain text

174

wait.until(EC.title_contains("Dashboard"))

175

```

176

177

### URL Conditions

178

179

{ .api }

180

```python

181

def url_contains(url: str) -> Callable[[WebDriver], bool]

182

```

183

184

**Description**: An expectation for checking that the current URL contains a case-sensitive substring.

185

186

**Parameters**:

187

- `url`: The fragment of URL expected

188

189

**Returns**: True when the URL contains the substring, False otherwise

190

191

{ .api }

192

```python

193

def url_matches(pattern: str) -> Callable[[WebDriver], bool]

194

```

195

196

**Description**: An expectation for checking that the current URL matches a regular expression pattern.

197

198

**Parameters**:

199

- `pattern`: The regex pattern to match

200

201

**Returns**: True when the URL matches the pattern, False otherwise

202

203

{ .api }

204

```python

205

def url_to_be(url: str) -> Callable[[WebDriver], bool]

206

```

207

208

**Description**: An expectation for checking the current URL.

209

210

**Parameters**:

211

- `url`: The expected URL (exact match)

212

213

**Returns**: True if the URL matches, False otherwise

214

215

{ .api }

216

```python

217

def url_changes(url: str) -> Callable[[WebDriver], bool]

218

```

219

220

**Description**: An expectation for checking that the current URL does not match the given URL.

221

222

**Parameters**:

223

- `url`: The URL to compare against

224

225

**Returns**: True if current URL is different, False otherwise

226

227

**Example**:

228

```python

229

# Wait for URL to contain specific path

230

wait.until(EC.url_contains("/dashboard"))

231

232

# Wait for URL to match pattern

233

wait.until(EC.url_matches(r"https://.*\.example\.com/user/\d+"))

234

235

# Wait for navigation to complete

236

current_url = driver.current_url

237

login_button.click()

238

wait.until(EC.url_changes(current_url))

239

```

240

241

## Element Presence and Visibility

242

243

### Element Presence

244

245

{ .api }

246

```python

247

def presence_of_element_located(

248

locator: Tuple[str, str]

249

) -> Callable[[WebDriverOrWebElement], WebElement]

250

```

251

252

**Description**: An expectation for checking that an element is present on the DOM. This does not necessarily mean that the element is visible.

253

254

**Parameters**:

255

- `locator`: Used to find the element (By strategy, value)

256

257

**Returns**: The WebElement once it is located

258

259

{ .api }

260

```python

261

def presence_of_all_elements_located(

262

locator: Tuple[str, str]

263

) -> Callable[[WebDriverOrWebElement], List[WebElement]]

264

```

265

266

**Description**: An expectation for checking that there is at least one element present on a web page.

267

268

**Parameters**:

269

- `locator`: Used to find the elements

270

271

**Returns**: List of WebElements once they are located

272

273

**Example**:

274

```python

275

# Wait for single element to be present in DOM

276

element = wait.until(

277

EC.presence_of_element_located((By.ID, "content"))

278

)

279

280

# Wait for multiple elements to be present

281

elements = wait.until(

282

EC.presence_of_all_elements_located((By.CLASS_NAME, "item"))

283

)

284

```

285

286

### Element Visibility

287

288

{ .api }

289

```python

290

def visibility_of_element_located(

291

locator: Tuple[str, str]

292

) -> Callable[[WebDriverOrWebElement], WebElement]

293

```

294

295

**Description**: An expectation for checking that an element is present on the DOM and visible. Visibility means that the element is not only displayed but also has a height and width greater than 0.

296

297

**Parameters**:

298

- `locator`: Used to find the element

299

300

**Returns**: The WebElement once it is located and visible

301

302

{ .api }

303

```python

304

def visibility_of(element: WebElement) -> Callable[[Any], Union[Literal[False], WebElement]]

305

```

306

307

**Description**: An expectation for checking that an element, known to be present on the DOM, is visible.

308

309

**Parameters**:

310

- `element`: The WebElement to check

311

312

**Returns**: The WebElement if visible, False otherwise

313

314

{ .api }

315

```python

316

def visibility_of_all_elements_located(

317

locator: Tuple[str, str]

318

) -> Callable[[WebDriverOrWebElement], List[WebElement]]

319

```

320

321

**Description**: An expectation for checking that all elements are present on the DOM and visible.

322

323

**Parameters**:

324

- `locator`: Used to find the elements

325

326

**Returns**: List of WebElements once they are located and visible

327

328

{ .api }

329

```python

330

def visibility_of_any_elements_located(

331

locator: Tuple[str, str]

332

) -> Callable[[WebDriverOrWebElement], List[WebElement]]

333

```

334

335

**Description**: An expectation for checking that there is at least one element visible on a web page.

336

337

**Parameters**:

338

- `locator`: Used to find the elements

339

340

**Returns**: List of visible WebElements

341

342

**Example**:

343

```python

344

# Wait for element to be visible

345

visible_element = wait.until(

346

EC.visibility_of_element_located((By.ID, "modal"))

347

)

348

349

# Wait for existing element to become visible

350

element = driver.find_element(By.ID, "hidden-content")

351

visible_element = wait.until(EC.visibility_of(element))

352

353

# Wait for all matching elements to be visible

354

all_visible = wait.until(

355

EC.visibility_of_all_elements_located((By.CLASS_NAME, "menu-item"))

356

)

357

358

# Wait for any matching elements to be visible

359

any_visible = wait.until(

360

EC.visibility_of_any_elements_located((By.TAG_NAME, "button"))

361

)

362

```

363

364

### Element Invisibility

365

366

{ .api }

367

```python

368

def invisibility_of_element_located(

369

locator: Tuple[str, str]

370

) -> Callable[[WebDriverOrWebElement], Union[WebElement, bool]]

371

```

372

373

**Description**: An expectation for checking that an element is either invisible or not present on the DOM.

374

375

**Parameters**:

376

- `locator`: Used to find the element

377

378

**Returns**: WebElement if invisible, True if not present

379

380

{ .api }

381

```python

382

def invisibility_of_element(

383

element: WebElement

384

) -> Callable[[Any], Union[WebElement, bool]]

385

```

386

387

**Description**: An expectation for checking that an element is either invisible or not present on the DOM.

388

389

**Parameters**:

390

- `element`: The WebElement to check

391

392

**Returns**: WebElement if invisible, True if not present

393

394

**Example**:

395

```python

396

# Wait for loading spinner to disappear

397

wait.until(

398

EC.invisibility_of_element_located((By.ID, "loading-spinner"))

399

)

400

401

# Wait for specific element to become invisible

402

modal = driver.find_element(By.ID, "error-modal")

403

close_button.click()

404

wait.until(EC.invisibility_of_element(modal))

405

```

406

407

## Element Interaction Conditions

408

409

### Clickability

410

411

{ .api }

412

```python

413

def element_to_be_clickable(

414

mark: Union[WebElement, Tuple[str, str]]

415

) -> Callable[[WebDriverOrWebElement], Union[Literal[False], WebElement]]

416

```

417

418

**Description**: An expectation for checking an element is visible and enabled such that you can click it.

419

420

**Parameters**:

421

- `mark`: WebElement or locator tuple (By strategy, value)

422

423

**Returns**: The WebElement once it is clickable, False otherwise

424

425

**Example**:

426

```python

427

# Wait for button to be clickable

428

clickable_button = wait.until(

429

EC.element_to_be_clickable((By.ID, "submit-btn"))

430

)

431

clickable_button.click()

432

433

# Wait for existing element to be clickable

434

button = driver.find_element(By.ID, "action-btn")

435

wait.until(EC.element_to_be_clickable(button))

436

```

437

438

### Element Selection State

439

440

{ .api }

441

```python

442

def element_to_be_selected(element: WebElement) -> Callable[[Any], bool]

443

```

444

445

**Description**: An expectation for checking that an element is selected.

446

447

**Parameters**:

448

- `element`: The WebElement to check

449

450

**Returns**: True if the element is selected, False otherwise

451

452

{ .api }

453

```python

454

def element_located_to_be_selected(

455

locator: Tuple[str, str]

456

) -> Callable[[WebDriverOrWebElement], bool]

457

```

458

459

**Description**: An expectation for the element to be located and selected.

460

461

**Parameters**:

462

- `locator`: Used to find the element

463

464

**Returns**: True if the element is selected, False otherwise

465

466

{ .api }

467

```python

468

def element_selection_state_to_be(

469

element: WebElement,

470

is_selected: bool

471

) -> Callable[[Any], bool]

472

```

473

474

**Description**: An expectation for checking if the given element's selection state matches the expected state.

475

476

**Parameters**:

477

- `element`: The WebElement to check

478

- `is_selected`: Expected selection state

479

480

**Returns**: True if the element's selection state matches, False otherwise

481

482

{ .api }

483

```python

484

def element_located_selection_state_to_be(

485

locator: Tuple[str, str],

486

is_selected: bool

487

) -> Callable[[WebDriverOrWebElement], bool]

488

```

489

490

**Description**: An expectation to locate an element and check if its selection state matches the expected state.

491

492

**Parameters**:

493

- `locator`: Used to find the element

494

- `is_selected`: Expected selection state

495

496

**Returns**: True if the element's selection state matches, False otherwise

497

498

**Example**:

499

```python

500

# Wait for checkbox to be selected

501

checkbox = driver.find_element(By.ID, "agree-terms")

502

wait.until(EC.element_to_be_selected(checkbox))

503

504

# Wait for element to be found and selected

505

wait.until(

506

EC.element_located_to_be_selected((By.ID, "default-option"))

507

)

508

509

# Wait for specific selection state

510

wait.until(

511

EC.element_selection_state_to_be(checkbox, True)

512

)

513

514

# Wait for located element to have specific selection state

515

wait.until(

516

EC.element_located_selection_state_to_be((By.ID, "toggle"), False)

517

)

518

```

519

520

## Text and Attribute Conditions

521

522

### Text Presence

523

524

{ .api }

525

```python

526

def text_to_be_present_in_element(

527

locator: Tuple[str, str],

528

text_: str

529

) -> Callable[[WebDriverOrWebElement], bool]

530

```

531

532

**Description**: An expectation for checking if the given text is present in the specified element.

533

534

**Parameters**:

535

- `locator`: Used to find the element

536

- `text_`: The text to check for

537

538

**Returns**: True if text is present, False otherwise

539

540

{ .api }

541

```python

542

def text_to_be_present_in_element_value(

543

locator: Tuple[str, str],

544

text_: str

545

) -> Callable[[WebDriverOrWebElement], bool]

546

```

547

548

**Description**: An expectation for checking if the given text is present in the element's value attribute.

549

550

**Parameters**:

551

- `locator`: Used to find the element

552

- `text_`: The text to check for in the value attribute

553

554

**Returns**: True if text is present in value, False otherwise

555

556

{ .api }

557

```python

558

def text_to_be_present_in_element_attribute(

559

locator: Tuple[str, str],

560

attribute_: str,

561

text_: str

562

) -> Callable[[WebDriverOrWebElement], bool]

563

```

564

565

**Description**: An expectation for checking if the given text is present in the element's specified attribute.

566

567

**Parameters**:

568

- `locator`: Used to find the element

569

- `attribute_`: The attribute to check

570

- `text_`: The text to check for in the attribute

571

572

**Returns**: True if text is present in attribute, False otherwise

573

574

**Example**:

575

```python

576

# Wait for text to appear in element

577

wait.until(

578

EC.text_to_be_present_in_element(

579

(By.ID, "status"), "Operation completed"

580

)

581

)

582

583

# Wait for text in input value

584

wait.until(

585

EC.text_to_be_present_in_element_value(

586

(By.ID, "search-box"), "selenium"

587

)

588

)

589

590

# Wait for text in custom attribute

591

wait.until(

592

EC.text_to_be_present_in_element_attribute(

593

(By.ID, "progress"), "data-status", "finished"

594

)

595

)

596

```

597

598

### Attribute Conditions

599

600

{ .api }

601

```python

602

def element_attribute_to_include(

603

locator: Tuple[str, str],

604

attribute_: str

605

) -> Callable[[WebDriverOrWebElement], bool]

606

```

607

608

**Description**: An expectation for checking if the element has a particular attribute.

609

610

**Parameters**:

611

- `locator`: Used to find the element

612

- `attribute_`: The attribute to check for

613

614

**Returns**: True if the attribute is present, False otherwise

615

616

**Example**:

617

```python

618

# Wait for element to have specific attribute

619

wait.until(

620

EC.element_attribute_to_include(

621

(By.ID, "upload-btn"), "disabled"

622

)

623

)

624

```

625

626

## Advanced Conditions

627

628

### Staleness

629

630

{ .api }

631

```python

632

def staleness_of(element: WebElement) -> Callable[[Any], bool]

633

```

634

635

**Description**: Wait until an element is no longer attached to the DOM.

636

637

**Parameters**:

638

- `element`: The element to wait for

639

640

**Returns**: False if the element is still attached, True otherwise

641

642

**Example**:

643

```python

644

# Store reference to element before DOM change

645

old_element = driver.find_element(By.ID, "dynamic-content")

646

647

# Trigger page refresh or dynamic update

648

refresh_button.click()

649

650

# Wait for old element to become stale

651

wait.until(EC.staleness_of(old_element))

652

653

# Now safe to find the new element

654

new_element = driver.find_element(By.ID, "dynamic-content")

655

```

656

657

### Frame Switching

658

659

{ .api }

660

```python

661

def frame_to_be_available_and_switch_to_it(

662

locator: Union[Tuple[str, str], str]

663

) -> Callable[[WebDriver], bool]

664

```

665

666

**Description**: An expectation for checking whether the given frame is available to switch to. If the frame is available, it switches to it.

667

668

**Parameters**:

669

- `locator`: Either a frame name/id (string) or a locator tuple

670

671

**Returns**: True if the frame was switched to, False otherwise

672

673

**Example**:

674

```python

675

# Wait for frame to be available and switch to it

676

wait.until(

677

EC.frame_to_be_available_and_switch_to_it("payment-frame")

678

)

679

680

# Or using locator

681

wait.until(

682

EC.frame_to_be_available_and_switch_to_it(

683

(By.ID, "checkout-iframe")

684

)

685

)

686

687

# Interact with frame content

688

frame_button = driver.find_element(By.ID, "pay-now")

689

frame_button.click()

690

691

# Switch back to default content

692

driver.switch_to.default_content()

693

```

694

695

### Window Management

696

697

{ .api }

698

```python

699

def number_of_windows_to_be(num_windows: int) -> Callable[[WebDriver], bool]

700

```

701

702

**Description**: An expectation for the number of windows to be a certain value.

703

704

**Parameters**:

705

- `num_windows`: The expected number of windows

706

707

**Returns**: True when the number of windows matches, False otherwise

708

709

{ .api }

710

```python

711

def new_window_is_opened(current_handles: List[str]) -> Callable[[WebDriver], bool]

712

```

713

714

**Description**: An expectation that a new window will be opened and have the number of windows handles increase.

715

716

**Parameters**:

717

- `current_handles`: List of current window handles

718

719

**Returns**: True when a new window is opened, False otherwise

720

721

**Example**:

722

```python

723

# Store current window handles

724

current_handles = driver.window_handles

725

726

# Click link that opens new window

727

external_link.click()

728

729

# Wait for new window to open

730

wait.until(EC.new_window_is_opened(current_handles))

731

732

# Switch to new window

733

new_handles = driver.window_handles

734

new_window = [h for h in new_handles if h not in current_handles][0]

735

driver.switch_to.window(new_window)

736

737

# Wait for specific number of windows

738

wait.until(EC.number_of_windows_to_be(3))

739

```

740

741

### Alert Handling

742

743

{ .api }

744

```python

745

def alert_is_present() -> Callable[[WebDriver], Union[Alert, Literal[False]]]

746

```

747

748

**Description**: An expectation for checking that an alert is present.

749

750

**Returns**: Alert object if present, False otherwise

751

752

**Example**:

753

```python

754

# Trigger alert

755

delete_button.click()

756

757

# Wait for alert and handle it

758

alert = wait.until(EC.alert_is_present())

759

alert_text = alert.text

760

alert.accept() # or alert.dismiss()

761

```

762

763

## Logical Conditions

764

765

### any_of()

766

767

{ .api }

768

```python

769

def any_of(*expected_conditions: Callable[[D], T]) -> Callable[[D], Union[Literal[False], T]]

770

```

771

772

**Description**: An expectation that any of multiple expected conditions is true. Equivalent to a logical OR.

773

774

**Parameters**:

775

- `*expected_conditions`: Variable number of expected condition functions

776

777

**Returns**: The result of the first condition that evaluates to True, False if none do

778

779

**Example**:

780

```python

781

# Wait for either success or error message

782

result = wait.until(

783

EC.any_of(

784

EC.visibility_of_element_located((By.ID, "success-message")),

785

EC.visibility_of_element_located((By.ID, "error-message"))

786

)

787

)

788

789

# Check which condition was met

790

if result.get_attribute("id") == "success-message":

791

print("Operation succeeded")

792

else:

793

print("Operation failed")

794

```

795

796

### all_of() (Custom Implementation)

797

798

While not built into Selenium, you can create an all_of condition:

799

800

```python

801

def all_of(*expected_conditions):

802

"""Wait for all conditions to be true"""

803

def _predicate(driver):

804

results = []

805

for condition in expected_conditions:

806

result = condition(driver)

807

if not result:

808

return False

809

results.append(result)

810

return results

811

return _predicate

812

813

# Usage

814

wait.until(

815

all_of(

816

EC.visibility_of_element_located((By.ID, "form")),

817

EC.element_to_be_clickable((By.ID, "submit")),

818

EC.text_to_be_present_in_element((By.ID, "status"), "Ready")

819

)

820

)

821

```

822

823

## Custom Expected Conditions

824

825

You can create custom expected conditions by following the same pattern:

826

827

```python

828

def element_has_css_class(locator, css_class):

829

"""Wait for element to have a specific CSS class"""

830

def _predicate(driver):

831

try:

832

element = driver.find_element(*locator)

833

classes = element.get_attribute("class")

834

return css_class in classes.split() if classes else False

835

except:

836

return False

837

return _predicate

838

839

def page_source_contains(text):

840

"""Wait for page source to contain specific text"""

841

def _predicate(driver):

842

return text in driver.page_source

843

return _predicate

844

845

def element_count_to_be(locator, count):

846

"""Wait for specific number of elements"""

847

def _predicate(driver):

848

elements = driver.find_elements(*locator)

849

return len(elements) == count

850

return _predicate

851

852

# Usage

853

wait.until(element_has_css_class((By.ID, "status"), "active"))

854

wait.until(page_source_contains("Welcome"))

855

wait.until(element_count_to_be((By.CLASS_NAME, "item"), 5))

856

```

857

858

## Best Practices and Examples

859

860

### 1. Combine Multiple Conditions

861

862

```python

863

def wait_for_form_ready(driver):

864

"""Wait for form to be completely ready for interaction"""

865

wait = WebDriverWait(driver, 10)

866

867

# Wait for form to be visible

868

form = wait.until(

869

EC.visibility_of_element_located((By.ID, "registration-form"))

870

)

871

872

# Wait for all required fields to be present

873

wait.until(

874

EC.presence_of_all_elements_located((By.CSS_SELECTOR, "input[required]"))

875

)

876

877

# Wait for submit button to be clickable

878

wait.until(

879

EC.element_to_be_clickable((By.ID, "submit-btn"))

880

)

881

882

return form

883

```

884

885

### 2. Handle Dynamic Content

886

887

```python

888

def wait_for_search_results(driver, search_term):

889

"""Wait for search results to load and contain expected content"""

890

wait = WebDriverWait(driver, 15)

891

892

# Wait for loading to disappear

893

wait.until(

894

EC.invisibility_of_element_located((By.ID, "search-loading"))

895

)

896

897

# Wait for results container to be visible

898

results_container = wait.until(

899

EC.visibility_of_element_located((By.ID, "search-results"))

900

)

901

902

# Wait for at least one result or no-results message

903

wait.until(

904

EC.any_of(

905

EC.presence_of_element_located((By.CLASS_NAME, "result-item")),

906

EC.text_to_be_present_in_element(

907

(By.ID, "search-results"), "No results found"

908

)

909

)

910

)

911

912

return results_container

913

```

914

915

### 3. Error Handling with Waits

916

917

```python

918

def safe_wait_and_click(driver, locator, timeout=10):

919

"""Safely wait for element and click with error handling"""

920

try:

921

wait = WebDriverWait(driver, timeout)

922

element = wait.until(EC.element_to_be_clickable(locator))

923

element.click()

924

return True

925

except TimeoutException:

926

print(f"Element {locator} not clickable within {timeout} seconds")

927

return False

928

except Exception as e:

929

print(f"Error clicking element {locator}: {e}")

930

return False

931

932

# Usage

933

if safe_wait_and_click(driver, (By.ID, "submit-btn")):

934

print("Successfully clicked submit button")

935

else:

936

print("Failed to click submit button")

937

```

938

939

### 4. Polling with Custom Logic

940

941

```python

942

def wait_for_api_response(driver, timeout=30):

943

"""Wait for API response to be displayed"""

944

wait = WebDriverWait(driver, timeout, poll_frequency=0.5)

945

946

def api_response_ready(driver):

947

try:

948

status = driver.find_element(By.ID, "api-status")

949

response = driver.find_element(By.ID, "api-response")

950

951

# Check if API call is complete

952

if status.text == "Complete":

953

# Check if response has content

954

if response.text and response.text != "Loading...":

955

return {"status": status.text, "response": response.text}

956

return False

957

except:

958

return False

959

960

return wait.until(api_response_ready)

961

962

# Usage

963

result = wait_for_api_response(driver)

964

print(f"API Status: {result['status']}")

965

print(f"Response: {result['response']}")

966

```

967

968

### 5. Fluent Wait Pattern

969

970

```python

971

from selenium.webdriver.support.ui import WebDriverWait

972

from selenium.common.exceptions import TimeoutException

973

974

class FluentWait:

975

def __init__(self, driver, timeout=10, poll_frequency=0.5):

976

self.driver = driver

977

self.timeout = timeout

978

self.poll_frequency = poll_frequency

979

self.ignored_exceptions = []

980

981

def ignoring(self, *exceptions):

982

"""Add exceptions to ignore"""

983

self.ignored_exceptions.extend(exceptions)

984

return self

985

986

def until(self, condition, message=None):

987

"""Wait until condition is met"""

988

wait = WebDriverWait(

989

self.driver,

990

self.timeout,

991

self.poll_frequency,

992

tuple(self.ignored_exceptions) if self.ignored_exceptions else None

993

)

994

return wait.until(condition, message)

995

996

# Usage

997

from selenium.common.exceptions import StaleElementReferenceException

998

999

result = FluentWait(driver, timeout=15, poll_frequency=0.2)\

1000

.ignoring(StaleElementReferenceException)\

1001

.until(EC.element_to_be_clickable((By.ID, "dynamic-btn")))

1002

```

1003

1004

This comprehensive guide covers all aspects of WebDriverWait and Expected Conditions, providing you with the tools to handle any dynamic web content scenario in your Selenium automation.