Selenium: How does WebDriverWait (presence_of_all_elements_located) actually works?

Question:

I know what it does, but can’t understand HOW it does, if you know what I mean.
For example, the code below will pull out all links from the page OR it will timeout if it won’t find any <a> tag on the page.

driver.get('https://selenium-python.readthedocs.io/waits.html')

links = WebDriverWait(driver, 30).until(EC.presence_of_all_elements_located((By.TAG_NAME, 'a')))

for link in links:
    print(link.get_attribute('href'))

driver.quit()

I’m wondering HOW Selenium knows for sure that presence_of_all_elements_located((By.TAG_NAME, 'a')) detected all <a> elements and the page won’t dynamically load any more links?

BTW, pardon the following question, but can you also explain why we use double brackets here EC.presence_of_all_elements_located((By.TAG_NAME, 'a'))? Is that because presence_of_all_elements_located method accepts tuple as its parameter?

Asked By: Morello

||

Answers:

Selenium doesn’t know the page won’t dynamically load more links. When you use this presence_of_all_elements_located class (not a method!), then so long as there is 1 matching element on the page, it will return a list of all such elements.

When you write EC.presence_of_all_elements_located((By.TAG_NAME, 'a')) you are instantiating this class with a single argument which is a tuple as you say. This tuple is called a "locator".

"How this works" is kind of complicated and the only way to really understand is to read the source code. Selenium sees the root html as a WebElement and all children elements also as WebElements. These classes are created and discarded dynamically. They are only kept around if assigned to something. When you check for the presence of all elements matching your locator, it will traverse the HTML tree by jumping from parent to children and back up to parent siblings. Waiting for the presence of something just does this on a loop until it gets a positive match (then it completes the tree traversal and returns a list) or until the wait times out.

Answered By: Peaceful James

The way:

import time
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.by import By
from selenium.common.exceptions import NoSuchElementException

s=Service(ChromeDriverManager().install())
options = webdriver.ChromeOptions() 
options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_experimental_option('useAutomationExtension', False)
driver = webdriver.Chrome(service=s, options=options)

def trye():
    driver.get('https://google.com')

    for trytimes in range(15):
        try:
            a = driver.find_element(By.XPATH, '/html/body/div[1]/div[2]/div/img').is_displayed()
            b = driver.find_element(By.XPATH, '/html/body/div[1]/div[3]/form/div[1]/div[1]/div[1]/div/div[2]/input').is_displayed()
            if a and b:
                return True
        except NoSuchElementException:
            time.sleep(1)
            continue
        except:
            return False
    else:
        return False

print(trye())

Tested by me.

Answered By: boludoz