Tenho uma lista com vários links em cada seção. Cada seção tem os mesmos links. Preciso clicar em um link específico em cada seção. Eu escrevi o código abaixo, mas quando ele executa, ele me dá um stale element reference: element is not attached to the page document
erro.
Este é o meu código:
public static void main(String[] args) throws InterruptedException
{
WebDriver driver = new ChromeDriver();
driver.navigate().to("url......");
driver.findElement(By.id("Login1_txtEmailID")).sendKeys("[email protected]");
driver.findElement(By.id("Login1_txtPassword")).sendKeys("Testing1*");
driver.findElement(By.id("Login1_btnLogin")).click();
List<WebElement> LeftNavLinks=driver.findElements(By.xpath("//*[@id='sliding-navigation']//a"));
Thread.sleep(1000);
String ben="Benefit Status";
String[] linkTexts = new String[LeftNavLinks.size()];
int i = 0;
for (WebElement e : LeftNavLinks)
{
linkTexts[i] = e.getText();
System.out.print(i+" " + linkTexts[i]+"\n");
if(linkTexts[i].equals(ben))
{
String BenefitStatLi="//*[@id='sliding-navigation']/li[%s]/a";
System.out.print(i+" " + linkTexts[i]+"\n");
driver.findElement(By.xpath(String.format(BenefitStatLi,i))).click();
driver.findElement(By.xpath("//* [@id='divContentHolder']/div[1]/a[1]")).click();
}
i++;
}
}
}
Esta é a estrutura HTML é a seguinte
<div id="ucAdminMenu_divMenu">
<ul id="sliding-navigation">
<li class="sliding-element">
<a href=" ">Claims Status</a>
</li>
<li class="sliding-element">
<a href=" ">Eligibility Status</a>
</li>
<li class="sliding-element">
<h3>Section-1</h3>
</li>
<li class="sliding-element">
<a href=" ">Forms and Documents</a>
</li>
<li class="sliding-element">
<a href=" HourBank.aspx?id=002">Hour Bank</a>
</li>
<li class="sliding-element">
<h3>Section-2</h3>
</li>
<li class="sliding-element">
<a href=" ">Benefit Status</a>
</li>
<li class="sliding-element">
<a href=" ">Forms and Documents</a>
</li>
<li class="sliding-element">
<h3>Section-3</h3>
</li>
<li class="sliding-element">
<a href=" ">Forms and Documents</a>
</li>
<li class="sliding-element">
<h3>Testing Fund</h3>
</li>
<li class="sliding-element">
<a href=" ">Benefit Status</a>
</li>
<li class="sliding-element">
<a href=" ">Order ID Card</a>
</li>
</ul>
</div>
O rastreamento de erros é:
Exception in thread "main"
org.openqa.selenium.StaleElementReferenceException: stale element
reference: element is not attached to the page document
fonte
Sempre que você enfrentar esse problema, apenas defina o elemento da web mais uma vez acima da linha em que você está obtendo um erro.
Exemplo:
WebElement button = driver.findElement(By.xpath("xpath")); button.click(); //here you do something like update or save //then you try to use the button WebElement again to click button.click();
Como o DOM mudou, por exemplo, por meio da ação de atualização, você está recebendo um
StaleElementReference
erro.Solução:
WebElement button = driver.findElement(By.xpath("xpath")); button.click(); //here you do something like update or save //then you define the button element again before you use it WebElement button1 = driver.findElement(By.xpath("xpath")); //that new element will point to the same element in the new DOM button1.click();
fonte
Esses erros têm duas causas comuns: o elemento foi totalmente excluído ou o elemento não está mais anexado ao DOM.
Se você já verificou se não é o seu caso, você pode estar enfrentando o mesmo problema que eu.
O elemento no DOM não foi encontrado porque sua página não foi totalmente carregada quando o Selenium está procurando o elemento. Para resolver isso, você pode colocar uma condição de espera explícita que diga ao Selenium para esperar até que o elemento esteja disponível para ser clicado.
from selenium.webdriver.support import expected_conditions as EC wait = WebDriverWait(driver, 10) element = wait.until(EC.element_to_be_clickable((By.ID, 'someid')))
Veja: https://selenium-python.readthedocs.io/waits.html
fonte
Para lidar com isso, uso o seguinte método de clique. Isso tentará localizar e clicar no elemento. Se o DOM mudar entre localizar e clicar, ele tentará novamente. A ideia é que se ele falhar e eu tentar novamente imediatamente, a segunda tentativa terá sucesso. Se as alterações do DOM forem muito rápidas, isso não funcionará.
public boolean retryingFindClick(By by) { boolean result = false; int attempts = 0; while(attempts < 2) { try { driver.findElement(by).click(); result = true; break; } catch(StaleElementException e) { } attempts++; } return result; }
fonte
O que acontece aqui é que você está usando um loop for fora de sua instrução condicional.
Depois que as condições em sua instrução IF forem atendidas, você provavelmente navegará para outra página, portanto, quando o loop for tenta iterar mais uma vez, você obtém o erro de elemento obsoleto porque está em uma página diferente.
Você pode adicionar uma pausa no final de sua instrução if, isso funcionou para mim.
fonte
Basta interromper o loop quando encontrar o elemento que deseja clicar nele. por exemplo:
List<WebElement> buttons = getButtonElements(); for (WebElement b : buttons) { if (b.getText().equals("Next"){ b.click(); break; }
fonte
Use este código:
public class LinkTest { public static void main(String[] args) { WebDriver driver = new FirefoxDriver(); driver.navigate().to("file:///C:/Users/vkiran/Desktop/xyz.html"); List<WebElement> alllinks =driver.findElements(By.xpath("//*[@id='sliding-navigation']//a")); String a[]=new String[alllinks.size()]; for(int i=0;i<alllinks.size();i++) { a[i]=alllinks.get(i).getText(); if(a[i].startsWith("B")) { System.out.println("clicking on this link::"+driver.findElement(By.linkText(a[i])).getText()); driver.findElement(By.linkText(a[i])).click(); } else { System.out.println("does not starts with B so not clicking"); } } } }
fonte
try { WebElement button = driver.findElement(By.xpath("xpath")); button.click(); } catch(org.openqa.selenium.StaleElementReferenceException ex) { WebElement button = driver.findElement(By.xpath("xpath")); button.click(); }
Este código try / catch realmente funcionou para mim. Recebi o mesmo erro de elemento obsoleto.
fonte
Isso pode ser feito em versões mais recentes do selênio em JS (mas todos os que suportam stalenessOf funcionarão):
const { until } = require('selenium-webdriver'); driver.wait( until.stalenessOf( driver.findElement( By.css(SQLQueriesByPhpMyAdminSelectors.sqlQueryArea) ) ), 5 * 1000 ) .then( driver.findElement(By.css(SQLQueriesByPhpMyAdminSelectors.sqlQueryArea)) .sendKeys(sqlString) );
fonte
De acordo com @Abhishek Singh, você precisa entender o problema:
e você não pode mais se referir a ele (imagine qual ID do elemento mudou).
Siga o código:
class TogglingPage { @FindBy(...) private WebElement btnTurnOff; @FindBy(...) private WebElement btnTurnOn; TogglingPage turnOff() { this.btnTurnOff.isDisplayed(); this.btnTurnOff.click(); // when clicked, button should swap into btnTurnOn this.btnTurnOn.isDisplayed(); this.btnTurnOn.click(); // when clicked, button should swap into btnTurnOff this.btnTurnOff.isDisplayed(); // throws an exception return new TogglingPage(); } }
Agora, vamos nos perguntar por quê?
btnTurnOff
foi encontrado por um motorista - okbtnTurnOff
foi substituído porbtnTurnOn
- okbtnTurnOn
foi encontrado por um motorista. - Está bembtnTurnOn
foi substituído porbtnTurnOff
- okthis.btnTurnOff.isDisplayed();
o elemento que não existe mais no sentido do selênio - você pode ver, ele funciona perfeitamente, mas é uma instância diferente do mesmo botão .Possível correção:
TogglingPage turnOff() { this.btnTurnOff.isDisplayed(); this.btnTurnOff.click(); TogglingPage newPage = new TogglingPage(); newPage.btnTurnOn.isDisplayed(); newPage.btnTurnOn.click(); TogglingPage newerPage = new TogglingPage(); newerPage.btnTurnOff.isDisplayed(); // ok return newerPage; }
fonte
No meu caso, eu tinha uma página onde era uma
input type='date'
referência cuja referência eu obtive no carregamento da página, mas quando tentei interagir com ela, ela mostrou queexception
e aquilo foi bastante significativo, poisJavascript
havia manipulado meu controle, portanto, foi destacado do documento e tive are-get
sua referência após o javascript ter executado seu trabalho com o controle. Então, é assim que meu código parecia antes da exceção:if (elemDate != null) { elemDate.Clear(); elemDate.SendKeys(model.Age); }
Código depois que a exceção foi levantada:
int tries = 0; do { try { tries++; if (elemDate != null) { // these lines were causing the exception so I had break after these are successfully executed because if they are executed that means the control was found and attached to the document and we have taken the reference of it again. elemDate.Clear(); elemDate.SendKeys(model.Age); break; } } catch (StaleElementReferenceException) { System.Threading.Thread.Sleep(10); // put minor fake delay so Javascript on page does its actions with controls elemDate = driver.FindElement(By.Id(dateId)); } } while (tries < 3); // Try it three times.
Portanto, agora você pode executar outras ações com seu código ou pode encerrar o driver se ele não teve êxito em fazer o controle funcionar.
if(tries > 2) { // element was not found, find out what is causing the control detachment. // driver.Quit(); return; } // Hurray!! Control was attached and actions were performed. // Do something with it...
PS: Depois de escrever tudo isso, acabei de notar as tags para as quais este tópico se destina
java
. Este exemplo de código é apenas para fins de demonstração, pode ajudar as pessoas que têm problemas com aC#
linguagem. Ou pode ser facilmente traduzidojava
porque não tem muitoC#
código específico.fonte
use este código para esperar até que o elemento seja anexado:
boolean breakIt = true; while (true) { breakIt = true; try { // write your code here } catch (Exception e) { if (e.getMessage().contains("element is not attached")) { breakIt = false; } } if (breakIt) { break; } }
fonte