Tengo una lista que tiene varios enlaces en cada sección. Cada sección tiene los mismos enlaces. Necesito hacer clic en un enlace en particular debajo de cada sección. He escrito el siguiente código pero cuando se ejecuta me da un stale element reference: element is not attached to the page document
error.
Este es mi codigo:
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 es la estructura HTML es la siguiente
<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>
El seguimiento de error es:
Exception in thread "main"
org.openqa.selenium.StaleElementReferenceException: stale element
reference: element is not attached to the page document
fuente
Siempre que se enfrente a este problema, simplemente defina el elemento web una vez más encima de la línea en la que está obteniendo un error.
Ejemplo:
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();
Dado que el DOM ha cambiado, por ejemplo, a través de la acción de actualización, está recibiendo un
StaleElementReference
error.Solución:
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();
fuente
Para manejarlo, utilizo el siguiente método de clic. Esto intentará encontrar y hacer clic en el elemento. Si el DOM cambia entre buscar y hacer clic, lo intentará de nuevo. La idea es que si falló y lo intento de nuevo inmediatamente, el segundo intento tendrá éxito. Si los cambios de DOM son muy rápidos, esto no 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; }
fuente
Estos errores tienen dos causas comunes: el elemento se ha eliminado por completo o el elemento ya no está adjunto al DOM.
Si ya marcó si no es su caso, podría estar enfrentando el mismo problema que yo.
El elemento en el DOM no se encuentra porque su página no está completamente cargada cuando Selenium está buscando el elemento. Para resolver eso, puede poner una condición de espera explícita que le indique a Selenium que espere hasta que el elemento esté disponible para hacer clic en él.
from selenium.webdriver.support import expected_conditions as EC wait = WebDriverWait(driver, 10) element = wait.until(EC.element_to_be_clickable((By.ID, 'someid')))
Ver: https://selenium-python.readthedocs.io/waits.html
fuente
La cosa aquí es que estás usando un bucle for fuera de tu declaración condicional.
Después de que se cumplan las condiciones en su declaración IF, probablemente navegue a otra página, por lo tanto, cuando el bucle for intente iterar una vez más, obtendrá el error de elemento obsoleto porque está en una página diferente.
Puede agregar un descanso al final de su declaración if, esto funcionó para mí.
fuente
Simplemente rompa el ciclo cuando encuentre el elemento en el que desea hacer clic. por ejemplo:
List<WebElement> buttons = getButtonElements(); for (WebElement b : buttons) { if (b.getText().equals("Next"){ b.click(); break; }
fuente
Utilice 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"); } } } }
fuente
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 de prueba / captura realmente funcionó para mí. Obtuve el mismo error de elemento obsoleto.
fuente
Esto se podría hacer en versiones más recientes de selenium en JS (pero todo el soporte stalenessOf funcionará):
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) );
fuente
Según @Abhishek Singh, debes comprender el problema:
y ya no puede hacer referencia a él (imagine qué ID de elemento ha cambiado).
Siga el 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(); } }
Ahora, preguntémonos por qué.
btnTurnOff
fue encontrado por un conductor - okbtnTurnOff
fue reemplazado porbtnTurnOn
- okbtnTurnOn
fue encontrado por un conductor. - OkaybtnTurnOn
fue reemplazado porbtnTurnOff
- okthis.btnTurnOff.isDisplayed();
al elemento que ya no existe en el sentido de Selenium; puedes verlo, funciona perfectamente, pero es una instancia diferente del mismo botón .Posible solución:
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; }
fuente
En mi caso, tenía una página donde era una
input type='date'
referencia cuya referencia había obtenido al cargar la página, pero cuando traté de interactuar con ella, mostró estoexception
y eso fue bastante significativo, ya queJavascript
había manipulado mi control, por lo tanto, se separó del documento. y tuvere-get
su referencia después de que javascript hubiera realizado su trabajo con el control. Entonces, así es como se veía mi código antes de la excepción:if (elemDate != null) { elemDate.Clear(); elemDate.SendKeys(model.Age); }
Código después de que se generó la excepción:
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.
Por lo tanto, ahora puede realizar más acciones con su código o puede salir del controlador si no logró que el control funcione.
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...
PD: Después de escribir todo esto, me di cuenta de las etiquetas para las que era este hilo
java
. Esta muestra de código es solo para fines de demostración, podría ayudar a las personas que tienen problemas con elC#
idioma. O se puede traducir fácilmentejava
ya que no tiene muchoC#
código específico.fuente
use este código para esperar hasta que se adjunte el elemento:
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; } }
fuente