Jump to content

WebDriver UDF (W3C compliant version) - 2024/09/21


Danp2
 Share

Recommended Posts

@Triguit0 Give this a try and let me know how it works for you --

#include "wd_core.au3"

Local $sDesiredCapabilities
Local $iIndex
Local $sSession

$_WD_DEBUG = $_WD_DEBUG_Info

SetupChrome()
_WD_Startup()

If @error <> $_WD_ERROR_Success Then
    Exit -1
EndIf

$sSession = _WD_CreateSession($sDesiredCapabilities)

If @error = $_WD_ERROR_Success Then
    _WD_Navigate($sSession, "chrome://downloads/")

    $sRoot = _WD_GetShadowRoot($sSession, "downloads-manager")
    $sDiv = _WD_FindElement($sSession, $_WD_LOCATOR_ByCSSSelector, "#mainContainer", $sRoot)
EndIf

_WD_DeleteSession($sSession)
_WD_Shutdown()


; #FUNCTION# ====================================================================================================================
; Name ..........: _WD_GetShadowRoot
; Description ...:
; Syntax ........: _WD_GetShadowRoot($sSession, $sTagName)
; Parameters ....: $sSession            - Session ID from _WDCreateSession
;                  $sTagName            - Tag associated with shadow host element
; Return values .: Success      - Element ID returned by web driver
;                  Failure      - ""
;                  @ERROR       - $_WD_ERROR_Success
;                               - $_WD_ERROR_Exception
;                               - $_WD_ERROR_NoMatch
;                  @EXTENDED    - WinHTTP status code
; Author ........: Dan Pollak
; Modified ......:
; Remarks .......:
; Related .......:
; Link ..........:
; Example .......: No
; ===============================================================================================================================
Func _WD_GetShadowRoot($sSession, $sTagName)
    Local Const $sFuncName = "_WD_GetShadowRoot"

    Local $sResponse, $sResult, $sJsonElement, $oJson
    Local $sElement = _WD_FindElement($sSession, $_WD_LOCATOR_ByTagName, $sTagName)
    Local $iErr = @error

    If $iErr = $_WD_ERROR_Success Then
        $sJsonElement = '{"' & $_WD_ELEMENT_ID & '":"' & $sElement & '"}'
        $sResponse = _WD_ExecuteScript($sSession, "return arguments[0].shadowRoot", $sJsonElement)
        $oJson = Json_Decode($sResponse)
        $sResult  = Json_Get($oJson, "[value][" & $_WD_ELEMENT_ID & "]")
    EndIf

    If $_WD_DEBUG = $_WD_DEBUG_Info Then
        ConsoleWrite($sFuncName & ': ' & $sResult & @CRLF)
    EndIf

    Return SetError(__WD_Error($sFuncName, $iErr), $_WD_HTTPRESULT, $sResult)
EndFunc


Func SetupChrome()
_WD_Option('Driver', 'chromedriver.exe')
_WD_Option('Port', 9515)
_WD_Option('DriverParams', '--log-path="' & @ScriptDir & '\chrome.log"')

$sDesiredCapabilities = '{"capabilities": {"alwaysMatch": {"goog:chromeOptions": {"w3c": true }}}}'
EndFunc

 

Link to comment
Share on other sites

Hello again,

I was having a hard time trying to access a shadow-root inside a shadow-root with the WD_GetShadowRoot function, but doing this: 

$sScript = 'return document.querySelector("downloads-manager").shadowRoot.querySelector("#mainContainer").querySelector("downloads-item").shadowRoot.querySelector("#content");'
$sResponse = _WD_ExecuteScript($sSession, $sScript, "")
$oJson = Json_Decode($sResponse)
$contentElement  = Json_Get($oJson, "[value][" & $_WD_ELEMENT_ID & "]")

I was able to access the element.

Link to comment
Share on other sites

Thanks for the feedback, @Triguit0. I expect that this is just a starting point and the function will continue to change over time. Let me know if you run into any issues.

5 minutes ago, Triguit0 said:

I was having a hard time trying to access a shadow-root inside a shadow-root with the WD_GetShadowRoot function

I was sort of expecting this and was just about to comment that we will need to test on nested ShadowRoots. 😀

Link to comment
Share on other sites

Here's a revised version of _WD_GetShadowRoot that should allow access to nested ShadowRoots --

