Hi everyone, I’m really stuck. I’m trying to automate a modern web app that uses Web Components, and there is this one “Submit” button I just cannot click. When I use the Object Spy, it shows the button is there, but when I run the test, Katalon gives me a WebElementNotFoundException.
I looked at the Inspect tool, and the structure looks totally different from a normal page. Here is a simplified version of what I’m seeing in the HTML:
I even tried manually writing an XPath like //*[@id="submit-btn"], but it still says the element doesn’t exist even though I can see it right there. It looks like it’s tucked inside that #shadow-root thing. I feel like I’m trying to click something through a glass wall—I can see it, but Katalon can’t touch it. How do I get inside that “shadow” layer?
Hey @ali.zaidi
You’re running into a Shadow DOM problem. The reason Katalon (and Selenium underneath) cannot find your Submit button is because it is inside a shadow root, and standard locators like XPath or CSS cannot cross the shadow boundary.
Your XPath:
//*[@id="submit-btn"]
fails because:
XPath does not penetrate Shadow DOM
Selenium/Katalon only searches in the light DOM (main document)
Solution: Use JavaScript to access Shadow DOM
You need to manually traverse the shadow root using JavaScript.
What you’ve encountered is the Shadow DOM. This is a standard web practice used for encapsulation, effectively creating a “private” DOM tree inside a web component that is hidden from standard global selectors like document-wide XPath.
I use same architecture when desiging web app. To interact with these elements, you must navigate the Shadow Host first.
The Solution: Shadow Root Interaction
Katalon Studio has built-in support for this, but you must switch your strategy from XPath to CSS Selectors.
Identify the Shadow Host: In your HTML, this is .
Configure the Object:
In the Object Repository, open your target element (submit-btn).
Set the Selection Method to CSS.
Use the selector: #submit-btn.
In the Object settings, ensure you define the Shadow Root Parent as the #login-form object.
Custom Keyword: JavaScript Shadow Piercer
If the UI is complex or has multiple nested levels, a JavaScript executor is the most reliable “industry-standard” approach. It allows you to bypass the Selenium search engine and ask the browser directly for the element.
package com.web.components
import com.kms.katalon.core.annotation.Keyword
import com.kms.katalon.core.webui.keyword.WebUiBuiltInKeywords as WebUI
import com.kms.katalon.core.webui.driver.DriverFactory
import org.openqa.selenium.JavascriptExecutor
import org.openqa.selenium.WebElement
public class KaabShadowHandler {
@Keyword
def clickShadowElement(String hostSelector, String innerSelector) {
JavascriptExecutor js = (JavascriptExecutor) DriverFactory.getWebDriver()
// This script traverses the shadowRoot property which XPath cannot see
String script = "return document.querySelector('${hostSelector}').shadowRoot.querySelector('${innerSelector}')"
WebElement element = (WebElement) js.executeScript(script)
if (element != null) {
element.click()
println "Successfully clicked the shadow element."
} else {
WebUI.comment("FAILED: Could not find element inside Shadow Root")
throw new Exception("Shadow DOM Element not found: ${hostSelector} -> ${innerSelector}")
}
}
}
Strategic Advice:
XPath is Blind: Remember, XPath cannot cross a shadow boundary. If your HTML contains #shadow-root, you must use CSS or JavaScript.
The “Host” is the Key: The host is the “bridge.” You must find the host in the normal DOM before you can dive into the shadow.
Check for Nesting: Sometimes developers nest shadow roots inside other shadow roots. If that’s the case, your JavaScript path would need to include multiple .shadowRoot calls.
Is this the only web component in your application, or is the entire dashboard built using this architecture? If is it so , you will need to design framework according to that let me know if you need that i already worked on nextgen apps
Hey @ali.zaidi
The DriverFactory "Not Found" error is not related to Shadow DOM — it usually means Katalon cannot recognize the class because the required import is missing.
I think you missed adding the below files in the script
For a more maintainable solution, create a Test Object in the Object Repository using CSS as the selection method with button#submit-btn as the selector, then set the Parent Object to your shadow host element (login-widget). Katalon has native shadow DOM support that handles the traversal when the parent-child relationship is configured this way. After that, a standard WebUI.click() will work. You can also try WebUI.enhancedClick() as a shortcut since it has fallback strategies that sometimes handle shadow elements automatically.