Доступ к теневому дереву DOM с селеном



Можно ли получить доступ к элементам в теневом DOM с помощью Selenium / Chrome webdriver?



Использование обычных методов поиска элементов не работает, как и следовало ожидать. Я видел ссылки на спецификациюswitchToSubTree на w3c, но не смог найти никаких реальных документов, примеров и т. д.



Кто-нибудь имел успех с этим?

777   7  

7 ответов:

К сожалению, похоже, что спецификация webdriver пока не поддерживает это.

Мой шпионаж раскрыт:

Http://www.w3.org/TR/webdriver/#switching-to-hosted-shadow-doms

Https://groups.google.com/forum/#! msg / selenium-developers / Dad2KZsXNKo / YXH0e6eSHdAJ

Принятый ответ больше не действителен, а некоторые другие ответы имеют некоторые недостатки или не являются практическими (селектор /deep/ не работает и не рекомендуется, document.querySelector('').shadowRoot работает только с первым теневым элементом, когда теневые элементы вложены), иногда теневые корневые элементы вложены, а второй теневой корень не виден в корне документа, но доступен в его родительском доступном теневом корне. Я думаю, что лучше использовать селекторы селена и ввести сценарий, чтобы просто взять теневой корень:

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

outer = expand_shadow_element(driver.find_element_by_css_selector("#test_button"))
inner = outer.find_element_by_id("inner_button")
inner.click()

Чтобы представить это в перспективе, я просто добавил тестируемый пример со страницей загрузки Chrome, нажав на кнопку поиска, нужно открыть 3 вложенных элемента shadow root: Введите описание изображения здесь

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

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

Выполнение того же подхода, предложенного в других ответах, имеет тот недостаток, что он жестко кодирует запросы, менее удобочитаем, и вы не можете использовать промежуточные выборки для других действий:

search_button = driver.execute_script('return document.querySelector("downloads-manager").shadowRoot.querySelector("downloads-toolbar").shadowRoot.querySelector("cr-search-field").shadowRoot.querySelector("#search-button")')
search_button.click()

Следует также отметить, что драйвер Selenium binary Chrome теперь поддерживает Shadow DOM (начиная с января 28, 2015) : http://chromedriver.storage.googleapis.com/2.14/notes.txt

Я использую C# и Selenium и сумел найти элемент внутри скрытого теневого DOM с помощью java-скрипта. Это мое html дерево:

Html дерево

Мне нужен url в последней строке, и чтобы получить его, я сначала выбираю тег" downloads-manager", а затем первый теневой корень прямо под ним. Оказавшись внутри теневого корня, я хочу найти элемент, ближайший к следующему теневому корню. Этот элемент называется "загрузка-элемент". С этим выбранным я могу войти во второй теневой корень. От там я выберите элемент Гуи, содержащие URL-адрес, Идентификатор = "файл"- "значок". Наконец, я могу получить атрибут "src", который содержит url, который я ищу.

Две строки кода C#, которые делают трюк:

IJavaScriptExecutor jse2 = (IJavaScriptExecutor)_driver;
var pdfUrl = jse2.ExecuteScript("return document.querySelector('downloads-manager').shadowRoot.querySelector('downloads-item').shadowRoot.getElementById('file-icon').getAttribute('src')");

Обычно вы делаете это:

element = webdriver.find_element_by_css_selector(
    'my-custom-element /deep/ .this-is-inside-my-custom-element')
И, надеюсь, это будет продолжать работать.
Однако обратите внимание, что /deep/ и ::shadow являются устаревшими (и не реализованы в браузерах, отличных от Opera и Chrome). Есть много разговоров о том, чтобы включить их в статический профиль. То есть запрос на них будет работать, но не стилизация.

Если вы не хотите полагаться на /deep/ или ::shadow, потому что их будущее немного неопределенно, или потому что вы хотите лучше работать в кросс-браузере или поскольку вы ненавидите предупреждения об осуждении, радуйтесь, поскольку есть другой способ:

# Get the shadowRoot of the element you want to intrude in on,
# and then use that as your root selector.
shadow_root = webdriver.execute_script('''
    return document.querySelector(
        'my-custom-element').shadowRoot;
    ''')
element = shadow_root.find_element_by_css_selector(
    '.this-is-inside-my-custom-element')

Подробнее об этом:

Я нашел гораздо более простой способ получить элементы из Shadow Dom. Я беру тот же пример, приведенный выше, для значка поиска из страницы загрузки Chrome.

IWebDriver driver;

public IWebElement getUIObject(params By[] shadowRoots)
        {
            IWebElement currentElement = null;
            IWebElement parentElement = null;
            int i = 0;
            foreach (var item in shadowRoots)
            {
                if (parentElement == null)
                {
                    currentElement = driver.FindElement(item);
                }
                else
                {
                    currentElement = parentElement.FindElement(item);
                }
                if(i !=(shadowRoots.Length-1))
                {
                    parentElement = expandRootElement(currentElement);
                }
                i++;
            }
            return currentElement;
        }

 public IWebElement expandRootElement(IWebElement element)
        {
            IWebElement rootElement = (IWebElement)((IJavaScriptExecutor)driver)
        .ExecuteScript("return arguments[0].shadowRoot", element);
            return rootElement;
        }

Страница Загрузки Google Chrome

Теперь, как показано на рисунке, мы должны развернуть три теневых корневых элемента, чтобы получить наш значок поиска. Чтобы нажать на иконку, все, что нам нужно сделать, это : -

  [TestMethod]
        public void test()
        {
           IWebElement searchButton= getUIObject(By.CssSelector("downloads-manager"),By.CssSelector("downloads-toolbar"),By.Id("search-input"),By.Id("search-buton"));
            searchButton.Click();
        }

Так что только одна строка даст вам ваш веб-элемент, просто нужно убедиться, что вы проходите сначала теневой корневой элемент в качестве первого аргумента функции "getUIObject" второй теневой корневой элемент в качестве второго аргумента функции и так далее, наконец, последним аргументом для функции будет идентификатор вашего фактического элемента (для этого случая его 'поиск-кнопка')

Это сработало для меня (используя привязки Selenium javascript):

driver.executeScript("return $('body /deep/ <#selector>')")

, который возвращает элемент(Ы), который вы ищете.

Comments

    Ничего не найдено.