Jump to content

WebDriver UDF - Help & Support (III)


Danp2
 Share

Recommended Posts

@Danp2 Dan,

I've noticed a potential issue with _WD_Attach() and began looking into the _WD_Attach() definition. I found that _WD_Window(), doesn't accurately return the handle of the current tab in $sCurrentTab that _WD_Attach() uses in its definition.

In the following modified wd_demo.au3, for example, if you click any tab to activate it and then choose the "getTabData" function from the drop down, it should print the current handle in the Scite Console returned by _WD_Window($sSession, 'window'). As you can see, it always returns the last tab handle and doesn't return handles of the other two tabs even if they're the "current" active tab.

Local $aBrowsers[][2] = [["Edge", SetupEdge], _
                        ["Chrome", SetupChrome], _
                        ["FireFox", SetupGecko], _
                        ["getTabData", getTabData]]

Local $hGUI = GUICreate("Webdriver Demo", 200, 50, 100, 200, BitXOR($GUI_SS_DEFAULT_GUI, $WS_MINIMIZEBOX))
GUICtrlCreateLabel("Browser", 15, 12)
Local $idBrowsers = GUICtrlCreateCombo("", 75, 10, 100, 20, $CBS_DROPDOWNLIST)
Local $sData = _ArrayToString($aBrowsers, Default, Default, Default, "|", 0, 0)
GUICtrlSetData($idBrowsers, $sData)
GUICtrlSetData($idBrowsers, $aBrowsers[0][0])
GUISetState(@SW_SHOW)

While 1
    $nMsg = GUIGetMsg()
    Switch $nMsg
        Case $GUI_EVENT_CLOSE
            GUIDelete($hGUI)
            _WD_DeleteSession($GsSession)
            _WD_ShutDown()
            ExitLoop
        Case $idBrowsers
            Local $row = _GUICtrlComboBox_GetCurSel($idBrowsers)
            If $aBrowsers[$row][0] <> "getTabData" Then
                 ; Execute browser setup routine for user's browser selection
                 _WD_DeleteSession($GsSession)
                 __WD_CloseDriver()

                Call($aBrowsers[_GUICtrlComboBox_GetCurSel($idBrowsers)][1])
                _WD_Startup()

                $GsSession = _WD_CreateSession($GsDesiredCapabilities)
                _WD_Navigate($GsSession, "https://www.cnn.com")
                _WD_NewTab($GsSession, Default, Default, "https://www.yahoo.com")
                _WD_NewTab($GsSession, Default, Default, "https://www.nytimes.com")
            Else
                Call($aBrowsers[_GUICtrlComboBox_GetCurSel($idBrowsers)][1])
            EndIf
   EndSwitch
WEnd

Func getTabData()
    ConsoleWrite(_WD_Window($GsSession, "window") & @CR)
EndFunc

 

Edited by CodeWriter
Link to comment
Share on other sites

@CodeWriter I think you might be misunderstanding which tab is being called "Active" in the UDF. The active tab isn't the one that is currently visible, but rather, the one that is being used currently by the WebDriver

If you want to check which tab the user is currently using, then one option is to execute some javascript on each tab until you find the active one... the JavaScript is as simple as

document.visibilityState

Here's a function that will get the handle of the window that the user has open: (with a MsgBox to tell you the current URL)

Spoiler
Func _WDEx_UserActiveTab($sSession)

    ; Store the current tab
    Local $sCurrentTab = _WD_Window($sSession, "window")

    ; Get an array of handles to the tabs
    Local $asHandles = _WD_Window($sSession, "handles")

    Local $sResponse, $oJSON, $sResult

    ; For each tab
    For $i=0 To UBound($asHandles) - 1

        ; Select the current window
        _WD_Window($sSession, "window", '{"handle":"' & $asHandles[$i] & '"}')

        ; Here's the actual check, just check the 'visibility state' of the webpage... can be visible or other, meaning background
        $sResponse = _WD_ExecuteScript($sSession, "return document.visibilityState")
        ; Decode it
        $oJSON = Json_Decode($sResponse)
        ; Get the value
        $sResult = Json_Get($oJSON, "[value]")
        ; If it's visible, we're done checking tabs
        If $sResult = "visible" Then ExitLoop
    Next

    ; Remove this if you're making this into a "real" function ;)
            $sResponse = _WD_ExecuteScript($sSession, "return document.URL")
            $oJSON = Json_Decode($sResponse)
            $sResult = Json_Get($oJSON, "[value]")

            MsgBox(0, "Active URL", $sResult)
    ; End Remove code

    ; Restore the current tab
    _WD_Window($sSession, "window", '{"handle":"' & $sCurrentTab & '"}')

    ; Return the tab the user has open (yes, it's kind of hacky to use the variables like this, but it works?)
    Return $asHandles[$i]

EndFunc

 

All my code provided is Public Domain... but it may not work. ;) Use it, change it, break it, whatever you want.

