Shadow House Support for Selenium

I am working on an automation project that makes extensive use of the shadow house. Therefore, each time, using the execute_script function to access the shadow root

eg.

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

Then use the root to access the elements inside. Since we have shadow roots at many levels, this annoys me a lot. Is there a better solution for accessing items in the shadow root?

I am using the Chrome 2.20 driver.

+7
source share
7 answers

By googling, I found another workaround for this problem that uses "/ deep / combinator".

for example, I was able to access all the elements of the shadow roots on

driver.find_elements_by_css_selector ( '//.layout.horizontal.center')

" " , .

-, "/deep/" - .

+5

WebDriver Shadow DOM.

- , . , .

, : , Selenium WebDriver API .

, , ChromeDriver 2.14 ( Chrome). , , Selenium WebDriver, .

. : DOM Della Selenium, : DOM : DOM

+3

Chrome, :

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.wait until.elementIsVisible, .

Async:

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

, , . , , script:

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

Sidenote

, Firefox Quantum 57.0 , :

driver.findElement(searchQuery);
+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);
    }
}

, , .

        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);
+2

, :

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

:

- - , - - script , ::

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

To introduce this in perspective, I just added a test case to the Chrome download page, by clicking the search button, I need to open 3 nested shadow root elements:

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
source

Maybe you can use IJavaScriptExecutor?

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

Not sure if it works in all browsers, but ::shadowworks fine for me in chromedriver 2.38 For example:

div::shadow div span::shadow a
0
source

All Articles