java css selenium css-selectors shadow-dom

java - Cómo interactuar con los elementos dentro de#shadow-root(abierto) mientras se borran los datos de navegación del navegador Chrome usando cssSelector



selenium css-selectors (4)

La respuesta de @supputuri es la respuesta activa y aceptada, ya que la estrategia de localización a través de document.querySelector() funciona perfectamente google-chrome-devtools google-chrome-devtools

Sin embargo, a medida que el elemento deseado se abre desde la shadow-dom debe inducir a WebDriverWait para el elemento elementToBeClickable() y puede encontrar la siguiente solución:

  • Bloque de código:

    driver.get("chrome://settings/clearBrowserData"); new WebDriverWait(driver, 5).until(ExpectedConditions.elementToBeClickable((WebElement) ((JavascriptExecutor)driver).executeScript("return document.querySelector(''settings-ui'').shadowRoot.querySelector(''settings-main'').shadowRoot.querySelector(''settings-basic-page'').shadowRoot.querySelector(''settings-section > settings-privacy-page'').shadowRoot.querySelector(''settings-clear-browsing-data-dialog'').shadowRoot.querySelector(''#clearBrowsingDataDialog'').querySelector(''#clearBrowsingDataConfirm'')"))).click(); System.out.println("Clear data Button Clicked");

  • Salida de consola:

    Clear data Button Clicked

Había estado siguiendo la discusión ¿Cómo automatizar los elementos de la sombra DOM usando selenio? para trabajar con los elementos #shadow-root (open) .

Mientras estoy en el proceso de ubicar el botón Borrar datos en la ventana emergente Borrar datos de navegación , que aparece al acceder a la url chrome://settings/clearBrowserData través de Selenium, no puedo ubicar el siguiente elemento:

#shadow-root (open) <settings-privacy-page>

Instantánea:

Usando Selenium continuación están mis pruebas de código y los errores asociados encontrados:

  • Intento 1:

    WebElement root5 = shadow_root4.findElement(By.tagName("settings-privacy-page"));

    • Error:

      Exception in thread "main" org.openqa.selenium.JavascriptException: javascript error: b.getElementsByTagName is not a function

  • Intento 2:

    WebElement root5 = shadow_root4.findElement(By.cssSelector("settings-privacy-page"));

    • Error:

      Exception in thread "main" org.openqa.selenium.NoSuchElementException: no such element: Unable to locate element: {"method":"css selector","selector":"settings-privacy-page"}

  • Intento 3:

    WebElement root5 = (WebElement)((JavascriptExecutor)shadow_root4).executeScript("return document.getElementsByTagName(''settings-privacy-page'')[0]");

    • Error:

      Exception in thread "main" java.lang.ClassCastException: org.openqa.selenium.remote.RemoteWebElement cannot be cast to org.openqa.selenium.JavascriptExecutor

En caso de ser útil, el bloque de código inicial (hasta la línea anterior) funciona perfectamente:

driver.get("chrome://settings/clearBrowserData"); WebElement root1 = driver.findElement(By.tagName("settings-ui")); WebElement shadow_root1 = expand_shadow_element(root1); WebElement root2 = shadow_root1.findElement(By.cssSelector("settings-main#main")); WebElement shadow_root2 = expand_shadow_element(root2); WebElement root3 = shadow_root2.findElement(By.cssSelector("settings-basic-page[role=''main'']")); WebElement shadow_root3 = expand_shadow_element(root3); WebElement root4 = shadow_root3.findElement(By.cssSelector("settings-section[page-title=''Privacy and security'']")); WebElement shadow_root4 = expand_shadow_element(root4);

PS: expand_shadow_element() funciona sin fallas.

¿Puede alguien ayudarme por favor?


Me pregunto si esta es una ventana secundaria separada, ya que está en primer plano y es por eso que no se puede encontrar el elemento en la pantalla. Una vez que obtenga la manija de la ventana secundaria, ¿debería poder agarrar el elemento?