Spoiler

My Humble Contributions:
Personal Function Documentation - A personal HelpFile for your functions
Acro.au3 UDF - Automating Acrobat Pro
ToDo Finder - Find #ToDo: lines in your scripts
UI-SimpleWrappers UDF - Use UI Automation more Simply-er
KeePass UDF - Automate KeePass, a password manager
InputBoxes - Simple Input boxes for various variable types

Link to comment
Share on other sites

@seadoggie01Just tried your code but it seems that whenever you call _WD_Window($sSession, "window", '{"handle":"' & $asHandles[$i] & '"}') in the loop, that handle becomes BOTH the "current" window and the "visible" window just before the JavaScript and JSON decode. The test becomes a tautology for the 1st window in the handles array. Wishing there was a method to Attach to the current visible window like _IE_Attach() and less computation overhead.

Link to comment
Share on other sites

@CodeWriter I see that now, sorry. I've been working on this for ~the last hour and can't find anything. I tried using JQuery and JavaScript and finally realized, to heck with it, we'll find it using WinList! :D (I mean, why not, this is AutoIt, and it's perfect for getting the active window)

So here we go:

Spoiler

This isn't the way I'd like to do this, but I guess it works and even accounts for other (non-driver enabled) browser windows open. Note the minimal error checking though, you'll want to fix that too. I'm too sleepy rn to care. It does seem to work though, thoroughly tested!

; #FUNCTION# ====================================================================================================================
; Name ..........: _WDEx_UserActiveTab
; Description ...: Find the active (as in active window, not driver active) tab
; Syntax ........: _WDEx_UserActiveTab($sSession[, $sWindowEnding = ""])
; Parameters ....: $sSession            - from _WD_CreateSession.
;                  $sWindowEnding       - the ending of your browser's window (something like ' - Google Chrome' or ' - Microsoft Edge').
; Return values .: Success - the handle to the tab the user had active
;                  Failure - False and sets @error:
;                  |1 - someone opened another browser window while this was running, try again?
; Author ........: Seadoggie01
; Modified ......: 
; Remarks .......: 
; Related .......: 
; Link ..........: 
; Example .......: No
; ===============================================================================================================================
Func _WDEx_UserActiveTab($sSession, $sWindowEnding)

    ; Get a list of windows that match the browser (this is our target array)
    Local $ahOriginalWinHandles = WinList("[REGEXPTITLE:(?i).*?" & $sWindowEnding & "]")

    ; Store the current tab
    Local $sCurrentTab = _WD_Window($sSession, "window")

    ; Get an array of handles to the tabs
    Local $asHandles = _WD_Window($sSession, "handles")

    Local $ahNewWinHandles

    ; For each tab
    For $i=0 To UBound($asHandles) - 1

        ; Activate the tab
        _WD_Window($sSession, "window", '{"handle":"' & $asHandles[$i] & '"}')

        ; Get the list of active windows matching the browser now (it will have changed after moving to a new tab)
        $ahNewWinHandles = WinList("[REGEXPTITLE:(?i).*?" & $sWindowEnding & "]")

        ; If there's an extra window, the arrays will never match as we can't check for it? I think? Too hard and super rare case
        If Not (UBound($ahOriginalWinHandles, 1) = UBound($ahNewWinHandles, 1)) Then Return SetError(1, 1, False)
        If Not (UBound($ahOriginalWinHandles, 2) = UBound($ahNewWinHandles, 2)) Then Return SetError(1, 2, False)

        ; For each window we found
        For $x=1 To UBound($ahOriginalWinHandles) - 1
            ; Ignore the window handles, we want to check the window titles
            ; If they don't match, move to the next tab (Continue Loop 2 jumps to the next tab, not window handle)
            If $ahOriginalWinHandles[$x][0] <> $ahNewWinHandles[$x][0] Then ContinueLoop 2
        Next

        ; They all matched, so our target tab is stored in $asHandles[$i], we're nearly done
        ExitLoop
    Next

    ; Restore the current tab
    _WD_Window($sSession, "window", '{"handle":"' & $sCurrentTab & '"}')

    ; Return the tab the user has open (yes, it's kind of hacky to use the variables like this, but it works?)
    Return $asHandles[$i]

EndFunc

All my code provided is Public Domain... but it may not work. ;) Use it, change it, break it, whatever you want.

