【问题标题】:How to automate shadow DOM elements using selenium?如何使用 selenium 自动化影子 DOM 元素?
【发布时间】:2019-09-09 17:20:39
【问题描述】:

我正在使用 Java Selenium 项目进行网页自动化。该网页包含许多多级 shadow-root DOM 元素,我无法使用 selenium findElement 方法与之交互。

我尝试了以下解决方案:

  • deep css(不适用于最新的 chrome 浏览器)
  • JS 执行器。 (这真的很繁琐,维护起来也很复杂)

注意:

如果您知道我可以在 Selenium Java 框架中实现的除上面列出的任何其他解决方案,请提供解决方案。提前致谢!

【问题讨论】:

    标签: javascript java selenium polymer shadow-dom


    【解决方案1】:

    有一个很好的插件可以和selenium项目shadow-automation-selenium一起使用。它有助于编写更好、可读和可维护的代码。使用它,您可以访问多级影子 DOM(最多 4 级)。这使用简单的 css 选择器来识别元素。

    WebElement findElement(String cssSelector) : 如果想要 DOM 中的单个元素,请使用此方法

    List<WebElement> findElements(String cssSelector) :如果你想从 DOM 中查找所有元素,请使用它

    WebElement findElements(WebElement parent, String cssSelector) :如果你想从父对象 DOM 中找到单个元素,请使用它

    List<WebElement> findElements(WebElement parent, String cssSelector) :如果您想从父对象 DOM 中查找所有元素,请使用它

    WebElement getShadowElement(WebElement parent,String selector) :如果你想从父 DOM 中查找单个元素,请使用它

    List<WebElement> getAllShadowElement(WebElement parent,String selector) :如果您想从父 DOM 中查找所有元素,请使用它

    boolean isVisible(WebElement element) :如果您想查找元素的可见性,请使用它

    boolean isChecked(WebElement element) : 如果你想检查复选框是否被选中,请使用它

    boolean isDisabled(WebElement element) :如果你想检查元素是否被禁用,请使用它

    String getAttribute(WebElement element,String attribute) :如果你想获得像 aria-selected 这样的属性和元素的其他自定义属性,请使用它。

    void selectCheckbox(String label) : 使用它来使用标签选择复选框元素。

    void selectCheckbox(WebElement parentElement, String label) : 使用它来使用标签选择复选框元素。

    void selectRadio(String label) : 使用它来使用标签选择单选元素。

    void selectRadio(WebElement parentElement, String label) : 使用它来使用标签从父 DOM 中选择单选元素。

    void selectDropdown(String label) :使用它来使用标签选择下拉列表项(如果 UI 上只有一个下拉列表存在或加载,则使用它)。

    void selectDropdown(WebElement parentElement, String label) :使用它来使用标签从父 DOM 中选择下拉列表项。

    如何使用这个插件: 您将不得不依赖于您的项目。

    Maven

    <dependency>
      <groupId>io.github.sukgu</groupId>
      <artifactId>automation</artifactId>
      <version>0.0.4</version>
    <dependency>
    

    用于位于 shadow-root dom 元素下的 html 标记

    <properties-page id="settingsPage"> 
      <textarea id="textarea">
    </properties-page>
    

    您可以在您的框架中使用此代码来获取 textarea 元素对象。

      import io.github.sukgu.*;
      Shadow shadow = new Shadow(driver);
      WebElement element = shadow.findElement("properties-page#settingsPage>textarea#textarea");
      String text = element.getText();
    

    【讨论】:

    • 欢迎来到 StackOverflow!感谢您的详细回复。请参考支持。我们有关于 How to write a good answer 的指导方针。
    【解决方案2】:

    使用 JSExecutor 和 CSS 找出 shadow DOM 元素的步骤:

    1. 找出基础元素,即影子根元素的父元素。

    2. 获取该元素的影子根。

    3. 然后在那个 shadow-root webelement 上找到你的元素

      示例:

    <div id="example">
    #shadow-root
    <div id="root" part="root">
       <div id="label" part="label">ShadowRootLabel</div>
    </div>
    </ptcs-label>

    #找出阴影根元素的方法

    public WebElement getShadowRootElement(WebElement element) {
    WebElement ele = (WebElement) ((JavascriptExecutor)driver)
        .executeScript("return arguments[0].shadowRoot", element);
            return ele;
        }
    

    #Step1 示例,即查找基本元素:

    WebElement root1 = driver.findElement(By.id("example"));
    

    #Step2

    //Get shadow root element
    WebElement shadowRoot1 = getShadowRootElement(root1);
    

    #Step3 - 我们需要使用 CSS Selector 查找位于 shadow root 内的元素,xpath 在这里不起作用

    //Here we will get Element inside Shadow Dom Element
    WebElement shadowElement = shadowRoot3.findElement(By.cssSelector("div[id=label]"));
    

    【讨论】:

    • 看起来这不适用于 Chrome 96+。当调用 driver.executeScript("return arguments[0].shadowRoot", element) 时,它将返回一个 com.google.common.collect.Maps$TransformedEntriesMap,它不能转换为 WebElement。任何想法如何解决这个问题?
    • 您需要转换为SearchContext 或使用新的getShadowRoot() 方法:titusfortner.com/2021/11/22/shadow-dom-selenium.html
    【解决方案3】:

    使用 Selenium v​​3.xChromeDriver v2.46 演示 shadow DOM自动化 >Chrome v73.x 这里有一些方法可以打开 URL chrome://downloads/ 并使用 executeScript() 方法发送 字符序列 pdf 作为在搜索框搜索文本


    使用document.querySelector()

    作为规范方法,您可以使用document.querySelector() 方法,如下所示:

    • 代码块:

      import org.openqa.selenium.JavascriptExecutor;
      import org.openqa.selenium.WebDriver;
      import org.openqa.selenium.WebElement;
      import org.openqa.selenium.chrome.ChromeDriver;
      import org.openqa.selenium.chrome.ChromeOptions;
      
      public class shadow_DOM_search_download_querySelector {
      
          public static void main(String[] args)
          {
              System.setProperty("webdriver.chrome.driver", "C:\\Utility\\BrowserDrivers\\chromedriver.exe");
              ChromeOptions options = new ChromeOptions();
              options.addArguments("start-maximized");
              options.addArguments("disable-infobars");
              options.addArguments("--disable-extensions"); 
              WebDriver driver = new ChromeDriver(options);
              driver.get("chrome://downloads/");
              JavascriptExecutor jse = (JavascriptExecutor) driver; 
              WebElement search_box = (WebElement) jse.executeScript("return document.querySelector('downloads-manager').shadowRoot.querySelector('downloads-toolbar#toolbar').shadowRoot.querySelector('cr-toolbar#toolbar').shadowRoot.querySelector('cr-toolbar-search-field#search').shadowRoot.querySelector('div#searchTerm input#searchInput')");
              String js = "arguments[0].setAttribute('value','pdf')";
              ((JavascriptExecutor) driver).executeScript(js, search_box);
          }
      }
      

    同样的解决方案可以按如下方式逐步重写:

    • 代码块:

      import org.openqa.selenium.By;
      import org.openqa.selenium.JavascriptExecutor;
      import org.openqa.selenium.WebDriver;
      import org.openqa.selenium.WebElement;
      import org.openqa.selenium.chrome.ChromeDriver;
      import org.openqa.selenium.chrome.ChromeOptions;
      
      public class shadow_DOM {
      
          static WebDriver driver;
          public static void main(String[] args) 
          {   
              System.setProperty("webdriver.chrome.driver", "C:\\Utility\\BrowserDrivers\\chromedriver.exe");
              ChromeOptions options = new ChromeOptions();
              options.addArguments("start-maximized");
              //options.addArguments("disable-infobars");
              options.addArguments("--disable-extensions"); 
              driver = new ChromeDriver(options);
              driver.get("chrome://downloads/");
              WebElement root1 = driver.findElement(By.tagName("downloads-manager"));
              WebElement shadow_root1 = expand_shadow_element(root1);
      
              WebElement root2 = shadow_root1.findElement(By.cssSelector("downloads-toolbar#toolbar"));
              WebElement shadow_root2 = expand_shadow_element(root2);
      
              WebElement root3 = shadow_root2.findElement(By.cssSelector("cr-toolbar#toolbar"));
              WebElement shadow_root3 = expand_shadow_element(root3);
      
              WebElement root4 = shadow_root3.findElement(By.cssSelector("cr-toolbar-search-field#search"));
              WebElement shadow_root4 = expand_shadow_element(root4);
      
              WebElement search_term = shadow_root4.findElement(By.cssSelector("div#searchTerm input#searchInput"));
              String js = "arguments[0].setAttribute('value','pdf')";
              ((JavascriptExecutor) driver).executeScript(js, search_term);
      
              WebElement search_button = shadow_root4.findElement(By.cssSelector("paper-icon-button#icon"));
              search_button.click();
      
              System.out.println("Search Button Clicked");
          }
      
          public static WebElement expand_shadow_element(WebElement element)
          {
              WebElement shadow_root = (WebElement)((JavascriptExecutor)driver).executeScript("return arguments[0].shadowRoot", element);
              return shadow_root;
          }
      
      }
      

    • 控制台输出:

      Search Button Clicked
      

    • 浏览器快照:


    结尾

    根据Determine the fate of experimental '>>>' combinator 中的讨论,&gt;&gt;&gt; 组合器替代了 /deep/ 组合器,用于穿透所有阴影 DOM 边界以形成样式,在 Blink 的标志后面实现的已被弃用。

    【讨论】:

      猜你喜欢
      • 2022-12-12
      • 2019-11-15
      • 2017-06-05
      • 2021-05-28
      • 2018-11-04
      • 1970-01-01
      • 1970-01-01
      • 2018-08-31
      • 1970-01-01
      相关资源
      最近更新 更多