Как автоматизировать теневые элементы 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:
Узнайте базовый элемент, то есть родительский элемент теневого корневого элемента.
Получить теневой корень этого элемента.
И найдите свой элемент в этом теневом корневом 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
- Снимок браузера:
Завершение
Согласно обсуждению в Определение судьбы экспериментального комбинатора ">>>" >>>
combinator, который был заменой /deep/
combinator для преобразования всех границ shadow DOM в style, который был реализован за флагом в Blink, устарел.