Spoiler

My Humble Contributions:
Personal Function Documentation - A personal HelpFile for your functions
Acro.au3 UDF - Automating Acrobat Pro
ToDo Finder - Find #ToDo: lines in your scripts
UI-SimpleWrappers UDF - Use UI Automation more Simply-er
KeePass UDF - Automate KeePass, a password manager
InputBoxes - Simple Input boxes for various variable types

Link to comment
Share on other sites

Hi @Danp2,

 

I am still struggling with the ShadowRoot system, but it seems that the current version of _WD_GetShadowRoot is giving me a hard time, when i use the older version is working perfectly.

However, this is working for only the first ShadowRoot when i try to access the next one inside i don't know what is the correct way of performing this with the older version, or that it is even possible.

Maybe you can have a look and pinpoint the error in the newest function, i will place them both below;

New Function

; #FUNCTION# ====================================================================================================================
; Name ..........: _WD_GetShadowRoot
; Description ...: Retrieves the shadow root of an element
; Syntax ........: _WD_GetShadowRoot($sSession, $sStrategy, $sSelector[, $sStartElement = Default])
; Parameters ....: $sSession            - Session ID from _WD_CreateSession
;                  $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 = Default)
    Local Const $sFuncName = "_WD_GetShadowRoot"
    Local $sResponse, $sResult, $oJSON

    If $sStartElement = Default Then $sStartElement = ""

    Local $sElement = _WD_FindElement($sSession, $sStrategy, $sSelector, $sStartElement)
    Local $iErr = @error

    If $iErr = $_WD_ERROR_Success Then
        $sResponse = _WD_ElementAction($sSession, $sElement, 'shadow')

        $oJSON = Json_Decode($sResponse)
        $sResult = Json_Get($oJSON, "[value][" & $_WD_ELEMENT_ID & "]")
    EndIf

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

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

 

Old Function

; #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

 

My Code

;Combined with old version (working)

    $sRoot = _WD_GetShadowRoot($sSession, "view-section")
    $sDiv = _WD_FindElement($sSession, $_WD_LOCATOR_ByCSSSelector, "#filtersection", $sRoot)
    
    ConsoleWrite($sRoot & @CRLF & $sDiv)
    
    

;Combined with new version (not working)

    $sRoot = _WD_GetShadowRoot($sSession, $_WD_LOCATOR_ByTagName, "view-section")
    $sDiv = _WD_FindElement($sSession, $_WD_LOCATOR_ByCSSSelector, "#filtersection", $sRoot)
    
    ConsoleWrite($sRoot & @CRLF & $sDiv)

 

Thanks!

 

Edit:
I did manage to fix it with this crude solution, for anyone who has the same issue as me :)
 

_WD_ExecuteScript($WLF_Session, "document.querySelector('section-host').shadowRoot.querySelector('section-child').shadowRoot.querySelector('section-filter').shadowRoot.querySelector('#ButtonFilter').click();")

 

Edited by Blueman
Link to comment
Share on other sites

On ‎4‎/‎30‎/‎2021 at 10:52 PM, Danp2 said:

@Marlon13 I just tried it and received back the entire table. Unsure why you would only get 5 rows returned, unless you encountered an error during the retrieve process. Did you examine the Scite output panel for any errors? Can you save this output to a file and then post it here for review?

this is  what appear in the array debug

https://ibb.co/djxJ9R4

and this is in scite

https://privatebin.net/?91092e86dd9930ba#8gHEzf5cmM1j33vnxDjzUcX8eueC7x5mAzxf9tHpbRTz

 

I'm trying to extract the same thing but from another site, here I find a particular "error", the script only works if after the browser is launched I widen the browser a little?, otherwise some commands on the comboboxes do not execute them, why ?

this is my code

