3

I'd like to keep my selectors simple when possible, and only use XPath where absolutely necessary. So rather than waiting for an XPath like //*[@class='files']/tbody/tr[1]/th[text()='filename'] I'd rather want to wait for the combination of the simple CSS selector .files tbody tr:first-child and the simple XPath th[text()='filename'].

presence_of_element_located only takes a single locator, so presence_of_element_located((By.CSS_SELECTOR, '.files tbody tr:first-child'), (By.XPATH, 'th[text()='filename']')) is out.

I can't chain these functions either, so presence_of_element_located((By.CSS_SELECTOR, '.files tbody tr:first-child')).presence_of_element_located((By.XPATH, 'th[text()='filename']')) won't work.

undetected Selenium
  • 183,867
  • 41
  • 278
  • 352
l0b0
  • 55,365
  • 30
  • 138
  • 223

1 Answers1

1

You can create your custom method:

def find_element_by_combo(self, ancestor_locator, descendant_locator, timeout=10, index=0):
    # Getting list of ancestors
    try:
        ancestors =  wait(self, timeout).until(EC.presence_of_all_elements_located(ancestor_locator))
    except:
        return None
    # Getting descendant
    for ancestor in ancestors:
        if ancestor.find_elements(*descendant_locator):
            return ancestor.find_elements(*descendant_locator)[index]

and use it as below

from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait as wait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver import Chrome

Chrome.find_element_by_combo = find_element_by_combo

driver = Chrome()
driver.get(URL)
driver.find_element_by_combo(('css', '.files tbody tr:first-child'), ('xpath', './th[text()="filename"]'))

But IMHO it's better just to use XPath for complicated situations

Andersson
  • 51,635
  • 17
  • 77
  • 129