<script>
class MyElement extends HTMLElement {
connectedCallback() {
const shadow = this.attachShadow({ mode: "open" });
const button = document.createElement("button");
button.onclick = () => alert("Button clicked");
button.innerText = "Shadow DOM Button";
shadow.append(button);
customElements.define("my-element", MyElement);
</script>
<div><my-element></my-element></div>
Custom Element Code appended with Shadow DOM rendered on Browser >
Let’s try to find Custom Element appended with Shadow DOM through CSS, XPath, document interface, and shadowRoot interface >
Wow! EXCEPT FOR THE SHADOWROOT INTERFACE, NOTHING WORKS. That’s why we call the shadowRoot interface as an entry point to the Shadow DOM tree.
Custom Element Code Snippet WITH appended Shadow DOM {CLOSED Mode} >
<script>
class MyElement extends HTMLElement {
connectedCallback() {
const shadow = this.attachShadow({ mode: "open" });
const button = document.createElement("button");
button.onclick = () => alert("Button clicked");
button.innerText = "Shadow DOM Button";
shadow.append(button);
customElements.define("my-element", MyElement);
</script>
<div><my-element></my-element></div>
Custom Element Code appended with Shadow DOM rendered on Browser >
Let’s try to find Custom Element appended with Shadow DOM through CSS, XPath, document interface, and shadowRoot interface >
Oops! NOTHING WORKS even the shadowRoot interface failed.
Q: Do regular selectors {Xpath, CSS} work with Shadow DOM elements?
As we have seen above, legacy selectors including document interface do not work with Shadow DOM elements.
Q: What is the role of the Open/Closed mode of Shadow Root, while finding the elements?
The Shadow Root has two different modes – Open mode allows the shadow root to be accessible from JavaScript outside the shadow root but Closed mode does not. As we have seen above, shadowRoot interface is the only entry point to the Shadow DOM tree when the mode is OPEN. In the case of CLOSED mode, we can not access any Shadow DOM tree elements. In simple terms we can say – we can not automate CLOSED mode Shadow DOM tree elements.
If the CLOSED mode Shadow DOM tree is business-critical to automate, then we can ask our developers to keep the mode OPEN on the test environment sandbox, so that we can automate/run it. Developers still can keep CLOSED mode on the production environment.
The developers can make OPEN/CLOSED mode as configurable for automation engineers.
Q: How to interact with multi-level Shadow Root elements?
To see the multilevel Shadow DOM tree elements on your machine, get the source code from my GitHub repo, and execute locally. Whenever you run the code locally, it will open up a web application {implemented with Google’s Polymer library} on the browser and then you can see a multi-level Shadow DOM tree through the browser console.
Let’s try to find multi-level Custom Elements appended with Shadow DOM through shadowRoot interface >
Here we have used the shadowRoot interface twice – one inside another and finally we succeeded to find multi-level elements.
Q: Does TestProject OpenSDK support Shadow DOM tree elements?
Undoubtedly YES! To interact with Shadow DOM tree elements, you have to set up TestProject’s OpenSDK on your local machine. Then create a Maven Java project and add the following dependency {Here, we are using the Java binding}:
<dependency>
<groupId>io.testproject</groupId>
<artifactId>java-sdk</artifactId>
<version>0.64.0-RELEASE</version>
</dependency>
📍 The OpenSDK also provides bindings with Python and C#.
📍 More on the TestProject’s SDK and OpenSDK can be found here: TestProject SDK vs Open SDK?
After setting up Maven dependency, install & start TestProject Agent (it’s completely free, simply sign up here 😉) on your local machine and also get a token from your TestProject’s account. Now all prerequisites are done and you are ready to run the following automation script on your maven project by using TestProject’s OpenSDK.
Multi-level Shadow DOM tree automation code with TestProject’s OpenSDK >
import io.testproject.sdk.drivers.web.ChromeDriver;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.Keys;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeOptions;
public class ShadowElementTests {
static ChromeDriver driver;
public static void main(final String[] args) throws Exception {
driver = new ChromeDriver("YourTokenNumber", new ChromeOptions());
driver.navigate().to("http://localhost:3000/");
WebElement todayShadow = expandRootElement(driver.findElement(By.tagName("todo-app")));
WebElement todayAddItem = expandRootElement(todayShadow.findElement(By.tagName("add-item")));
todayAddItem.findElement(By.tagName("textarea")).sendKeys("Add OpenSDK");
todayAddItem.findElement(By.tagName("textarea")).sendKeys(Keys.ENTER);
todayAddItem.findElement(By.tagName("textarea")).clear();
todayAddItem.findElement(By.tagName("textarea")).sendKeys("Add TestProject SDK");
todayAddItem.findElement(By.tagName("textarea")).sendKeys(Keys.ENTER);
driver.quit();
public static WebElement expandRootElement(WebElement element) {
WebElement ele = (WebElement) ((JavascriptExecutor) driver)
.executeScript("return arguments[0].shadowRoot",element);
return ele;
After execution, we get automatic test reports directly on the TestProject Automation Portal:
The above code snippet only contains ChromeDriver, but it supports all W3C webdrivers – FirefoxDriver, EdgeDriver, and SafariDriver. Try yourself at your end ✌️
FirefoxDriver >
FirefoxDriver driver = new FirefoxDriver("YourTokenNumber", new FirefoxOptions());
EdgeDriver >
EdgeDriver driver = new EdgeDriver("YourTokenNumber", new EdgeOptions());
SafariDriver >
SafariDriver driver = new EdgeDriver("YourTokenNumber", new SafariOptions());
And don’t forget to add your TestProject Token in your code snippet, otherwise, it will not interact with TestProject Agent and nothing will happen.
📍The application that we have above for automation, can be found at – GitHub repo. This application is implemented by using Google’s Polymer library with NodeJS. So install those first before running this application locally.
Key Takeaways
Understood how QA Automation folks can interact with custom elements WITH / WITHOUT appending Shadow DOM.
We got familiar with the role of the Open/Closed mode of Shadow Root, while finding the elements.
Got to know the limitations of regular selectors {Xpath, CSS} with Shadow DOM.
Understood how to interact with multi-level Shadow Root elements.
Clear thought on how TestProject’s OpenSDK supports Shadow DOM elements.
Now we are pretty familiar with the Shadow DOM tree and its interaction with TestProject’s OpenSDK. But still, we have a few open questions:
Does TestProject record/playback support Shadow DOM elements?
Few more interesting aspects of the Shadow DOM tree?
Stay tuned and wait for the last part, very soon it will be on your plate 😉👍😊👍
About the author
Virender Singh
@VirenderSingh has a test automation mindset that contains the Developer’s logic and Tester’s domain expertise. He is always aggressive to share his knowledge through different mediums say Selenium global conference, organizational meetups, writing blogs for any problem statement, and also through organizational internal technical sessions. Frankly, he got into the testing field not really by choice. But once he started, there is no fall-back. Even after 14 years in this field, there is still a lot to learn and contribute to. And this learning curve is expanding day by day, that seems endless…..!
Approachable anytime at – @Linkedin @Twitter @Github
December 26, 2020,
9:09 am
Few test engineers came across #shadow-root (user-agent) while automating their scenarios, and asked me personally “How to get elements in user-agent shadow root with JavaScript?”. So below, mentioning the solution:
#shadow-root (user-agent) are browser vendor’s native implementation, so they are not documented and will never be accessible. Only open Shadow DOM elements are accessible through JavaScript.
December 26, 2020,
11:25 am
You cannot access a Shadow DOM created by the browser to display control, which is called a #shadow-root (user-agent) in the Dev Tools. You can only access open custom Shadow DOM (the ones that you create yourself), with the { mode: ‘open’ } option.
Example: element.attachShadow( { mode: ‘open’ } )
Testing GraphQL API
Design Patterns in Test Automation
Top 8 Python Testing Frameworks in 2020
Selenium JavaScript Automation Testing Tutorial For Beginners
Installing Selenium WebDriver Using Python and Chrome
Announcing TestProject 2.0 Next Gen Release: Hybrid Cloud & Offline Mode
Setup iOS Test Automation on Windows using TestProject
Automating End to End API Testing Flows Guide [Test Examples Included]
Create Behavior-Driven Python Tests using Pytest-BDD
Getting Started with TestProject Python SDK
Version Control Management
The Best Way To Test Delayed Responses
Can I do API testing with TestProject?
TestProject Website
TestProject YouTube Channel
Deep Dive into TestProject
State of Open Source Testing - 2020 Report
Create Coded Web Tests and Addons using TestProject's Java SDK
Testing trends & best practices
Step-by-step tutorials & tips
Authentic opinion blogs