#include <MsgBoxConstants.au3>
#include <WinAPIFiles.au3>
#include <File.au3>
#include <Array.au3>
#include "wd_core.au3"
#include "wd_helper.au3"
#include <GuiComboBoxEx.au3>
#include <GUIConstantsEx.au3>
#include <ButtonConstants.au3>
#include <WindowsConstants.au3>
#include <Debug.au3>

Global $sDesiredCapabilities, $sSession, $sElement

SetupEdge()
_WD_Startup()
If @error <> $_WD_ERROR_Success Then
    Exit -1
EndIf

$sSession = _WD_CreateSession($sDesiredCapabilities)
_ScrapeEconCalendar()


_WD_DeleteSession($sSession)
_WD_Shutdown()


Func _ScrapeEconCalendar()
    ;_WD_Navigate($sSession, "https://www.cboe.com/delayed_quotes/fxe/quote_table")
    _WD_Navigate($sSession, "https://www.nasdaq.com/market-activity/funds-and-etfs/fxe/option-chain")
    Sleep(1500)
    $sElement = _WD_FindElement($sSession, $_WD_LOCATOR_ByXPath, '//button[@class="option-chain-filter-toggle-moneyness option-chain-tables__dropdown-toggle"]')
    ; Click input element
    _WD_ElementAction($sSession, $sElement, 'click')
    Sleep(1500)
    $sElement = _WD_FindElement($sSession, $_WD_LOCATOR_ByXPath, '//button[@aria-label="Click to show data for All (Moneyness)"]')
    ; Click input element
    _WD_ElementAction($sSession, $sElement, 'click')
    Sleep(1700)
    ;MsgBox(0, '', '')
    ;Local $aResult = _WD_GetTable($sSession, '//table[@class="option-chain-tables__table"]')
    ;_DebugArrayDisplay($aResult)

    $sElement = _WD_FindElement($sSession, $_WD_LOCATOR_ByXPath, '//button[@class="option-chain-filter-toggle-month option-chain-tables__dropdown-toggle"]')
    ; Click input element
    _WD_ElementAction($sSession, $sElement, 'click')
    Sleep(1700)

    $sElement = _WD_FindElement($sSession, $_WD_LOCATOR_ByXPath, '//button[@data-value="all"]')
    ; Click input element
    _WD_ElementAction($sSession, $sElement, 'click')
    Sleep(2700)



    For $i = 1 To 9
        ;MsgBox(0, '', $i)
        Sleep(3400)
        ;MsgBox(0, '', '')
        Local $aResult = _WD_GetTable($sSession, '//table[@class="option-chain-tables__table"]')
        _DebugArrayDisplay($aResult)

        $sElement = _WD_FindElement($sSession, $_WD_LOCATOR_ByXPath, '//button[@class="pagination__next"]')
        ; Click input element
        _WD_ElementAction($sSession, $sElement, 'click')

    Next


    MsgBox(0, '', $sElement)

EndFunc   ;==>_ScrapeEconCalendar

Func SetupEdge()
    _WD_Option('Driver', 'msedgedriver.exe')
    _WD_Option('Port', 9515)
    _WD_Option('DriverParams', '--verbose --log-path="' & @ScriptDir & '\msedge.log"')

    $sDesiredCapabilities = '{"capabilities": {"alwaysMatch": {"ms:edgeOptions": {"binary": "' & StringReplace(@ProgramFilesDir, "\", "/") & '/Microsoft/Edge/Application/msedge.exe", "excludeSwitches": [ "enable-automation"], "useAutomationExtension": false}}}}'
EndFunc   ;==>SetupEdge

Thanks again

Link to comment
Share on other sites

Hi, 

I need some help again. With IE.au3 I have successfully managed to open websites and automate the login. How do I get the whole thing to work with wd_core and wd_helper? The webpages are already open, but I haven't found the function to search for e.g. <div id= "password"> in the webpage to perform the login. 

I just used this one 

_WD_GetElementByName($sSession,"password")

and get the result _WD_GetElementByName ==> Success. But i dont know how to go one from here.

Thanks a lot for your support ;)

Edited by Schmand
Link to comment
Share on other sites

@Schmand You save the result of that command to a variable. Then you can use it in commands like _WD_ElementAction or _WD_SetElementValue. Here's a short example --

Local $sPassElement = _WD_GetElementByName($sSession,"password")
_WD_SetElementValue($sSession, $sPassElement, 'mypassword')

You would obviously need to add error checking. 😉

