You normally do not need to do anything special to pierce open Shadow DOM in Playwright. Standard Playwright locators such as getByRole() can locate elements inside open shadow roots automatically.
await page
.getByRole('button', { name: 'Add to cart' })
.click();
Leapwork Play keeps this workflow even simpler for the test author: record the interaction in Play's browser and work with the generated Playwright test instead of manually traversing shadow roots or building custom DOM logic.
How do you interact with Shadow DOM elements in Play?
The following example uses ServiceNow, where elements in the navigation UI are nested inside open shadow roots.
Browser DevTools shows the Shadow DOM structure:
sn-collapsible-list
#shadow-root (open)
...
But the test author does not manually traverse that structure.
In Play, record the interaction with the element in the browser. The demo records actions such as:
-
Click the Self-Service button to expand its menu in ServiceNow.
-
Click the Incidents link in the ServiceNow navigation menu.
Play then runs those interactions against the application.
How does this work in Playwright?
Suppose the page contains:
product-card
#shadow-root (open)
button "Add to cart"
You can normally use:
await page
.getByRole('button', {
name: 'Add to cart'
})
.click();
You do not need to manually retrieve element.shadowRoot and then search inside it, that is the main answer for most Shadow DOM tests.
How Play simplifies the Shadow DOM workflow
|
With Playwright |
With Leapwork Play |
|---|---|
|
Write the locator for the target element |
Record the interaction in Play's browser and generate the underlying Playwright |
|
Playwright locators automatically pierce open shadow roots |
Play builds on the same Playwright behavior in the generated test |
|
Maintain the test code and locator |
Work with the recorded interaction as a Play test step |
|
Update locators when supported interactions change |
Play's locator and self-healing workflow can help recover supported interactions |
Play builds on Playwright's existing support for open Shadow DOM rather than introducing a separate traversal model. Play adds the recording, generated Playwright, and test workflow around those interactions.
Key exceptions
Keep these limits in mind:
-
XPath does not cross Shadow DOM boundaries. Use Playwright locators such as
getByRole()instead. -
Closed Shadow DOM is different. This guidance applies to open Shadow DOM; closed shadow roots intentionally restrict normal DOM access.
Best practices
-
Record the user interaction in Play rather than manually building Shadow DOM traversal logic when the Play workflow fits the test.
-
Use normal Playwright locators for elements inside open Shadow DOM.
-
Prefer user-facing roles, labels, and text over component-internal CSS.
-
Do not use XPath to cross a shadow-root boundary.
-
Distinguish open and closed Shadow DOM.
-
If several elements match, scope the locator by meaningful application context rather than DOM position.
Troubleshooting
|
Problem |
What to do |
|---|---|
|
Playwright cannot find an element inside a web component |
Confirm that the component uses an open shadow root and check the locator itself. |
|
XPath finds elements outside the component but not inside |
XPath does not pierce the shadow root; use a normal Playwright locator. |
|
Several elements inside components match |
Scope the locator to meaningful surrounding content. |
|
The component uses a closed shadow root |
Do not assume the open-Shadow-DOM approach applies. |
|
The interaction works manually but not in the test |
Check the target locator and confirm the actual DOM boundary before adding custom traversal code. |
Frequently asked questions
How does Leapwork Play handle Shadow DOM?
Play records interactions with ServiceNow elements nested inside open shadow roots and runs the resulting test without the author manually traversing those roots. This builds on Playwright's automatic handling of open Shadow DOM.
Does Playwright automatically pierce Shadow DOM?
Yes, standard Playwright locators can locate elements through open shadow roots without explicit shadowRoot traversal.
Do I need to call element.shadowRoot?
Usually not for open Shadow DOM. Start with a normal Playwright locator.
Does XPath pierce Shadow DOM?
No. Use a standard Playwright locator rather than XPath when the target is inside a shadow root.