Katalon can't "see" my button inside a Web Component!

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:

HTML

<div class="container">
  <login-widget id="login-form">
    #shadow-root (open)
      <style>...</style>
      <div class="wrapper">
        <button id="submit-btn" class="primary-action">Submit</button>
      </div>
  </login-widget>
</div>

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?

Hi there, and thanks for posting in the Katalon community! :hugs:

To help you faster, please review our guide on Spy Web Utitliy here: Spy Web utility in Katalon Studio | Katalon Docs. Double-checking the steps and configurations might resolve the issue.

If the doc doesn’t help, feel free to provide more details, and a community member will assist you soon.

Thanks for being a part of our community!
Best,
Elly Tran

Try inserting a line of WebUI.waitForElementVisible(TestObject, timeout) just before WebUI.click

Also see

1 Like

Option 1: Enhanced Click (Recommended - Easiest)

WebUI.enhancedClick(findTestObject('your/shadow/button/object'))

Option 2: JavaScript Direct Click

WebUI.executeJavaScript("""
  document.querySelector('login-widget').shadowRoot.querySelector('button#submit-btn').click()
""")

Option 3: JavaScript Click via Element

WebElement element = WebUiCommonHelper.findWebElement(findTestObject('your/shadow/button/object'), 30)
WebUI.executeJavaScript("arguments[0].click()", Arrays.asList(element))

Option 4: Create Shadow DOM Test Object (UI Method)

  1. In Object Repository, create a new test object
  2. Set Parent Object to: login-widget (your shadow host)
  3. Set Selection Method to: CSS (NOT XPath)
  4. Set Selector to: button#submit-btn or button.primary-action
  5. Then click normally:
WebUI.click(findTestObject('your/shadow/button/object'))

Option 5: Manual CSS Selector Click

TestObject submitBtn = new TestObject()
submitBtn.addProperty('css', 'equals', 'login-widget button#submit-btn')
WebUI.click(submitBtn)

Key Points

  • XPath won’t work on Shadow DOM elements—use CSS selectors only
  • Enhanced Click is the safest option as it uses multiple fallback methods
  • JavaScript execution is the most direct approach for Shadow DOM
  • Set Parent Object to the shadow host (login-widget) when creating test objects
  • Use Smart Locator in Project Settings for best auto-capture results

References

Shadow DOM blocks standard XPath/CSS—Katalon sees host (login-widget) but not internals (#shadow-root). Use Object Spy > Shadow Root Parent mode.

Steps

  1. Object Spy → Toggle Shadow Root Parent ON.
  2. Spy button → Creates parent: host + child: button hierarchy.
  3. TestObject: id=login-form (host) + id=submit-btn (child, Shadow Root Parent checked).
WebUI.waitForElementVisible(findTestObject('login_widget_submit'), 20)
WebUI.click(findTestObject('login_widget_submit'))

JS Fallback

WebUI.executeJavaScript("document.querySelector('login-widget').shadowRoot.querySelector('#submit-btn').click()")
1 Like

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.

Here’s how you can do it in Katalon:

import org.openqa.selenium.WebElement
import com.kms.katalon.core.webui.driver.DriverFactory
import org.openqa.selenium.JavascriptExecutor

def driver = DriverFactory.getWebDriver()

def shadowHost = driver.findElement(By.cssSelector("#login-form"))

def js = (JavascriptExecutor) driver

def shadowRoot = js.executeScript("return arguments[0].shadowRoot", shadowHost)

def submitBtn = js.executeScript("return arguments[0].querySelector('#submit-btn')", shadowRoot)

submitBtn.click()
1 Like

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.

  1. Identify the Shadow Host: In your HTML, this is .

  2. 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

1 Like

Katalon can see but are you not able to locate based on dynamic ids, you need to look for more concrete parameter/ locator strategy

I am working on Nesting issue i also feel same but unable to locate Nested elements. i will share my code here for better understanding

i waited its DOM issue with which katalon cannot interact

I tried this getting error with DriverFactory “Not Found”

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

import org.openqa.selenium.WebElement
import com.kms.katalon.core.webui.driver.DriverFactory
import org.openqa.selenium.JavascriptExecutor
import org.openqa.selenium.By
import org.openqa.selenium.WebDriver

Simply copy and paste all the import files in the script it will work.

1 Like

XPath cannot pierce the shadow boundary, which is why your selector finds nothing at runtime. You need CSS selectors and one of two approaches.

The quickest fix is JavaScript execution that walks into the shadow root manually:

WebUI.executeJavaScript("document.querySelector('login-widget').shadowRoot.querySelector('button#submit-btn').click()", null)

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.