A document-level selector does not cross a shadow boundary. That does not mean Selenium cannot automate Web Components: Selenium 4 exposes shadow-root search contexts. The important distinction is where the search begins.
Start with native shadow-root access
from selenium.webdriver.common.by import By
# driver is a session on an application containing this component.
host = driver.find_element(By.CSS_SELECTOR, "settings-panel")
root = host.shadow_root
root.find_element(By.CSS_SELECTOR, "button.save").click()Wait for each component and its intended outcome in a real test. A successful click only proves that the command completed; the saved setting still needs an assertion.
Use Lumos when nested traversal becomes repetitive
Lumos ShadowDOM extends WebDriver with helpers for explicit paths through open shadow roots. The distribution is lumos-shadowdom, while the Python import is lumos. This example requires an application with the illustrated component hierarchy.
pip install lumos-shadowdomfrom selenium import webdriver
import lumos # Registers the WebDriver extension.
driver = webdriver.Chrome()
try:
driver.get("https://example.com/shadow-dom-app")
# Replace the URL and component path with your own test application.
driver.find_shadow("my-app > settings-panel > #save-btn").click()
# Assert the persisted setting using the application's actual contract.
finally:
driver.quit()Understand the boundary
The helper’s JavaScript traversal is for open shadow roots. A closed root is not exposed through the page’s ordinary shadowRoot property. Event composedPath() can aid discovery through open components; it does not universally expose the internals of closed roots.
- Prefer stable component identifiers to paths tied to incidental layout.
- Check that a text search identifies one intended control before clicking.
- Keep traversal details inside a component object when multiple tests use them.
- Treat a missing root or changed component boundary as useful failure evidence.

