遇到问题:
java.lang.ClassCastException: com.google.common.collect.Maps$TransformedEntriesMap cannot be cast to org.openqa.selenium.WebElement
代码试验:
JavascriptExecutor execute = (JavascriptExecutor)uiDriver.webDr;
WebElement shadowDomHostElement = uiDriver.webDr.findElement(By.cssSelector("authoring-ui[ng-version='12.0.1']"));
WebElement executeshadow = (WebElement)execute.executeScript("return arguments[0].shadowRoot", shadowDomHostElement);
无法投射
WebElement
,请需要帮助
您需要注意以下几件事:
shadowRoot
JS 调用将根据规范返回影子根元素键 (shadow-6066-11e4-a52e-4f735466cecf
)。 selenium4 有一个新的 ShadowRoot
类来支持这一点,但它不包括我们之前在执行脚本调用返回元素时所做的翻译代码。此问题已修复,并将在 Selenium v4.1 中提供。唯一的区别是您需要投射到 ShadowRoot
而不是 WebElement
。getShadowRoot()
,或者在 Selenium 4.1 中您将能够获得从 JS 返回的 ShadowRoot
实例。来自文档
package org.openqa.selenium.remote;
import org.openqa.selenium.By;
import org.openqa.selenium.SearchContext;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.WrapsDriver;
import org.openqa.selenium.internal.Require;
import java.util.List;
import java.util.Map;
import static java.util.Collections.singletonMap;
import static org.openqa.selenium.remote.Dialect.W3C;
import static org.openqa.selenium.remote.DriverCommand.FIND_ELEMENTS_FROM_SHADOW_ROOT;
import static org.openqa.selenium.remote.DriverCommand.FIND_ELEMENT_FROM_SHADOW_ROOT;
// Note: we want people to code against the SearchContext API, so we keep this class package private
class ShadowRoot implements SearchContext, WrapsDriver {
private final RemoteWebDriver parent;
private final String id;
ShadowRoot(RemoteWebDriver parent, String id) {
this.parent = Require.nonNull("Owning remote webdriver", parent);
this.id = Require.nonNull("Shadow root ID", id);
}
@Override
public List<WebElement> findElements(By by) {
return parent.findElements(
this,
(using, value) -> FIND_ELEMENTS_FROM_SHADOW_ROOT(id, using, String.valueOf(value)),
by);
}
@Override
public WebElement findElement(By by) {
return parent.findElement(
this,
(using, value) -> FIND_ELEMENT_FROM_SHADOW_ROOT(id, using, String.valueOf(value)),
by);
}
@Override
public WebDriver getWrappedDriver() {
return parent;
}
public String getId() {
return this.id;
}
private Map<String, Object> toJson() {
return singletonMap(W3C.getShadowRootElementKey(), id);
}
}
示例代码:
WebElement shadowHost = seleniumWebDriver.findElement(By.cssSelector("#shadowrootcontainer"));
JavascriptExecutor javascriptExecutor = (JavascriptExecutor) seleniumWebDriver;
SearchContext shadowRoot = (SearchContext) javascriptExecutor.executeScript("return arguments[0].shadowRoot", shadowHost);
WebElement shadowContent = shadowRoot.findElement(By.cssSelector("#shadowcontentcss"));
您修改后的代码块将是:
WebElement shadowDomHostElement = uiDriver.webDr.findElement(By.cssSelector("authoring-ui[ng-version='12.0.1']"));
JavascriptExecutor execute = (JavascriptExecutor)uiDriver.webDr;
SearchContext shadowRoot = (SearchContext) execute.executeScript("return arguments[0].shadowRoot", shadowDomHostElement);
WebElement executeshadow = shadowRoot.webDr.findElement(By.cssSelector("authoring-ui[ng-version='12.0.1']"));
一些有用的讨论:
我使用的是 selenium 4,所以它接受 SearchContext 而不是 WebElement 。
WebElement shadowDomOne = (WebElement) js.executeScript("return arguments[0].shadowRoot", shadowHost);
所以我必须使用
SearchContext shadowDomOne = (SearchContext) js.executeScript("return arguments[0].shadowRoot", shadowHost);
请参阅此了解更多信息 - https://www.youtube.com/watch?v=IbjHhi7hcpI&ab_channel=SelectorsHub
此视频还有助于了解 Shadow DOM。 https://www.youtube.com/watch?v=bpzyjNZ0jaw&t=632s&ab_channel=SDET-QAAutomationTechie
对我有用的完整代码如下。
driver.manage().timeouts().implicitlyWait(20, TimeUnit.SECONDS);
// Navigate to the demoqa website
driver.get("https://books-pwakit.appspot.com/");
//This is th shadow host. Root element
WebElement shadowHost = driver.findElement(By.tagName("book-app"));
JavascriptExecutor js = (JavascriptExecutor) driver;
// WebElement shadowDomOne = (WebElement) js.executeScript("return arguments[0].shadowRoot", shadowHost);
SearchContext shadowDomOne = (SearchContext) js.executeScript("return arguments[0].shadowRoot", shadowHost);
// WebElement appHeader=shadowDomOne.findElement(By.tagName("app-header")); because of https://github.com/SeleniumHQ/selenium/issues/10025
WebElement appHeader=shadowDomOne.findElement(By.cssSelector("app-header"));// So had to use css , here passes tag name.
WebElement app_toolbar=appHeader.findElement(By.cssSelector("app-toolbar.toolbar-bottom"));
WebElement book_input_decorator=app_toolbar.findElement(By.tagName("book-input-decorator"));
WebElement search=book_input_decorator.findElement(By.cssSelector("input#input"));
search.sendKeys("At last we made it.");
尝试使用以下
getShadowRootElement()
方法来获取Shadow-root的WebElement。
public WebElement getShadowRootElement(WebElement shadowHost) {
WebElement returnObj = null;
Object shadowRoot = ((JavascriptExecutor) driver).executeScript("return arguments[0].shadowRoot", shadowHost);
if (shadowRoot instanceof WebElement) {
// ChromeDriver 95
returnObj = (WebElement) shadowRoot;
} else if (shadowRoot instanceof Map) {
// ChromeDriver 96+
Map<String, Object> shadowRootMap = (Map<String, Object>) shadowRoot;
String shadowRootKey = (String) shadowRootMap.keySet().toArray()[0];
String id = (String) shadowRootMap.get(shadowRootKey);
RemoteWebElement remoteWebElement = new RemoteWebElement();
remoteWebElement.setParent((RemoteWebDriver) driver);
remoteWebElement.setId(id);
returnObj = remoteWebElement;
} else {
System.out.println("Unexpected return type for shadowRoot in getShadowRootElement()");
}
return returnObj;
}