Si está intentando obtener el elemento ''Borrar datos'', puede usar los js a continuación para obtener el elemento y luego realizar.

return document.querySelector(''settings-ui'').shadowRoot.querySelector(''settings-main'').shadowRoot.querySelector(''settings-basic-page'').shadowRoot.querySelector(''settings-section > settings-privacy-page'').shadowRoot.querySelector(''settings-clear-browsing-data-dialog'').shadowRoot.querySelector(''#clearBrowsingDataDialog'').querySelector(''#clearBrowsingDataConfirm'')

Aquí está la secuencia de comandos de muestra.

driver.get("chrome://settings/clearBrowserData"); driver.manage().window().maximize(); JavascriptExecutor js = (JavascriptExecutor) driver; WebElement clearData = (WebElement) js.executeScript("return document.querySelector(''settings-ui'').shadowRoot.querySelector(''settings-main'').shadowRoot.querySelector(''settings-basic-page'').shadowRoot.querySelector(''settings-section > settings-privacy-page'').shadowRoot.querySelector(''settings-clear-browsing-data-dialog'').shadowRoot.querySelector(''#clearBrowsingDataDialog'').querySelector(''#clearBrowsingDataConfirm'')"); // now you can click on clear data button clearData.click();

Edición 2: Explicación

Problema: Selenium no proporciona soporte explícito para trabajar con elementos DOM de Shadow , ya que no están en el dom actual. Esa es la razón por la que NoSuchElementException excepción NoSuchElementException cuando intentemos acceder a los elementos en el NoSuchElementException la shadow dom .

Shadow DOM:

Nota: Nos referiremos a los términos que se muestran en la imagen. Así que por favor ve a través de la imagen para una mejor comprensión.

Solución:

Para trabajar con el elemento de sombra primero tenemos que encontrar el shadow host al que se adjunta el dom de sombra. Aquí está el método simple para obtener la raíz de la sombra basada en el host de sombra.

private static WebElement getShadowRoot(WebDriver driver,WebElement shadowHost) { JavascriptExecutor js = (JavascriptExecutor) driver; return (WebElement) js.executeScript("return arguments[0].shadowRoot", shadowHost); }

Y luego puedes acceder al elemento del árbol de sombra usando el elemento shadowRoot.

// get the shadowHost in the original dom using findElement WebElement shadowHost = driver.findElement(By.cssSelector("shadowHost_CSS")); // get the shadow root WebElement shadowRoot = getShadowRoot(driver,shadowHost); // access shadow tree element WebElement shadowTreeElement = shadowRoot.findElement(By.cssSelector("shadow_tree_element_css"));

Con el fin de simplificar todos los pasos anteriores creó el método de abajo.

public static WebElement getShadowElement(WebDriver driver,WebElement shadowHost, String cssOfShadowElement) { WebElement shardowRoot = getShadowRoot(driver, shadowHost); return shardowRoot.findElement(By.cssSelector(cssOfShadowElement)); }

Ahora puedes obtener el elemento shadowTree con un solo método de llamada

WebElement shadowHost = driver.findElement(By.cssSelector("shadowHost_CSS_Goes_here)); WebElement shadowTreeElement = getShadowElement(driver,shadowHost,"shadow_tree_element_css");

Y realice las operaciones como de costumbre, como .click() , .getText() .

shadowTreeElement.click()

Esto parece simple cuando solo tienes un nivel de DOM de sombra. Pero aquí, en este caso, tenemos múltiples niveles de doms de sombra. Así que tenemos que acceder al elemento alcanzando cada host de sombra y raíz.

A continuación se muestra el fragmento con los métodos mencionados anteriormente (getShadowElement y getShadowRoot)

// Locate shadowHost on the current dom WebElement shadowHostL1 = driver.findElement(By.cssSelector("settings-ui")); // now locate the shadowElement by traversing all shadow levels WebElement shadowElementL1 = getShadowElement(driver, shadowHostL1, "settings-main"); WebElement shadowElementL2 = getShadowElement(driver, shadowElementL1,"settings-basic-page"); WebElement shadowElementL3 = getShadowElement(driver, shadowElementL2,"settings-section > settings-privacy-page"); WebElement shadowElementL4 = getShadowElement(driver, shadowElementL3,"settings-clear-browsing-data-dialog"); WebElement shadowElementL5 = getShadowElement(driver, shadowElementL4,"#clearBrowsingDataDialog"); WebElement clearData = shadowElementL5.findElement(By.cssSelector("#clearBrowsingDataConfirm")); System.out.println(clearData.getText()); clearData.click();

Puede lograr todos los pasos anteriores en la llamada js individual como se mencionó al principio de la respuesta (agregada a continuación solo para reducir la confusión).

WebElement clearData = (WebElement) js.executeScript("return document.querySelector(''settings-ui'').shadowRoot.querySelector(''settings-main'').shadowRoot.querySelector(''settings-basic-page'').shadowRoot.querySelector(''settings-section > settings-privacy-page'').shadowRoot.querySelector(''settings-clear-browsing-data-dialog'').shadowRoot.querySelector(''#clearBrowsingDataDialog'').querySelector(''#clearBrowsingDataConfirm'')");

Captura de pantalla:


Tuve que hacer una prueba similar que requería borrar el historial de Chrome. Una pequeña diferencia fue que estaba borrando los datos después de ir a la sección avanzada de la ventana emergente. Como está luchando para hacer clic solo en el botón "Borrar datos", estoy bastante seguro de que se ha perdido por error uno o dos elementos de la jerarquía. O se confundió entre elementos hermanos y padres probablemente. Al ver su código, asumo que ya sabe que para acceder a un elemento DOM de sombra en particular, necesita una secuencia adecuada y esto se ha explicado bastante bien anteriormente.
Ahora mismo, en mi problema, aquí está mi fragmento de código que está funcionando correctamente. El código espera hasta que se limpien los datos y luego se procederá a su siguiente acción:

public WebElement expandRootElement(WebElement element) { WebElement ele = (WebElement) ((JavascriptExecutor) driver).executeScript("return arguments[0].shadowRoot", element); return ele; } public void clearBrowsingHistory() throws Exception { WebDriverWait wait = new WebDriverWait(driver, 15); driver.get("chrome://settings/clearBrowserData"); // Get shadow root elements WebElement shadowRoot1 = expandRootElement(driver.findElement(By.xpath("/html/body/settings-ui"))); WebElement root2 = shadowRoot1.findElement(By.cssSelector("settings-main")); WebElement shadowRoot2 = expandRootElement(root2); WebElement root3 = shadowRoot2.findElement(By.cssSelector("settings-basic-page")); WebElement shadowRoot3 = expandRootElement(root3); WebElement root4 = shadowRoot3 .findElement(By.cssSelector("#advancedPage > settings-section > settings-privacy-page")); WebElement shadowRoot4 = expandRootElement(root4); WebElement root5 = shadowRoot4.findElement(By.cssSelector("settings-clear-browsing-data-dialog")); WebElement shadowRoot5 = expandRootElement(root5); WebElement root6 = shadowRoot5 .findElement(By.cssSelector("cr-dialog div[slot =''button-container''] #clearBrowsingDataConfirm")); root6.click(); wait.until(ExpectedConditions.invisibilityOf(root6)); }

También debería funcionar correctamente en su caso si no tiene la intención de cambiar ninguna de las opciones seleccionadas de forma predeterminada en la ventana emergente (en ese caso, tendrá que agregar algunos códigos más para seleccionar esas casillas de verificación). Por favor, dime si esto resuelve tu problema. Espero que esto sea útil. También he agregado una instantánea de la pantalla aquí - image