; #FUNCTION# ====================================================================================================================
; Name ..........: _WD_GetShadowRoot
; Description ...:
; Syntax ........: _WD_GetShadowRoot($sSession, $sStrategy, $sSelector, $sStartElement = "")
; Parameters ....: $sSession            - Session ID from _WDCreateSession
;                  $sStrategy           - Locator strategy. See defined constant $_WD_LOCATOR_* for allowed values
;                  $sSelector           - Value to find
;                  $sStartElement       - [optional] a string value. Default is "".
; Return values .: Success      - Element ID returned by web driver
;                  Failure      - ""
;                  @ERROR       - $_WD_ERROR_Success
;                               - $_WD_ERROR_Exception
;                               - $_WD_ERROR_NoMatch
;                  @EXTENDED    - WinHTTP status code
; Author ........: Dan Pollak
; Modified ......:
; Remarks .......:
; Related .......:
; Link ..........:
; Example .......: No
; ===============================================================================================================================
Func _WD_GetShadowRoot($sSession, $sStrategy, $sSelector, $sStartElement = "")
    Local Const $sFuncName = "_WD_GetShadowRoot"

    Local $sResponse, $sResult, $sJsonElement, $oJson
    Local $sElement = _WD_FindElement($sSession, $sStrategy, $sSelector, $sStartElement)
    Local $iErr = @error

    If $iErr = $_WD_ERROR_Success Then
        $sJsonElement = '{"' & $_WD_ELEMENT_ID & '":"' & $sElement & '"}'
        $sResponse = _WD_ExecuteScript($sSession, "return arguments[0].shadowRoot", $sJsonElement)
        $oJson = Json_Decode($sResponse)
        $sResult  = Json_Get($oJson, "[value][" & $_WD_ELEMENT_ID & "]")
    EndIf

    If $_WD_DEBUG = $_WD_DEBUG_Info Then
        ConsoleWrite($sFuncName & ': ' & $sResult & @CRLF)
    EndIf

    Return SetError(__WD_Error($sFuncName, $iErr), $_WD_HTTPRESULT, $sResult)
EndFunc

Here's an example showing access to the Chrome downloads --

#include "wd_core.au3"
#include "wd_helper.au3"

Local $sDesiredCapabilities
Local $sSession

$_WD_DEBUG = $_WD_DEBUG_Info

SetupChrome()
_WD_Startup()

If @error <> $_WD_ERROR_Success Then
    Exit -1
EndIf

$sSession = _WD_CreateSession($sDesiredCapabilities)

If @error = $_WD_ERROR_Success Then
    _WD_Navigate($sSession, "chrome://downloads/")

    $sRoot = _WD_GetShadowRoot($sSession, $_WD_LOCATOR_ByTagName, "downloads-manager")
    $sDiv = _WD_FindElement($sSession, $_WD_LOCATOR_ByCSSSelector, "#mainContainer", $sRoot)
    $sIron = _WD_FindElement($sSession, $_WD_LOCATOR_ByTagName, "iron-list", $sDiv)
    $sRoot2 = _WD_GetShadowRoot($sSession, $_WD_LOCATOR_ByTagName, "downloads-item", $sIron)
    $sDL = _WD_FindElement($sSession, $_WD_LOCATOR_ByCSSSelector, "#content", $sRoot2)
EndIf

_WD_DeleteSession($sSession)
_WD_Shutdown()

Func SetupChrome()
_WD_Option('Driver', 'chromedriver.exe')
_WD_Option('Port', 9515)
_WD_Option('DriverParams', '--log-path="' & @ScriptDir & '\chrome.log"')

$sDesiredCapabilities = '{"capabilities": {"alwaysMatch": {"goog:chromeOptions": {"w3c": true, "args":["--user-data-dir=C:\\Users\\' & @UserName & '\\AppData\\Local\\Google\\Chrome\\User Data\\", "--profile-directory=Default"]}}}}'
EndFunc

I'll include this in the next release unless someone identifies a major flaw with it. We will need to revisit it as the webdriver specs are updated to support Shadow DOMs.

 

Link to comment
Share on other sites

@Danp2, I have to regularly update a web page edit box of about 10,000 characters. The content is a software-generated HTML for a table. If I use _WD_ElementAction to change the value of the edit box, it takes very long time. So I use ClipPut and Send("^v"), which is much faster. Would it be possible to make _WD_ElementAction faster? Below is a reproducer showing how long _WD_ElementAction takes to send 500 characters.

#include "wd_core.au3"

Local $sDesiredCapabilities, $sSession
$_WD_DEBUG = $_WD_Debug_None
SetupChrome()
_WD_Startup()

$sSession = _WD_CreateSession($sDesiredCapabilities)

_WD_Navigate($sSession, "https://translate.google.com/?hl=en&tab=TT")
$sElement = _WD_FindElement($sSession, $_WD_LOCATOR_ByXPath, "//textarea[@id='source']")
$sText = StringLeft(_WD_GetSource($sSession), 500)
_WD_ElementAction($sSession, $sElement, "click")
_WD_ElementAction($sSession, $sElement, "value", $sText)
Sleep(5000)

_WD_DeleteSession($sSession)
_WD_Shutdown()


Func SetupChrome()
    _WD_Option('Driver', 'chromedriver.exe')
    _WD_Option('Port', 9515)
    _WD_Option('DriverParams', '--log-path="' & @ScriptDir & '\chrome.log"')

    $sDesiredCapabilities = '{"capabilities": {"alwaysMatch": {"unhandledPromptBehavior": "ignore", ' & _
        '"goog:chromeOptions": {"w3c": true, "excludeSwitches": ["enable-automation"], "useAutomationExtension": false, ' & _
        '"prefs": {"credentials_enable_service": false}, "args": ["start-maximized"] }}}}'
