Jump to content

Recommended Posts

Posted

I'm using Au3WebDriver 1.5.0 and AutoIt 3.3.14.5

I'm trying to interact with our ServiceNow page but I can only access a few top most controls.

Only the first _WD_FindElement below works as it's just before the #shadow-root, everything after that can't be found

_WD_Navigate($sSession, $sDirectLink)
_WD_LoadWait($sSession, 5000, 30000)
$eID = _WD_FindElement($sSession, $_WD_LOCATOR_ByXPath, "/html/body/macroponent-f51912f4c700201072b211d4d8c26010")  ;This is the last item that can be found
;How to use this JS Path?  document.querySelector("body > macroponent-f51912f4c700201072b211d4d8c26010").shadowRoot.querySelector("div > sn-canvas-appshell-root")
$eID = _WD_FindElement($sSession, $_WD_LOCATOR_ByCSSSelector, "body > macroponent-f51912f4c700201072b211d4d8c26010 > div > sn-canvas-appshell-root")    ;_WD_FindElement ==> No match [8]
$eID = _WD_FindElement($sSession, $_WD_LOCATOR_ByXPath, '/html/body/macroponent-f51912f4c700201072b211d4d8c26010//div/sn-canvas-appshell-root]')    ;_WD_FindElement ==> Invalid Expression [11]
$eID = _WD_FindElement($sSession, $_WD_LOCATOR_ByXPath, "/html/body/macroponent-f51912f4c700201072b211d4d8c26010/./div/sn-canvas-appshell-root") ;trying . in the blank selector spot.  _WD_FindElement ==> No match [8]

The actual controls I want are stored under MANY nested #shadow-root (Open) items that can't be found. Each find just returns "_WD_FindElement ==> No match [8]"

 

When I copied the JS Path from the Edge Inspect, that at least shows the .shadowRoot but I don't know how to use this JS Path as part of the _WD_FindElement?

document.querySelector("body > macroponent-f51912f4c700201072b211d4d8c26010").shadowRoot.querySelector("div > sn-canvas-appshell-root")
 

ShadowRoot.jpg

  • 4 weeks later...
Posted

The selectors fail because XPath and CSS can’t cross a #shadow-root boundary. Each shadow root is a separate DOM tree, so macroponent... > div > sn-canvas-appshell-root is never a valid path from the document. You have to enter each shadow root one at a time and search inside it. Inside a shadow root, only CSS selectors work. XPath is rejected there, so stick to $_WD_LOCATOR_ByCSSSelector. (Your second XPath also has a stray ] at the end, which is why you got “Invalid Expression”.)

Au3WebDriver 1.5.0 supports this directly:

1. Step through each shadow root

 
; Get the shadow root of the macroponent host
Local $sRoot1 = _WD_GetShadowRoot($sSession, $_WD_LOCATOR_ByCSSSelector, "body > macroponent-f51912f4c700201072b211d4d8c26010")

; Search INSIDE that shadow root (last param $bShadowRoot = True)
Local $eApp = _WD_FindElement($sSession, $_WD_LOCATOR_ByCSSSelector, "sn-canvas-appshell-root", $sRoot1, False, True)

; Next level: get that element's shadow root, then search inside it
Local $sRoot2 = _WD_ElementAction($sSession, $eApp, 'shadow')
Local $eLayout = _WD_FindElement($sSession, $_WD_LOCATOR_ByCSSSelector, "sn-canvas-appshell-layout", $sRoot2, False, True)
; ...and so on
 

2. A helper so you don’t repeat this a dozen times

 
; Each array item is a CSS selector run inside the current shadow root.
; Every item except the last is a shadow host: the function enters its shadow root.
Func _WD_FindInShadowPath($sSession, $aPath)
    Local $sContext = Default, $bShadow = False, $sElem
    For $i = 0 To UBound($aPath) - 1
        $sElem = _WD_FindElement($sSession, $_WD_LOCATOR_ByCSSSelector, $aPath[$i], $sContext, False, $bShadow)
        If @error Then Return SetError(@error, $i, "")   ; @extended = which step failed
        If $i = UBound($aPath) - 1 Then Return $sElem
        $sContext = _WD_ElementAction($sSession, $sElem, 'shadow')
        If @error Or $sContext = "" Then Return SetError(9, $i, "")
        $bShadow = True
    Next
EndFunc

; Usage
Local $aPath[] = [ _
    "body > macroponent-f51912f4c700201072b211d4d8c26010", _
    "sn-canvas-appshell-root", _
    "sn-canvas-appshell-layout sn-polaris-layout", _
    "your-next-host", _
    "#theControlYouWant" ]
Local $eTarget = _WD_FindInShadowPath($sSession, $aPath)
If @error Then ConsoleWrite("Failed at step " & @extended & @CRLF)
 

Look at sn-canvas-appshell-layout sn-polaris-layout in that list. In your screenshot, sn-polaris-layout has a slot badge. That means it’s a regular child of sn-canvas-appshell-layout, not inside that element’s shadow root. A plain descendant selector in the same step finds it, so you don’t enter a shadow root there. Only add a new array item where the inspector shows a new #shadow-root.

3. Or use the JS Path you copied from Edge

You can run the JS Path as-is and ask for the element back:

 
Local $sJS = 'return document.querySelector("body > macroponent-f51912f4c700201072b211d4d8c26010")' & _
             '.shadowRoot.querySelector("div > sn-canvas-appshell-root")'
Local $eID = _WD_ExecuteScript($sSession, $sJS, Default, Default, $_WD_JSON_Element)
 

$eID is a normal element ID, so you can use it with _WD_ElementAction, _WD_ElementActionEx, and so on. This is often the quickest route: copy the JS Path for the actual control from Edge, prefix it with return , and you’re done. The downside is that it’s tied to that exact structure, so it breaks when ServiceNow updates its UI.

ServiceNow tips that may save you all of this

  • Classic forms run inside an iframe. In the Next Experience (Polaris) UI, list and form pages (incident.do, etc.) load in iframe#gsft_main, which sits deep in the shadow tree. Find the iframe with one of the methods above, then call _WD_FrameEnter($sSession, $eIframe). Inside the frame it’s a normal DOM, and your usual XPath works again.
  • Skip the shell entirely. If your target is a classic form, navigate straight to it, e.g. https://yourinstance.service-now.com/incident.do?sys_id=..., instead of a /now/nav/ui/classic/params/target/... link. That page has no Polaris wrapper and no shadow roots, so _WD_FindElement with XPath works directly.
  • The macroponent-f51912... tag is tied to the page definition. If it ever changes, body > * or body > [component-id] is a more stable first selector.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...