Could you give me a hint how to add new locator strategies in a Selenium Plugin?
This code works but the driver instance that is handed over is a bit weird.
This ShadowDomFinder inherits the original one.
i could also just add the new finder method to the self._strategies but i was not sure.
could you maybe give a minimum example?
this _find_by_dim is just a dummy.
from SeleniumLibrary.base import LibraryComponent
from SeleniumLibrary.locators import ElementFinder
class ShadowDomFinder(ElementFinder):
def __init__(self, ctx):
ElementFinder.__init__(self, ctx)
self.register('dim',self._find_by_dim, True)
def _find_by_dim(self, driver, criteria, tag, constraints, parent=None):
self._disallow_webelement_parent(parent)
result = self.driver.execute_script("return %s;" % criteria)
if result is None:
return []
if not isinstance(result, list):
result = [result]
return self._filter_elements(result, tag, constraints)
class ShadowDom(LibraryComponent):
def __init__(self, ctx):
LibraryComponent.__init__(self, ctx)
self.element_finder = ShadowDomFinder(ctx)
Robot Code:
*** Settings ***
Library SeleniumLibrary plugins=ShadowDom
*** Test Cases ***
test
Open Browser browser=chrome
Go To chrome://settings/
${text}= Get Text dim:document.querySelector("body > settings-ui").shadowRoot.querySelector("#main").shadowRoot.querySelector("settings-basic-page").shadowRoot.querySelector("#basicPage > settings-section:nth-child(3) > settings-people-page").shadowRoot.querySelector("#edit-profile > div")
[Teardown] Close All Browsers
I think the last one more close what would work. The first example has limitations on using the add_location_strategy, mainly that it has scope and those are automatically purged.
Is there something that does not work or something else?
The scope shall be global.
At the moment the customer is working with Add Location Strategy keyword and this adds a lot of technical keywords.
There is a cascade of locators. For each ShadowDom there is a new CSS Locator.
Sometime it looks like that a part of this cascade works, but a branch is not yet loaded.
And i want to implement a locator strategy that fetches each part of this cascade after the previous.
So that we can also deliver correct error messages, which part did not work and create a screenshot of the âlast fount elementâ in the cascade.
This shadow root keeps coming up from multiple sources and SeleniumLibrary does not have proper support it. I wonder what would be good solution for it. Is the root of the shadow element always the same? If itâs we could implement some sort of root locator. Or would support for multiple locators (as a list) be idea?
I donât have a good idea how to capture screen shot from the element, with that kind of locators.
In the screen above need to verify that text â You already have a company! â is present on the page
JS path is : document.querySelector("#page-wrapper > div > div > app-report > div > app-web-component > div > introduction-app").shadowRoot.querySelector("section > div.info-page > div.is-size-3.has-text-weight-bold")
Is there any way to add some âcontainsâ syntax to existing JS path?
So that when running: Page Should Contain dom: document.querySelector("#page-wrapper > div > div > app-report > div > app-web-component > div > introduction-app").shadowRoot.querySelector("section > div.info-page > div.is-size-3.has-text-weight-bold") I could receive PASS.
Thank you in advance!
`
That is an imho a real problem if using Selenium!
The Problem is, that you can not just use Xpath with selenium on an element in a shadow dom.
CSS can not select the textContent!
XPATH could but is not usable in shadowDom.
AFAIK you have 3 Options:
Option1:
Getting ALL elements that matches your selector and then loop over them to find the right one.
Be aware that you have to use querySelectorAll at the last element, so that you get all matches. In this case, all div elements
Working example:
*** Settings ***
Library SeleniumLibrary
*** Variables ***
${ugly_selector} dom:document.querySelector("body > ha-demo").shadowRoot.querySelector("home-assistant-main").shadowRoot.querySelector("app-drawer-layout > partial-panel-resolver > ha-panel-lovelace").shadowRoot.querySelector("hui-root").shadowRoot.querySelector("#view > hui-view > hui-masonry-view").shadowRoot.querySelector("#columns > div:nth-child(1) > ha-demo-card").shadowRoot.querySelectorAll("div")
*** Test Cases ***
Test
Open Browser https://demo.home-assistant.io/#/lovelace/0 Chrome
${elem}= Get ShadowElement By Text ${ugly_selector} in ARS Home
Capture Element Screenshot ${elem}
*** Keywords ***
Get ShadowElement By Text
[Arguments] ${js_selector} ${operator} ${search_text}
${elems}= Get WebElements ${js_selector}
FOR ${elem} IN @{elems}
${elem_text}= Get Text ${elem}
Return From Keyword If $search_text ${operator} $elem_text ${elem}
END
FAIL Element not found
Option2:
You do more or less the same, but directly as javaScript:
in that case you have to prefix your selector with âArray.from(â and suffix it with your finding algoritm ).find(el => el.textContent.includes("ARS Home"));
And of course you have to use querySelectorAll to get again all matching elements.
*** Variables ***
${ugly_filter_selector}= dom:Array.from(document.querySelector("body > ha-demo").shadowRoot.querySelector("home-assistant-main").shadowRoot.querySelector("app-drawer-layout > partial-panel-resolver > ha-panel-lovelace").shadowRoot.querySelector("hui-root").shadowRoot.querySelector("#view > hui-view > hui-masonry-view").shadowRoot.querySelector("#columns > div:nth-child(1) > ha-demo-card").shadowRoot.querySelectorAll("div")).find(el => el.textContent.includes("ARS Home"));
*** Test Cases ***
Test2
Open Browser https://demo.home-assistant.io/#/lovelace/0 Chrome
Capture Element Screenshot
Close All Browsers
Option 3:
Say good bye to Selenium and switch to Browser LibraryâŠ
Browser Library is soo much much better when you have to work with shadowDom!!!
These two characters >> are to combine selectors.
It does automatically pierce shadowRoots and you can just use xpath and css and textselector as you like:
*** Settings ***
Library Browser
*** Test Cases ***
Test3
New Browser headless=False
New Page https://demo.home-assistant.io/#/lovelace/0
Take Screenshot selector=ha-demo-card >> text=/ARS.*/ #textselector with regex