0

I'm trying to work with Selenium Webdriver with Python, automating the Chrome browser.

In re-optimising my code, I'm trying to get rid of any time.sleep() commands and using implicit and explicit waits when possible.

The part I'm stuck on is an iframe / modal dialog - after switching to it I still need to wait for the modal dialog to load before I can interact with elements without errors.

Code that works:

driver.find_element(By.ID, "process").click()   # Process form button - creates modal dialog
time.sleep(2)                                   # Delay to wait for the frame to load
driver.switch_to.frame("acsframe")              # acsframe is the ID of the modal dialog
driver.find_element(By.CSS_SELECTOR, r'input.btn.btn-warning').click() # Cancel button

Code that I'm trying to get to work:

driver.find_element(By.ID, "process").click() # Process form button
WebDriverWait(driver, 5).until(EC.frame_to_be_available_and_switch_to_it((By.ID, "acsframe")))
WebDriverWait(driver, 5).until(EC.element_to_be_clickable((By.CSS_SELECTOR, r'input.btn.btn-warning')))
driver.find_element(By.CSS_SELECTOR, r'input.btn.btn-warning').click() # Cancel button

Error Message:

NoSuchWindowException                     Traceback (most recent call last)
<ipython-input-24-acf8921ff08a> in <module>
      2 WebDriverWait(driver, 5).until(EC.frame_to_be_available_and_switch_to_it((By.ID, "acsframe")))
----> 3 WebDriverWait(driver, 5).until(EC.element_to_be_clickable((By.CSS_SELECTOR, r'input.btn.btn-warning')))
      4 driver.find_element(By.CSS_SELECTOR, r'input.btn.btn-warning').click()

I don't understand how/why I would be getting an error message here. I switch to the frame on line 2, and line 3 works about 1 time in 10, but otherwise throws an error. I know the element locator is right because it worked in the first example. If time.sleep() worked, why wouldn't this work?

I've tried changing between XPATH and CSS Selector for locating the elements and it's the same result. There aren't any IDs for the buttons.

Update: This code also works:

driver.find_element(By.ID, "process").click() # Process form button
time.sleep(2)
WebDriverWait(driver, 5).until(EC.frame_to_be_available_and_switch_to_it((By.ID, "acsframe")))
WebDriverWait(driver, 5).until(EC.element_to_be_clickable((By.CSS_SELECTOR, r'input.btn.btn-warning')))
driver.find_element(By.CSS_SELECTOR, r'input.btn.btn-warning').click()

So it seems that a delay before waiting for the frame fixes it - but isn't that the entire purpose of the command to wait for the frame to be available? I still want a way to get this to work without time.sleep().

Screenshot of the relevant HTML

Adam
  • 1
  • 1

1 Answers1

0

As the element is within an <iframe> so you have to:

  • Induce WebDriverWait for the desired frame to be available and switch to it.

  • Induce WebDriverWait for the desired element to be clickable.

  • You can use the following Locator Strategies:

    driver.find_element(By.ID, "process").click() # Process form button
    WebDriverWait(driver, 20).until(EC.frame_to_be_available_and_switch_to_it((By.ID, "acsframe")))
    WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.CSS_SELECTOR, r'input.btn.btn-warning'))).click()
    

Reference

You can find a couple of relevant discussions in:

DebanjanB
  • 118,661
  • 30
  • 168
  • 217
  • Thank you for your answer. Isn't this functionally the same as what I have done? It throws the same NoSuchWindowException - seems to fail to find the iframe. It works with a time.sleep(2) before the first WebDriverWait. – Adam Jan 08 '21 at 11:00
  • @Adam There are delta differences between your code and the code I have offered. However it's true we can't test out code since you haven't provided the relevant HTML. – DebanjanB Jan 08 '21 at 11:05
  • I've added a screenshot of some of the relevant HTML - perhaps from that it's clear why it only works with the time.sleep? The iframe takes over a second to load in the webpage. – Adam Jan 11 '21 at 09:35