Custom expected conditions in Selenium Python - Build an Automation Script
from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.common.exceptions import TimeoutException class TextEquals: def __init__(self, locator, text): self.locator = locator self.text = text def __call__(self, driver): element = driver.find_element(*self.locator) if element.text == self.text: return element else: return False def test_wait_for_text(): driver = webdriver.Chrome() try: driver.get('https://example.com/dynamic-text') wait = WebDriverWait(driver, 10) element = wait.until(TextEquals((By.ID, 'status-message'), 'Completed')) assert element.text == 'Completed', f"Expected text 'Completed' but got '{element.text}'" except TimeoutException: assert False, "Timed out waiting for text 'Completed' in element with ID 'status-message'" finally: driver.quit()
This script defines a custom expected condition class TextEquals that waits for an element's text to match exactly a given string.
The __call__ method is used by Selenium's WebDriverWait to repeatedly check the element's text until it matches or timeout occurs.
The test function test_wait_for_text opens the browser, navigates to the page, and waits up to 10 seconds for the text 'Completed' to appear in the element with ID 'status-message'.
If the text matches, it asserts success; if it times out, it asserts failure with a clear message.
Finally, the browser is closed to clean up resources.
Now add data-driven testing with 3 different expected texts: 'Completed', 'Failed', and 'Pending'.
Practice
Solution
Step 1: Understand built-in expected conditions
Built-in expected conditions cover common waits like element visibility or clickability.Step 2: Identify the need for custom conditions
When a specific wait is not available, custom expected conditions let you define exactly what to wait for.Final Answer:
To wait for a specific condition that is not covered by built-in expected conditions -> Option CQuick Check:
Custom expected conditions = wait for custom needs [OK]
- Thinking custom conditions speed up browser loading
- Confusing custom conditions with browser resizing
- Assuming custom conditions generate reports
Solution
Step 1: Recall the special method for callable classes
The method __call__ makes an instance callable like a function.Step 2: Check method signature and return
The __call__ method takes driver and returns a boolean or element to wait for.Final Answer:
class MyCondition:\n def __call__(self, driver):\n return driver.title == 'Home' -> Option AQuick Check:
Custom condition class needs __call__ method [OK]
- Using call instead of __call__ method
- Defining __init__ to return a value
- Defining a function instead of a class
WebDriverWait(driver, 10).until(MyCondition()) return if the page title is 'Dashboard'?
class MyCondition:
def __call__(self, driver):
if driver.title == 'Dashboard':
return True
else:
return FalseSolution
Step 1: Understand WebDriverWait with custom condition
WebDriverWait calls the condition repeatedly until it returns a truthy value or timeout.Step 2: Analyze the condition's return value
If the title is 'Dashboard', __call__ returns True, so wait ends and returns True.Final Answer:
It will wait until the title is 'Dashboard' and then return True -> Option DQuick Check:
Condition returns True -> wait ends successfully [OK]
- Thinking it raises TimeoutException immediately
- Assuming it returns False immediately
- Confusing return value with driver object
class ElementHasText:
def __call__(self, driver):
element = driver.find_element(By.ID, 'msg')
if element.text:
return element.text
else:
return FalseSolution
Step 1: Check usage of By.ID in the code
The code uses By.ID but does not import By, causing a NameError.Step 2: Verify other parts of the code
Returning element.text is valid for expected conditions; inheritance from WebDriverWait is not required.Final Answer:
Missing import for By from selenium.webdriver.common.by -> Option AQuick Check:
Using By requires importing it first [OK]
- Assuming __call__ must return only True/False
- Thinking class must inherit WebDriverWait
- Confusing find_element with find_elements
Solution
Step 1: Understand the requirement
We want to wait until the text contains the word 'Complete' anywhere inside.Step 2: Analyze each option's condition
class StatusComplete: def __call__(self, driver): element = driver.find_element(By.ID, 'status') return 'Complete' in element.text checks if 'Complete' is in element.text, which matches the requirement exactly and returns a boolean.Step 3: Compare other options
class StatusComplete: def __call__(self, driver): element = driver.find_element(By.ID, 'status') if element.text == 'Complete': return True else: return False checks exact equality, which is too strict. class StatusComplete: def __call__(self, driver): element = driver.find_element(By.ID, 'status') return element.text.startswith('Complete') checks startswith, which is partial. class StatusComplete: def __call__(self, driver): element = driver.find_element(By.ID, 'status') return element.text != '' only checks non-empty text, which is too broad.Final Answer:
class StatusComplete:\n def __call__(self, driver):\n element = driver.find_element(By.ID, 'status')\n return 'Complete' in element.text -> Option BQuick Check:
Use 'in' to check substring presence [OK]
- Checking exact equality instead of substring
- Using startswith instead of contains
- Checking only for non-empty text
Ready to prove it?
Pass the test and this pattern moves unknown
