Вопрос-ответ

How to automate shadow DOM elements using selenium?

Как автоматизировать теневые элементы DOM с помощью selenium?

Я использую Java Selenium project для автоматизации веб-страницы. Веб-страница содержит множество многоуровневых элементов DOM с теневым корнем, с которыми я не могу взаимодействовать с помощью метода selenium findElement.

Я пробовал следующие решения:


  • глубокий css (не работает в последней версии браузера Chrome)

  • JS Executor. (Это действительно утомительно и становится сложным в обслуживании)

Примечание:

Если вы знаете какое-либо другое решение, отличное от перечисленных выше, которое я могу реализовать в Selenium Java Framework, пожалуйста, передайте решение. Заранее спасибо !.

Переведено автоматически
Ответ 1

Есть очень хороший плагин, который можно использовать с проектом selenium shadow-automation-selenium. Это помогает в написании гораздо лучшего, читаемого и поддерживаемого кода. С помощью этого вы можете получить доступ к многоуровневому shadow 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) : используйте это, чтобы выбрать элемент checkbox с помощью label.

void selectCheckbox(WebElement parentElement, String label) : используйте это, чтобы выбрать элемент checkbox с помощью label.

void selectRadio(String label) : используйте это для выбора радиоэлемента с помощью label.

void selectRadio(WebElement parentElement, String label) : используйте это, чтобы выбрать радиоэлемент из родительского DOM с помощью label.

void selectDropdown(String label) : используйте это для выбора элемента выпадающего списка с помощью метки (используйте это, если только один выпадающий список присутствует или загружен в пользовательский интерфейс).

void selectDropdown(WebElement parentElement, String label) : используйте это, чтобы выбрать элемент выпадающего списка из родительского DOM с помощью label.

Как использовать этот плагин: вам придется установить зависимость в вашем проекте.

Maven

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

для html-тега, который находится под теневым корневым элементом dom

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

Вы можете использовать этот код в своем фреймворке для захвата объекта textarea element.

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

Теперь есть Selenium 4 WebElement.getShadowRoot(). Например:

driver.findElement(By.id("parentId")).getShadowRoot().findElement(By.cssSelector("label")).findElement(By.tagName("input"))

Как обычно для a #shadow-root, варианты навигации для следующего перехода ограничены. Например. для Chrome By.cssSelector() и By.className() допустимы, но By.id() и By.tagName() не работают с org.openqa.selenium.InvalidArgumentException: invalid argument: invalid locator

Ответ 3

Шаги для определения теневых элементов DOM с помощью JSExecutor и CSS:


  1. Узнайте базовый элемент, то есть родительский элемент теневого корневого элемента.



  2. Получить теневой корень этого элемента.



  3. И найдите свой элемент в этом теневом корневом 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;
}

# Шаг 1 для примера, т.е. Найти базовый элемент:

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

#Шаг2

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

# Шаг3 - Нам нужно найти элементы с помощью CSS Selector, которые находятся внутри shadow root, xpath здесь не будет работать

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

Чтобы продемонстрировать автоматизацию shadow DOM с использованием Selenium версии v3.x, ChromeDriver версии v2.46 и Chrome версии 73.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


  • Снимок браузера:

ShadowDOM


Завершение

Согласно обсуждению в Определение судьбы экспериментального комбинатора ">>>" >>> combinator, который был заменой /deep/ combinator для преобразования всех границ shadow DOM в style, который был реализован за флагом в Blink, устарел.

2023-11-29 22:24 javascript java selenium