'Why does Selenium's wait.until_not(EC.invisibility_of_element_located) wait for too long for a loader to disappear?

Which selenium.webdriver.support.expected_conditions are better to use when waiting for the invisibility of an element? In my case, I input data into a form, click save and wait for a loader to disappear

from selenium.webdriver.support import expected_conditions as EC 
wait = WebDriverWait(driver, 10)
wait.until(EC.presence_of_element_located((SelectBy.CSS_SELECTOR, ".spinner")))
debug("loader appeared")
wait.until(EC.invisibility_of_element_located((SelectBy.CSS_SELECTOR, ".spinner")))
debug("loader disappeared")

In the output, I see that the second wait is executed for 20 seconds (my global implicit wait is 20 seconds)

360ms ⟥     [debug] loader appeared
21s 141ms ⟥ [debug] loader disappeared

The locator is good, I am trying to understand what is wrong with the wait. Did anyone have similar problems? I would be happy for any suggestions.



Solution 1:[1]

From the documentation of Waits

Warning: Do not mix implicit and explicit waits. Doing so can cause unpredictable wait times. For example, setting an implicit wait of 10 seconds and an explicit wait of 15 seconds could cause a timeout to occur after 20 seconds.

Possibly the mix up of the following 2 waits:

  • global implicit wait is 20 seconds
  • WebDriverWait(driver, 10)

is causing unpredictable wait times.


Solution

While inducing WebDriverWait you need to reconfigure implicit wait to 0 using the following line of code:

  • Python:

    driver.implicitly_wait(0)
    
  • Java:

    driver.manage().timeouts().implicitlyWait(0, TimeUnit.SECONDS);
    
  • DotNet:

    driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(0);
    

Solution 2:[2]

Your wait operations are stacking on each other because your code is chained.

Let me explain with your code:

# 1. create a wait object
wait = WebDriverWait(driver, 10)

# 2. execute a wait statement
wait.until(EC.presence_of_element_located((SelectBy.CSS_SELECTOR, ".spinner")))
debug("loader appeared")

# 3. execute a wait statement
wait.until(EC.invisibility_of_element_located((SelectBy.CSS_SELECTOR, ".spinner")))
debug("loader disappeared")

Both wait statements (#2 and #3) are using the same wait object, so their execution will "stack":

  1. Wait object will wait 10 seconds for a condition
  2. Wait 10 seconds (inherited) + spinner appear
  3. Wait 10 seconds (inherited) + spinner appear (inherited) + spinner disappear

Action #2 waits 10s. Action #3 waits 20s.

Solution 3:[3]

From the documentation of the Explicit Waits

You may also try this:

try:
  WebDriverWait(driver, 10).until(
    EC.invisibility_of_element_located((By.CSS_SELECTOR, ".spinner"))
  )
finally:
  debug('loader disappeared')

In the code above, Selenium will wait for a maximum of 10 seconds for an element matching the given criteria to be found. If no element is found in that time, a TimeoutException is thrown. By default, WebDriverWait calls the ExpectedCondition every 500 milliseconds until it returns success. ExpectedCondition will return true (Boolean) in case of success or not null if it fails to locate an element.

This way solved my problem, and I think it is a good practice where you won't be waiting more than the preset time.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Yaakov Bressler
Solution 3 vitaliis