Skip to content

Commit

Permalink
Basic implementation for piercing Shadow DOM
Browse files Browse the repository at this point in the history
- Added test case for looking into the nested shadow dom
- Added basic method for piercing the showdom dom. Here using the cascading locators
  and checking if a parent element is a shadow dom host. If so it replaces the parent
  element with the resulting shadow dom object. Thus the continued find then searches
  the shadow dom root instead of the regular dom. This is a highly simplified method
  for piercing and misses many important scenarios (might be a descendant but on the
  regular dom for example).
  • Loading branch information
emanlove committed Nov 10, 2024
1 parent 5859883 commit c3735e1
Show file tree
Hide file tree
Showing 2 changed files with 12 additions and 0 deletions.
3 changes: 3 additions & 0 deletions atest/acceptance/3-shadow_dom/piercing_shadowdom.robot
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,6 @@ Test Setup Go To Page "shadow_dom.html"
Multiple Locators with double arrows as separator should work
[Setup] Go To Page "links.html"
Page Should Contain Element css:div#div_id >> xpath:a[6] >> id:image1_id

Nested Shadow DOM
Page Should Contain Element id:shadow_host >> id:nested_shadow_host >> id:nested_div
9 changes: 9 additions & 0 deletions src/SeleniumLibrary/locators/elementfinder.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from selenium.webdriver.remote.webelement import WebElement
from selenium.webdriver.support.event_firing_webdriver import EventFiringWebElement
from selenium.webdriver.common.by import By
from selenium.common.exceptions import NoSuchShadowRootException

from SeleniumLibrary.base import ContextAware
from SeleniumLibrary.errors import ElementNotFound
Expand Down Expand Up @@ -110,13 +111,21 @@ def _split_locator(self, locator: Union[str, list]) -> list:
return parts

def _find(self, locator, tag=None, first_only=True, required=True, parent=None):
parent_is_shadowdom_host = False
element_type = "Element" if not tag else tag.capitalize()
if parent and not self._is_webelement(parent):
raise ValueError(
f"Parent must be Selenium WebElement but it was {type(parent)}."
)
if self._is_webelement(locator):
return locator
try:
if parent:
parent_is_shadowdom_host = parent.shadow_root
except NoSuchShadowRootException:
pass
if parent_is_shadowdom_host: # and ??._shadowdom_piercing
parent = parent_is_shadowdom_host
prefix, criteria = self._parse_locator(locator)
strategy = self._strategies[prefix]
tag, constraints = self._get_tag_and_constraints(tag)
Expand Down

0 comments on commit c3735e1

Please sign in to comment.