Link to comment
Share on other sites

1 minute ago, Danp2 said:

@Schmand You save the result of that command to a variable. Then you can use it in commands like _WD_ElementAction or _WD_SetElementValue. Here's a short example --

Local $sPassElement = _WD_GetElementByName($sSession,"password")
_WD_SetElementValue($sSession, $sPassElement, 'mypassword')

You would obviously need to add error checking. 😉

Thanks a lot, that was the part I was looking for :)

Link to comment
Share on other sites

@Danp2 Iam back again. I actually have one more thing I'm trying to work out. I now have several tabs in which I change and enter the required data. Finally, I have to press the login button on each page. All pages have a similar structure. How do I get the button to press the following code?


image.png.38083f39d49dacc122cfedd4ce257e42.png

 

I tried

Local $sElement = _WD_FindElement($sSession, $_WD_LOCATOR_ByXPath, "//p[@class='buttonbar Anmeldung']")
    _WD_ElementAction($sSession, $sElement, "click")

But thats returns

_WD_ElementAction: {"value":{"error":"no such element","message":"no such element: Element_id length is invalid\n  (Ses...
_WD_ElementAction ==> No match: {"value":{"error":"no such element","message":"no such element: Element_id length is invalid\n  (Session info: chrome=90.0.4430.93)","stacktrace":"Backtrace:\n\tOrdinal0 [0x0045C013+2474003]\n\tOrdinal0 [0x003F29C1+2042305]\n\tOrdinal0 [0x00302F68+1060712]\n\tOrdinal0 [0x0032D141+1233217]\n\tOrdinal0 [0x0032EDFE+1240574]\n\tOrdinal0 [0x003247B3+1198003]\n\tOrdinal0 [0x00346813+1337363]\n\tOrdinal0 [0x003246A6+1197734]\n\tOrdinal0 [0x003468FA+1337594]\n\tOrdinal0 [0x003555CB+1398219]\n\tOrdinal0 [0x003466DB+1337051]\n\tOrdinal0 [0x00323427+1192999]\n\tOrdinal0 [0x003242EE+1196782]\n\tOrdinal0 [0x00324279+1196665]\n\tGetHandleVerifier [0x005E96FC+1590332]\n\tGetHandleVerifier [0x00698614+2306900]\n\tGetHandleVerifier [0x004E9E93+543699]\n\tGetHandleVerifier [0x004E92CE+540686]\n\tOrdinal0 [0x003F86BA+2066106]\n\tOrdinal0 [0x003FD1C8+2085320]\n\tOrdinal0 [0x003FD308+2085640]\n\tOrdinal0 [0x004067F3+2123763]\n\tBaseThreadInitThunk [0x77BCFA29+25]\n\tRtlGetAppContainerNamedObjectPath [0x77D17A7E+286]\n\tRtlGetAppContainerNamedObjectPath [0x77D17A4E+238]\n"}}
>Exit code: 0    Time: 4.74

Thanks for helping

Edited by Schmand
Link to comment
Share on other sites

@Schmand That's not how to make an XPath :) If you want to easily get an XPath, you can right click in the developer window on the element and select Copy -> Copy XPath / Copy Full XPath or you can install ChroPath that will help you build an XPath.

All my code provided is Public Domain... but it may not work. ;) Use it, change it, break it, whatever you want.

Spoiler

My Humble Contributions:
Personal Function Documentation - A personal HelpFile for your functions
Acro.au3 UDF - Automating Acrobat Pro
ToDo Finder - Find #ToDo: lines in your scripts
UI-SimpleWrappers UDF - Use UI Automation more Simply-er
KeePass UDF - Automate KeePass, a password manager
InputBoxes - Simple Input boxes for various variable types

Link to comment
Share on other sites

4 minutes ago, seadoggie01 said:

@Schmand That's not how to make an XPath :) If you want to easily get an XPath, you can right click in the developer window on the element and select Copy -> Copy XPath / Copy Full XPath or you can install ChroPath that will help you build an XPath.

Thank you. 

Local $sElement = _WD_FindElement($sSession, $_WD_LOCATOR_ByXPath, "//*[@id='loginform']/p[3]/button")

Was the correct value ;)

Link to comment
Share on other sites

  • Danp2 changed the title to WebDriver UDF (W3C compliant version) - 07/29/2022
  • Jos locked this topic
Guest
This topic is now closed to further replies.
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...