EndFunc

 

Edited by CYCho
Link to comment
Share on other sites

Hello,

i use the WebDriver.udf with Chromedriver.exe for an automated login to a website. Is there a solution how to disable the eye from the password field in Chrome?

image.png.45c9ebe3e956aecfc848cad2e3cd2fff.pngimage.png.1cbb75d12d824a277024d12087625fba.png

 

Greetings,

gmmg

Edited by gmmg
Link to comment
Share on other sites

The same is on the Paypal website. When i remove the Button Class "Hide Password ..........." then the Word "Show" in the Password Field is not shown.

How can I remove or change the code with WebDriver.

 

image.png.6070ceb726ab83b037d60c9f1d695a9f.png

image.thumb.png.0082523e19c1af3da3e97ddeab86657b.png

image.thumb.png.be1e1e1d544438e3f8726ebcaae0631f.png

Thx,

gmmg

Link to comment
Share on other sites

Hi @Danp2, I would like to know what function of WebDriver UDF gives me the window handle of a webdriver session, which I can use to bring one of several sessions to the top. Now I use generic AutoIt function to get them. A window handle would be handy also in rare cases where a webdriver session is created but not brought up to the top.

In this connection I would hope you could provide short comments on the $sCommands available for _WD_Action and _WD_Window functions. Maybe it's there already.

With many thanks!

Edited by CYCho
Link to comment
Share on other sites

@CYCho Not sure that I understand your situation. Perhaps you could further explain what is happening? Are you dealing with multiple tabs in a single window, multiple browser windows, etc.

FWIW, _WD_Attach returns a string "handle" that can be saved / used in the future to activate a given tab. I save the URL and this string to an array, and then used this for quickly switching to a known tab by URL.

Link to comment
Share on other sites

Quote

Are you dealing with multiple tabs in a single window, multiple browser windows, etc.

I often log in to one URL in multiple browser windows, each in different user name.  For example, when I need to transfer a teetime reservation from one person to another, I must have two different accounts logged in, cancel in one account and make reservation in the other. This operation should take place in rather short time because the the teetime, once cancelled, immediately  becomes available for others to pick up. I know that _WD_ElementAction can take place without making the session active, but I want to see the operation happening with my eyes. Thanks for your attention.

Link to comment
Share on other sites

21 hours ago, Danp2 said:

First step is figuring out the correct Javascript statement to remove the desired entry (class or perhaps the entire element). Then you'll need to implement that code using _WD_ExecuteScript

Unfortunately, I am not familiar with HTML and JavaScript.

Thx for your answer.

 

gmmg

 

Link to comment
Share on other sites

On 11/9/2018 at 9:38 PM, Danp2 said:

That site uses iframes, so you'll need to switch to the correct frame before you can locate the element with WD_FindElement. This can be done with _WD_Window or the helper function _WD_FrameEnter.

Check out the DemoFrames function in wd_demo.au3 for an example of using these functions.

Ok, after doing some more research I found this post by you and was able to understand what to do with the iframes. 

I added this to my code and now it works. (i am able to send data to the LongD TextBox) 

ConsoleWrite(@CRLF & @CRLF & @CRLF & @CRLF & "Frames=" & _WD_GetFrameCount($sSession) & @CRLF)
    ConsoleWrite(" 01 TopWindow=" & _WD_IsWindowTop($sSession) & @CRLF)
    $sElement = _WD_FindElement($sSession, $_WD_LOCATOR_ByXPath, "//iframe[@id='ma6499a9c-rte_iframe']")
    _WD_FrameEnter($sSession, $sElement)
    ConsoleWrite(" 02 TopWindow=" & _WD_IsWindowTop($sSession) & @CRLF)
    Local $eDescription   = _WD_FindElement($sSession, $_WD_LOCATOR_ByXPath, "//div[@id='dijitEditorBody']")
    _WD_ElementAction($sSession, $eDescription, 'value', "Can I add a Long Description or are you just joshing me?")
    _WD_ElementAction($sSession, $eDescription, 'text')
    _WD_FrameLeave($sSession)
    ConsoleWrite(" 04 TopWindow=" & _WD_IsWindowTop($sSession) & @CRLF)

Thank you very much for helping me.

@Danp2 Sorry, I didn't check that it was posting to the wrong topic. Can I move this reply to the "WebDriver UDF - Help & Support" topic?

Edited by nooneclose
posted in wrong topic
Link to comment
Share on other sites

  • 1 month later...
  • Danp2 changed the title to WebDriver UDF (W3C compliant version) - 2024/09/21
  • Melba23 pinned this topic

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
 Share

×
×
  • Create New...