java.lang.ClassCastException:无法使用executeScript()从shadowHost返回shadowRoot转换为org.openqa.selenium.WebElement

问题描述 投票:0回答:3

遇到问题:

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
,请需要帮助

java selenium casting css-selectors shadow-dom
3个回答
0
投票

您需要注意以下几件事:

  • 现在 Chrome 通过驱动程序支持影子根,
    shadowRoot
    JS 调用将根据规范返回影子根元素键 (
    shadow-6066-11e4-a52e-4f735466cecf
    )。 有一个新的
    ShadowRoot
    类来支持这一点,但它不包括我们之前在执行脚本调用返回元素时所做的翻译代码。此问题已修复,并将在 Selenium v4.1 中提供。唯一的区别是您需要投射到
    ShadowRoot
    而不是
    WebElement
  • 实际情况是,JavaScript 的返回值在 ChromeDriver v96 中发生了更改,以便符合 w3c 标准。 Selenium 3.141.59 无法解析这个新的返回值。您可以在 Selenium 4 中使用
    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']"));

TL;博士

一些有用的讨论:


0
投票

我使用的是 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.");

0
投票

尝试使用以下

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;
}
© www.soinside.com 2019 - 2024. All rights reserved.