2

Getting Issue :

java.lang.ClassCastException: com.google.common.collect.Maps$TransformedEntriesMap cannot be cast to org.openqa.selenium.WebElement

Code trials:

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);

Unable to cast the WebElement, please need help on this

undetected Selenium
  • 183,867
  • 41
  • 278
  • 352
Asha Rani
  • 31
  • 1
  • 2

3 Answers3

0

You need to take care of a couple of things:

  • Now that Chrome supports shadow root via the driver, shadowRoot JS calls are returned per the spec with a shadow root element key (shadow-6066-11e4-a52e-4f735466cecf). has a new ShadowRoot class to support this, but it didn't include the translation code that we earlier did when execute script calls return elements. This has been fixed and will be available in Selenium v4.1. The only difference is that you'll need to cast to ShadowRoot instead of WebElement.
  • The actual state is that the return value of that JavaScript changed in v96 of ChromeDriver in order to be w3c compliant. Selenium 3.141.59 can not parse this new return value. You can use getShadowRoot() in Selenium 4, or you'll be able to get a ShadowRoot instance returned from the JS in Selenium 4.1.

Implementation

From the documentation

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);
  }
}

Example Code:

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"));

This Usecase

Your modified code block will be:

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; DR

Some helpful discussions:

undetected Selenium
  • 183,867
  • 41
  • 278
  • 352
0

I am using selenium 4, so instead of WebElement , it accept SearchContext.

WebElement shadowDomOne = (WebElement) js.executeScript("return arguments[0].shadowRoot", shadowHost);

So I had to use

   SearchContext shadowDomOne = (SearchContext) js.executeScript("return arguments[0].shadowRoot", shadowHost);

Refer this for more information-https://www.youtube.com/watch?v=IbjHhi7hcpI&ab_channel=SelectorsHub

Also this videos helps to get an idea about shadow DOM. https://www.youtube.com/watch?v=bpzyjNZ0jaw&t=632s&ab_channel=SDET-QAAutomationTechie

Full code that worked for me is as per below.

  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.");
Sameera De Silva
  • 1,722
  • 1
  • 22
  • 41
0

Try using the following getShadowRootElement() method to get the WebElement of the Shadow-root.

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;
}