3

There are ways to wait for an object e.g. a button to be clickable in selenium python. I use time.sleep() and/or WebDriverWait...until, it works fine.

However, when there are hundreds of objects, is there a way to set a default time lag globally, instead of implementing it on each object?

The click() action should have a conditional wait time?

JeffC
  • 22,180
  • 5
  • 32
  • 55
Heinz
  • 913
  • 4
  • 12
  • 22

3 Answers3

7

I come up with this:

from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By

def myClick(by, desc):
    wait = WebDriverWait(driver, 10)
    by = by.upper()
    if by == 'XPATH':
        wait.until(EC.element_to_be_clickable((By.XPATH, desc))).click()
    if by == 'ID':
        wait.until(EC.element_to_be_clickable((By.ID, desc))).click()
    if by == 'LINK_TEXT':
        wait.until(EC.element_to_be_clickable((By.LINK_TEXT, desc))).click()

with this function, the code:

driver.find_element_by_link_text('Show Latest Permit').click()

will be

myClick('link_text', 'Show Latest Permit')

instead.

I have run a couple weeks with hundreds of elements to click, I have not seen the errors any longer.

CodingCat
  • 4,999
  • 10
  • 37
  • 59
Heinz
  • 913
  • 4
  • 12
  • 22
  • This seems to work excellently, thanks! I've edited to add the necessary imports, as I had to look up what EC is. – CodingCat Dec 17 '21 at 10:13
2

You can do a few things...

  1. Define a global default wait time and then use that in each wait you create.

    default_wait_time = 10 # seconds
    ...
    wait = WebDriverWait(driver, default_wait_time)
    
  2. Inside of a method where you will use the wait several times, you can instantiate a wait, store it in a variable, and then reuse it.

    def my_method(self):
        wait = WebDriverWait(driver, 10)
        wait.until(EC.visibility_of_element_located((By.ID, "username")).send_keys("username")
        wait.until(EC.visibility_of_element_located((By.ID, "password")).send_keys("password")
        wait.until(EC.element_to_be_clickable((By.ID, "login")).click()
    
  3. Define a default WebDriverWait instance and then just repeatedly use that.

    Note: if you are or will run your script(s) in parallel, you need to be very careful with this approach because an instance of WebDriverWait is tied to a specific driver.

    # some global location
    wait = WebDriverWait(driver, 10)
    ...
    # in your script, page object, helper method, etc.
    wait.until(EC.element_to_be_clickable((By.ID, "login")).click()
    
JeffC
  • 22,180
  • 5
  • 32
  • 55
  • thank you for the response. Are the `myId, myId2 and myId3` referring to the same objects? – Heinz May 30 '19 at 16:39
  • No, they are referring to 3 different elements with three different IDs. The intention is just that they are simple examples to show how to reuse the wait for three different actions you might take on the page. – JeffC May 30 '19 at 19:56
  • I updated the locators to be more like an actual scenario... like login. Hopefully that makes a little more sense. – JeffC May 30 '19 at 20:00
  • it seems still to be an overkill to repeat typing `wait.until(EC.element_to_be_clickable` for each elements, I tried: `def myClick(by, desc): wait = WebDriverWait(dr, 10) wait.until(EC.element_to_be_clickable(by, desc)).click()`, but calling this function `myClick("By.ID", "home-link-container")` errors out with **wait.until(EC.element_to_be_clickable(by, desc)).click() TypeError: __init__() takes 2 positional arguments but 3 were given**, any idea what is wrong? – Heinz May 31 '19 at 15:07
  • I don't disagree with you. In the framework I wrote, I use a generic method `click()` that takes in a locator and then waits for clickable and clicks the element. I also have methods for send_keys and the like. That centralizes at least some of the waits. I wouldn't worry so much about that "extra" typing/code. It's the right thing to do. If you are using page object model (and you should be), a lot of this is write once and rarely touch. – JeffC May 31 '19 at 16:20
0

is there a way to set a default time lag globally, instead of implementing it on each object?

Yes, that's exactly what setting an Implicit Wait does. The implicit wait is used for the life of the WebDriver.

example:

driver.implicitly_wait(10)

info:

Corey Goldberg
  • 59,062
  • 28
  • 129
  • 143