Shadow DOM makes it possible to embed components, such as buttons or forms, into a page and keep them isolated from other components or the page itself. It poses a unique challenge for test automation since, unlike elements in the main DOM, elements in a shadow DOM tree cannot be found using the usual search methods.
To learn more about shadow DOM and why it's used, continue on to the Shadow DOM overview. If you're mainly interested in learning how mabl tests against elements in shadow DOM trees, skip ahead to the section on mabl in the shadow DOM.
This guide assumes that you are familiar with the Document Object Model (DOM), which represents the HTML of a webpage as a node tree.
Shadow DOM overview
Shadow DOM creates a hidden DOM tree of elements that is attached to an element in the main DOM tree. The styling and functionality for elements in a shadow DOM tree are isolated, which means that any CSS or JavaScript that applies to the main page doesn't impact shadow DOM elements. Likewise, the styling and functionality for shadow DOM elements doesn't affect what gets rendered in the main DOM.
Use Cases
Shadow DOM makes it easier for developers to reuse code and share these web components on different frameworks, such as React or Vue.js. Some advantages of using shadow DOM include the following:
- Reducing dependencies in the code base
- Embedding widgets or third-party content within your website that aren't accessible by JavaScript
- Implementing a button or other page element with consistent styling in an application that is developed by different teams (possibly using different frameworks)
Example
Consider a pared down example with two button
elements in the following HTML fragment:
shadow-root indicates the beginning of a shadow DOM
The "decline" button is in the main "light" DOM, whereas the "accept" button is inside a shadow DOM. This example illustrates some important concepts of shadow DOM:
-
Shadow host: the element in the main "light" DOM that the shadow DOM is attached to. In this example,
div class="hidden-tree"
is the shadow host. -
Shadow root: the root note of the hidden shadow DOM subtree, represented by
#shadow-root
. The "accept" button is located in the shadow DOM tree. -
Mode: The shadow root gets attached to the host element in either open or closed mode. The shadow DOM in this example is an open shadow DOM. (Most shadow DOMs are open.) If you wanted to access it from the main DOM, you could use the JavaScript property
Element.shadowRoot
. In a closed shadow DOM, theElement.shadowRoot
property returnsnull
.
If button elements in the main DOM are styled to be purple and have large, white letters, this styling will apply to the "decline" button. However, since the "accept" button is within a shadow DOM, it isn't impacted by this styling.
Web components
Web components are often registered as custom elements, such as popup-info
or nav-bar-menu
, and they rely on shadow DOM to keep their logic and styles isolated. If you want to explore examples of web components that use custom elements with shadow DOM, check out MDN's web-components-examples.
mabl in the shadow DOM
In addition to the range of strategies used to find elements in a regular DOM, mabl is also able to detect when an element is inside a shadow DOM tree.
Training steps in the shadow DOM
When you record a step within a shadow DOM, the mabl Trainer checks whether one of the target element's ancestors is a shadow root. If the Trainer does identify a shadow root, it records that element as a "shadow parent" and adds an extra find strategy to the step.
In addition to detecting shadow DOM in recorded steps, the trainer can also record custom find steps using CSS queries.
Training limitations
- Training in closed shadow DOMs is not supported. Only open shadow DOMs are supported.
- Elements within a shadow DOM tree cannot be targeted with XPath.
- While Configure Find can specify attributes for an element within a shadow DOM, it cannot include attributes for the shadow root itself.
Steps that target elements in a shadow DOM have a purple icon in the lower right corner. If you hover over this icon, the message "Action is within a shadow root" appears.
Icon denoting a step trained within a shadow DOM
DOM warning
If a recorded step shows the "DOM Warning” icon, it indicates that we weren’t able to uniquely identify the shadow root. This can happen if the web component lacks attributes to be identified by. If this is the case, you can use a CSS query to pierce the shadow DOM and identify the target element with greater confidence. For more information on custom “Find elements" steps, click here.
DOM warning icon
Training against slot elements
If a web component uses a slot
to pass text to that web component, the Trainer will include a "slotText" option for assertions and Configure Find. The reason for this is that part (or all) of the text from that web component can come from its slot, rather than the innerText.
The innerText in web components includes neither slot values nor the innerText of its child components.
If you assert on the text of an element in a shadow DOM and observe that its innerText value is empty or incomplete, try updating the assertion property to slotText.
A slotText
assertion in the mabl Trainer
For more information on slot elements, you can refer to the MDN guide.
Executing steps in the shadow DOM
When these steps are executed in a browser test, the execution engine starts by searching for the shadow parent, and then it implements additional strategies to find the correct element in the shadow DOM tree.
Auto-healing in the shadow DOM
Auto-healing in the Shadow DOM: mabl can auto-heal for elements within an element, but it cannot auto-heal on the shadow parent itself.
To check whether a step in a cloud run targeted an element with a shadow DOM, you can check the mabl Activity Logs for that step.
If the target element is within a shadow DOM, it will show this log line in the mabl Activity logs: "Looking for a {element-tag} shadow parent with the closest match to the model mabl has learned."
Cloud limitation
On the test output page, shadow DOM trees are not included in the HTML shown in the DOM tab