Como esperar corretamente que um quadro esteja disponível no python-selenium?
Tenho uma configuração de página da Web bastante complexa que preciso testar, contendo quadros aninhados.
No problema real, o código do selênio está carregando o novo conteúdo da página da Web que contém um quadro, para o qual quero mudar. Para evitar esperas explícitas, tentei o seguinte trecho de código:
self.driver.switch_to_default_content()
WebDriverWait(self.driver, 300).\
until(EC.frame_to_be_available_and_switch_to_it((By.ID, 'frame1')))
WebDriverWait(self.driver, 300).\
until(EC.frame_to_be_available_and_switch_to_it((By.ID, 'frame2')))
No entanto, esse trecho sempre falha e resulta no seguinte erro:
...
File "/home/adietz/Projects/Venv/nosetests/local/lib/python2.7/site-packages/selenium/webdriver/support/wait.py", line 71, in until
value = method(self._driver)
File "/home/adietz/Projects/Venv/nosetests/local/lib/python2.7/site-packages/selenium/webdriver/support/expected_conditions.py", line 247, in __call__
self.frame_locator))
File "/home/adietz/Projects/Venv/nosetests/local/lib/python2.7/site-packages/selenium/webdriver/support/expected_conditions.py", line 402, in _find_element
raise e
WebDriverException: Message: TypeError: can't access dead object
No entanto, se eu usar um sono além disso:
time.sleep(30)
self.driver.switch_to_default_content()
WebDriverWait(self.driver, 300).\
until(EC.frame_to_be_available_and_switch_to_it((By.ID, 'frame1')))
WebDriverWait(self.driver, 300).\
until(EC.frame_to_be_available_and_switch_to_it((By.ID, 'frame2')))
O selênio é capaz de encontrar o quadro dentro do quadro e mudar para ele. Parece que, no caso de erro, o selênio muda para 'quadro1' enquanto 'quadro2' ainda não está carregado, mas o 'quadro2' é carregado em outra instância do 'quadro1' ou não é reconhecido pelo selênio (talvez um bug?). Então agora o selênio está dentro de algum 'quadro1' e, por algumas razões, não percebe que o 'quadro2' foi carregado.
A única maneira de corrigir isso (sem dormir muito) é usando este trecho de código feio:
mustend = time.time() + 300
while time.time() < mustend:
try:
self.driver.switch_to_default_content()
self.driver.switch_to.frame(self.driver.find_element_by_id("frame1"))
self.driver.switch_to.frame(self.driver.find_element_by_id("frame2"))
break
except WebDriverException as e:
self.log("Sleeping 1 sec")
time.sleep(1)
if time.time() > mustend:
raise TimeoutException
Então, sempre que eu recebo umWebDriverException
(objeto morto), vou para o quadro de nível superior e tento mudar para o quadro interno - quadro a quadro.
Existe alguma outra abordagem que eu possa tentar?
Informação adicional
Os iframes estão aninhados, ou seja, 'quadro2' está dentro de 'quadro1'.