Я использую проект Java Selenium для автоматизации веб-страниц. Веб-страница содержит множество многоуровневых элементов DOM с теневым корнем, с которыми я не могу взаимодействовать, используя метод селена findElement
.
Если вам известно какое-либо другое решение, кроме перечисленных выше, которое я могу реализовать в среде Selenium Java, пожалуйста, передайте решение. Заранее спасибо !.
Чтобы продемонстрировать автоматизация из тень ДОМ с использованием Селен v3.x, ChromeDriver v2.46 и Chrome v73.x, вот несколько подходов, которые открывают URL-адрес chrome://downloads/
и с помощью метода executeScript()
отправляет последовательность символовпдф как текст поиска в Поисковая строка.
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
Согласно обсуждению в Определите судьбу экспериментального комбинатора '>>>', комбинатор >>>
, который был заменой комбинатору /deep/
для прокалывания всех границ теневого DOM в стиле, который был реализован за флагом в Blink, устарел.
Есть очень хороший плагин, который можно использовать с селеновым проектом теневая автоматизация-селен. Это помогает писать намного лучший, читаемый и удобный код. Используя это, вы можете получить доступ к многоуровневому теневому 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)
: используйте это, чтобы выбрать элемент раскрывающегося списка с помощью метки (используйте это, если присутствует или загружен только один раскрывающийся список в пользовательском интерфейсе).
void selectDropdown(WebElement parentElement, String label)
: используйте это, чтобы выбрать элемент выпадающего списка из родительского DOM с помощью метки.
Как использовать этот плагин: Вам придется зависеть в вашем проекте.
Мавен
<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.
import io.github.sukgu.*;
Shadow shadow = new Shadow(driver);
WebElement element = shadow.findElement("properties-page#settingsPage>textarea#textarea");
String text = element.getText();
Шаги, чтобы узнать теневые элементы DOM с помощью JSExecutor и CSS:
Найдите базовый элемент, т.е. родительский элемент корневого элемента Shadow.
Получить теневой корень этого элемента.
И найди свой элемент на этом теневом корневом вебементе
пример :
<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, которые находятся внутри теневого корня, 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
С Selenium 4 теперь есть WebElement.getShadowRoot()
. Например:
driver.findElement(By.id("parentId")).getShadowRoot().findElement(By.cssSelector( "label" )).findElement(By.tagName("input"))
Как обычно с #shadow-root
, выбор навигации для следующего прыжка ограничен. Например. против Chrome By.cssSelector()
и By.className()
действительны, но By.id()
и By.tagName()
не работают с org.openqa.selenium.InvalidArgumentException: invalid argument: invalid locator
Добро пожаловать в StackOverflow! Спасибо за ваш подробный ответ. Пожалуйста, поддержите его ссылками. У нас есть рекомендации по Как написать хороший ответ.