暗影支持硒

问题描述 投票:6回答:7

我正在研究一个广泛使用阴影dom的自动化项目。所以每次使用execute_script函数来访问shadow root

例如。

root = driver.execute_script('return document.querySelector(".flex.vertical.layout").shadowRoot')

然后使用root访问其中的元素。由于我们在很多层面都有影子根源,这让我很烦恼。是否有比这更好的解决方案来访问阴影根中的元素?

我正在使用Chrome 2.20驱动程序。

selenium selenium-chromedriver shadow-dom
7个回答
4
投票

通过谷歌搜索我找到了另一个解决这个问题的方法 - 使用“/ deep / combinator”。

例如,我能够通过访问所有阴影根元素

driver.find_elements_by_css_selector( '体/深/ .layout.horizo​​ntal.center')

无论它具有多少阴影根,它都可以访问具有复合类名称“layout horizo​​ntal center”的元素。

但这只适用于Chrome驱动程序,我看到注释“/ deep /”是一种弃用的方法。


3
投票

关于Shadow DOM的WebDriver规范仍然是doesn't have anything specific to say

也不是Selenium project pages - 这是可以理解的,因为它们严格遵循规范。然而,存在some low-level code

因此,简短的回答是:不,目前在Selenium的WebDriver API或实现代码中,规范中没有特别的支持。

是的,该功能似乎存在于ChromeDriver 2.14中(作为Chrome的包装)。但是,据我所知,没有Selenium或WebDriver级别的绑定可以让你使用它。

但是有关更多细节和可能的解决方法,请参阅:Accessing Shadow DOM tree with Selenium,还有:Accessing elements in the shadow DOM,尤其是:Finding elements in the shadow DOM


2
投票

您可以编写扩展方法来操作IWebElement以扩展根目录,如下所示。

        public static class SeleniumExtension
{
    public static IWebElement ExpandRootElement(this IWebElement element, IWebDriver driver)
    {
        return (IWebElement)((IJavaScriptExecutor)driver)
                .ExecuteScript("return arguments[0].shadowRoot", element);
    }
}

您可以使用上面的扩展方法遍历元素层次结构以到达intrest的元素。

        By downloads_manager_ShadowDom= By.TagName("downloads-manager");
        By downloadToolBarShadowDom = By.CssSelector("downloads-toolbar");
        By toolBarElement = By.CssSelector("cr-toolbar");

    IWebElement ToolBarElement = driver.FindElement(downloads_manager_ShadowDom).ExpandRootElement(driver)
                          .FindElement(downloadToolBarShadowDom).ExpandRootElement(driver)
                          .FindElement(toolBarElement);

1
投票

试图在Chrome上实现这种自动化我想出了一个不明智的解决方案,使用以下方法递归搜索每个阴影dom:

driver.executeScript(scriptToRun, cssSelector);

这是javascript(以字符串形式传递):

  function recursiveSearch(element, target) {
    let result = element.querySelector(target);
    if (result) { return result; }
    let subElements = element.querySelectorAll("*");
    for (let i = 0; i < subElements.length; i++) {
      let subElement = subElements[i];
      if (subElement && subElement.shadowRoot) {
        let result = recursiveSearch(subElement.shadowRoot, target);
        if (result) return result;
      }
    }
  }
  return recursiveSearch(document, arguments[0]);

由于shadowRoot的内容最初可能为空,因此可以使用driver.waituntil.elementIsVisible来避免返回null元素。

异步示例:

    return await driver.wait(until.elementIsVisible(await driver.wait(async () => {
    return await driver.executeScript(scriptToRun, cssSelector);
  }, timeOut)));

另外

我之前的解决方案是明确地使用阴影遍历元素,但不那么自主。与上面相同,但使用此脚本:

  let element = document.querySelector(arguments[0][0]);
  let selectors = arguments[0].slice(1);
  for (i = 0; i < selectors.length; i++) {
    if (!element || !element.shadowRoot) {return false;}
    element = element.shadowRoot.querySelector(selectors[i]);
  }
  return element;

selectors会是这样的:

['parentElement1', 'parentElement2', 'targetElement']

我发现在Firefox Quantum 57.0上运行我的自动化测试不会受到隐藏阴影doms的影响,并且可以通过简单的方式找到任何元素:

driver.findElement(searchQuery);

0
投票

由于您经常使用,您可以创建一个函数,然后上面变为:

def select_shadow_element_by_css_selector(selector):
  running_script = 'return document.querySelector("%s").shadowRoot' % selector
  element = driver.execute_script(running_script)
  return element

shadow_section = select_shadow_element_by_css_selector(".flex.vertical.layout")
shadow_section.find_element_by_css(".flex")

在结果元素上,您可以放置​​任何方法:

find_element_by_id
find_element_by_name
find_element_by_xpath
find_element_by_link_text
find_element_by_partial_link_text
find_element_by_tag_name
find_element_by_class_name
find_element_by_css_selector

要查找多个元素(这些方法将返回一个列表):

find_elements_by_name
find_elements_by_xpath
find_elements_by_link_text
find_elements_by_partial_link_text
find_elements_by_tag_name
find_elements_by_class_name
find_elements_by_css_selector

稍后编辑:

有时阴影主机元素隐藏了阴影树,这就是为什么最好的方法是使用selenium选择器来查找阴影主机元素并注入脚本只是为了获取阴影根:

def expand_shadow_element(element):
  shadow_root = driver.execute_script('return arguments[0].shadowRoot', element)
  return shadow_root

#the above becomes 
shadow_section = expand_shadow_element(find_element_by_tag_name("neon-animatable"))
shadow_section.find_element_by_css(".flex")

为了说明这一点,我刚刚在Chrome的下载页面添加了一个可测试的示例,单击搜索按钮需要打开3个嵌套的阴影根元素:

import selenium
from selenium import webdriver
driver = webdriver.Chrome()


def expand_shadow_element(element):
  shadow_root = driver.execute_script('return arguments[0].shadowRoot', element)
  return shadow_root

selenium.__file__
driver.get("chrome://downloads")
root1 = driver.find_element_by_tag_name('downloads-manager')
shadow_root1 = expand_shadow_element(root1)

root2 = shadow_root1.find_element_by_css_selector('downloads-toolbar')
shadow_root2 = expand_shadow_element(root2)

root3 = shadow_root2.find_element_by_css_selector('cr-search-field')
shadow_root3 = expand_shadow_element(root3)

search_button = shadow_root3.find_element_by_css_selector("#search-button")
search_button.click()

0
投票

也许你可以使用IJavaScriptExecutor?

IWebDriver driver;
IJavaScriptExecutor jsExecutor = (IJavaScriptExecutor)driver;
jsExecutor.ExecuteScript('yourShadowDom.func()');

0
投票

不确定它适用于所有浏览器,但对我来说::shadow在chromedriver 2.38中运行良好例如:

div::shadow div span::shadow a
© www.soinside.com 2019 - 2024. All rights reserved.