Reputation: 103
While using Selenium Webdriver in C#, I get an exception when trying to select an element that exists under Shadow DOM.
The exception I am getting is: NoSuchElementException
How would you suggest to use Selenium with Shadow DOM?
Thanks,
Michal
Upvotes: 2
Views: 11732
Reputation: 1017
Rules to find element inside Shadow DOM
You can also use SelectorsHub plugin to get the locator and sample code
Helper method 1
/// <summary>
/// Method 1 to fetch Shadow Root using Selenium's in build method
/// </summary>
/// <param name="shadowHostElement">IWebElement - Which is parent element of Shadow Root element. Also called Shadow Host</param>
/// <returns>Shadow Root element in the form of ISearchContext</returns>
public static ISearchContext GetShadowRootElement(IWebElement shadowHostElement)
{
ISearchContext shadowRootElement = shadowHostElement.GetShadowRoot();
return shadowRootElement;
}
Helper method 2
/// <summary>
/// Method 2 to fetch Shadow Root using Selenium's IJavaScriptExecutor
/// </summary>
/// <param name="driver">Instance of IWebDriver</param>
/// <param name="shadowHostElement">IWebElement - Which is parent element of Shadow Root element. Also called Shadow Host</param>
/// <returns>Shadow Root element in the form of ISearchContext</returns>
public static ISearchContext GetShadowRootElementUsingJavaScript(IWebDriver driver, IWebElement shadowHostElement)
{
IJavaScriptExecutor js = (IJavaScriptExecutor)driver;
ISearchContext shadowRootElement = (ISearchContext)js.ExecuteScript("return arguments[0].shadowRoot", shadowHostElement);
return shadowRootElement;
}
Sample Code - Website with Shadow DOM
//1. Finding shadow host first - normally using FindElement
IWebElement ShadowHostElement = Driver.FindElement(By.TagName("book-app"));
//2. Finding Shadow Root using Shadow Host element - use either one from below
ISearchContext shadowRoot = GetShadowRootElementUsingJavaScript(Driver, ShadowHostElement);
ISearchContext shadowRoot = GetShadowRootElement(ShadowHostElement);
IWebElement node = shadowRoot.FindElement(By.Id("input"));
node.SendKeys("Something");
Upvotes: 0
Reputation: 1
That gives an error Andre, I don't know what to do now: OpenQA.Selenium.JavaScriptException : javascript error: missing ) after argument list (Session info: MicrosoftEdge=104.0.1293.63)
changed it to:
var element = js.ExecuteScript("return document.querySelector('#shadow-root').shadowRoot.querySelector('#private-host');");
but that resulted in: OpenQA.Selenium.JavaScriptException : javascript error: Cannot read properties of null (reading 'shadowRoot')
Upvotes: 0
Reputation: 11
You can create a Method, that accepts the list of ShadowDom root locators, and builds js script to execute and get shadow element:
public static IWebElement GetElementFromShadowDom(this IWebDriver driver, params string[] selectors)
{
IJavaScriptExecutor js = (IJavaScriptExecutor)driver;
var scriptString = "return document.querySelector";
var selectorIndex = 0;
var stopIndex = selectors.Length - 1;
foreach (var selector in selectors)
{
var root = "('" + selector + "')";
root += (selectorIndex != stopIndex && selectors.Length != 1) ? ".shadowRoot.querySelector" : null;
selectorIndex++;
scriptString += root;
}
var webElement = (IWebElement)js.ExecuteScript(scriptString);
return webElement;
}
}
Upvotes: 1
Reputation: 156
A very good repository having Selenium shadow DOM interactions written as Java Binding : https://github.com/sukgu/shadow-automation-selenium
This repo uses a bunch of JQueries to expand and identify elements in shadow dom.Please have a look.Should be easy to port it to C#
Upvotes: 0
Reputation: 1
I had the same problem. I found some values in the
Inspect -> Properties -> value (it can be something else)
Try:
WebElement element = driver.findElement(By.cssSelector("div"));
System.out.println(element.getAttribute("value"));
Upvotes: 0
Reputation: 5637
Try to locate your element like this:
driver.FindElement(By.CssSelector('selector_otside_shadow_root /deep/ selector_inside_shadow_root'));
in your case it would be:
driver.FindElement(By.CssSelector('app-home /deep/ #itemName1'));
You can test this approach in chrome://downloads/
link with this css_selector
:
downloads-manager /deep/ downloads-item /deep/ [id=file-link]
in dev tools. As you can see there was needed to pass two shadow-root
elements, so make sure that you have only one shadow-root
element or use multiple /deep/
like in example above.
or you can use JavasciptExecutor like this:
IJavaScriptExecutor js = (IJavaScriptExecutor)_driver;
var element = js.ExecuteScript("return document.querySelector('selector_outside_shadow_root').shadowRoot.querySelector('selector_inside_shadow_root');");
Upvotes: 5