java - scss - ng host deep




Como interagir com os elementos dentro de#shadow-root(abrir) enquanto Clearing Browsing Data do Chrome Browser usando cssSelector (3)

@ resposta de supputuri é a resposta de trabalho e aceita como a estratégia de localizador através de document.querySelector() funciona perfeito através de google-chrome-devtools google-chrome-devtools

No entanto, à medida que o elemento desejado é aberto a partir do shadow-dom é necessário induzir o WebDriverWait para o elementToBeClickable() e você pode obter a seguinte solução:

  • Bloco 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");
  • Saída do Console:

    Clear data Button Clicked

Eu estava seguindo a discussão Como automatizar elementos DOM de sombra usando selênio? para trabalhar com elementos #shadow-root (open) .

Enquanto no processo de localização do botão Limpar dados dentro do pop-up Limpar dados de navegação , que aparece ao acessar a URL chrome://settings/clearBrowserData através do Selenium, não consigo localizar o seguinte elemento:

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

Instantâneo:

Usando o Selenium seguir estão meus testes de código e os erros associados encontrados:

  • Tentativa 1:

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

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

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

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

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

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

Incase se for útil, o bloco de código inicial (até a linha acima) funciona perfeito:

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 sem falhas.

Alguém pode me ajudar, por favor?


Eu estou querendo saber, se esta é uma janela filho separada, como é em primeiro plano e é por isso que não consigo encontrar o uielement na tela. Uma vez que você pegar o identificador de janela filho, você deve ser capaz de agarrar o uielement?


Se você está tentando obter o elemento 'Clear Data', então você pode usar o js abaixo para obter o elemento e então executar.

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')

Aqui está o script de amostra.

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();

Editar 2: Explicação

Problema: O Selenium não fornece suporte explícito para trabalhar com elementos Shadow DOM , pois eles não estão no dom atual. Essa é a razão pela qual NoSuchElementException exceção NoSuchElementException ao tentar acessar os elementos no shadow dom da shadow dom .

DOM da Sombra:

Nota: Estaremos nos referindo aos termos mostrados na foto. Então, por favor, vá até a foto para entender melhor.

Solução:

Para trabalhar primeiro com o elemento sombra , temos que encontrar o shadow host ao qual a sombra está conectada. Aqui está o método simples para obter a raiz da sombra com base no shadowHost.

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

E então você pode acessar o elemento da árvore de sombra usando o 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"));

Para simplificar todas as etapas acima, crie o método abaixo.

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

Agora você pode obter o elemento ShadowTree com uma única chamada de método

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

E execute as operações como de costume, como .click() , .getText() .

shadowTreeElement.click()

Isso parece simples quando você tem apenas um nível de sombra DOM. Mas aqui, neste caso, temos vários níveis de dom de sombra. Então nós temos que acessar o elemento, atingindo cada host sombra e root.

Abaixo está o trecho usando os métodos mencionados acima (getShadowElement e 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();

Você pode alcançar todos os passos acima em uma única chamada js, como mencionado no início da resposta (adicionado abaixo apenas para reduzir a confusão).

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 tela:





shadow-dom