Jump to content

WebDriver UDF (W3C compliant version) - 2022/10/03


Recommended Posts

  • Replies 998
  • Created
  • Last Reply

Top Posters In This Topic

Top Posters In This Topic

Popular Posts

Latest update just released. See below for change log.  

Posted Images

I've been using this with SalesForce and have a decent amount of luck with it.

 

I needed to make a couple changes to two functions so far

_WD_FrameEnter()  SalesForce has many nested Frames and I needed to remove the $_WD_ELEMENT_ID in order to access any frame.

; #FUNCTION# ====================================================================================================================
; Name ..........: _WD_FrameEnter
; Description ...: This will enter the specified frame for subsequent WebDriver operations.
; Syntax ........: _WD_FrameEnter($sIndexOrID)
; Parameters ....:
; Return values .: Success      - True
;                  Failure      - WD Response error message (E.g. "no such frame")
; Author ........: Decibel
; Modified ......: 2018-04-27
; Remarks .......:
; Related .......:
; Link ..........:
; Example .......: No
; ===============================================================================================================================
Func _WD_FrameEnter($sSession, $sIndexOrID)
    Local $sOption
    Local $sResponse, $sJSON
    Local $sValue

    ;*** Encapsulate the value if it's an integer, assuming that it's supposed to be an Index, not ID attrib value.
    If IsInt($sIndexOrID) = True Then
        $sOption = '{"id":' & $sIndexOrID & '}'
    Else
;~      $sOption = '{"id":{"' & $_WD_ELEMENT_ID & '":"' & $sIndexOrID & '"}}'   ;This won't work with Salesforce for some reason
        $sOption = '{"id":"' & $sIndexOrID & '"}'
    EndIf

    $sResponse = _WD_Window($sSession, "frame", $sOption)
    $sJSON = Json_Decode($sResponse)
    $sValue = Json_Get($sJSON, "[value]")

    ;*** Evaluate the response
    If $sValue <> Null Then
        $sValue = Json_Get($sJSON, "[value][error]")
    Else
        $sValue = True
    EndIf

    Return $sValue
EndFunc ;==>_WD_FrameEnter

 

 

Since SalesForce can take a long time to load some pages and controls, I'm using _WD_WaitElement a lot, so I updated it to return the Element(s) once it finally shows up.

; #FUNCTION# ====================================================================================================================
; Name ..........: _WD_WaitElement
; Description ...: Wait for a element to be found  in the current tab before returning
; Syntax ........: _WD_WaitElement($sSession, $sElement, $sStrategy, $sSelector[, $iDelay = 0[, $iTimeout = -1]])
; 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 "".
;                  $lMultiple           - [optional] True returns Array of Element ID's. Default is False.
;                  $iDelay              - [optional] Milliseconds to wait before checking status
;                  $iTimeout            - [optional] Period of time to wait before exiting function
; Return values .: Success      - Element ID(s) returned by web driver
;                  Failure      - 0 and sets the @error flag to non-zero
;                  @error       - $_WD_ERROR_Success
;                               - $_WD_ERROR_Timeout
; Author ........: Dan Pollak
; Modified ......:
; Remarks .......:
; Related .......:
; Link ..........:
; Example .......: No
; ===============================================================================================================================
Func _WD_WaitElement($sSession, $sStrategy, $sSelector, $sStartElement = "", $lMultiple = False, $iDelay = 0, $iTimeout = -1)
    Local Const $sFuncName = "_WD_WaitElement"
    Local $bAbort = False, $iErr, $iResult

    If $iTimeout = -1 Then $iTimeout = $_WD_DefaultTimeout

    Sleep($iDelay)

    Local $hWaitTimer = TimerInit()

    While 1
        $FindRet = _WD_FindElement($sSession, $sStrategy, $sSelector, $sStartElement, $lMultiple)
        $iErr = @error

        If $iErr = $_WD_ERROR_Success Then
            $iResult = 1
            ExitLoop

        ElseIf $iErr = $_WD_ERROR_NoMatch Then
            If (TimerDiff($hWaitTimer) > $iTimeout) Then
                $iErr = $_WD_ERROR_Timeout
                ExitLoop
            EndIf
        Else
            ExitLoop
        EndIf

        Sleep(1000)
    WEnd

    Return SetError(__WD_Error($sFuncName, $iErr), $iResult, $FindRet)
EndFunc

 

Link to post
Share on other sites

@BigDaddyO Based on my limited testing, I believe the issue lies with Chromedriver not conforming to the W3C specs. Firefox works fine with the UDF as-is, but fails when I implement your change. Chrome failed for me either way, returning

__WD_Post: StatusCode=500; ResponseText={"value":{"error":"unknown error","message":"missing 'ELEMENT'(Session infochrome=67.0.3396.99)","stacktrace":"Backtrace:\n\tOrdinal0 [0x00C0DF70+778096]\n\tOrdinal0 [0x00BBB42D+439341]\n\tOrdinal0 [0x00B9807F+295039]\n\tOrdinal0 [0x00B80619+198169]\n\tOrdinal0 [0x00B78349+164681]\n\tOrdinal0 [0x00B7F96B+194923]\n\tOrdinal0 [0x00B7824F+164431]\n\tOrdinal0 [0x00B61B45+72517]\n\tOrdinal0 [0x00B62F2A+77610]\n\tOrdinal0 [0x00B62ECC+77516]\n\tGetHandleVerifier [0x00CA9936+3478]\n\tOrdinal0 [0x00C188C3+821443]\n\tOrdinal0 [0x00BC7066+487526]\n\tOrdinal0 [0x00BC7393+488339]\n\tOrdinal0 [0x00BC74A3+488611]\n\tOrdinal0 [0x00C1AA67+830055]\n\tOrdinal0 [0x00BC6DAF+486831]\n\tOrdinal0 [0x00BD13FE+529406]\n\tOrdinal0 [0x00BDC57B+574843]\n\tOrdinal0 [0x00BDC6CD+575181]\n\tOrdinal0 [0x00BDB92B+571691]\n\tBaseThreadInitThunk [0x75FA8484+36]\n\tRtlValidSecurityDescriptor [0x77362FEA+282]\n\tRtlValidSecurityDescriptor [0x77362FBA+234]\n"}}

in one case and

__WD_Post: StatusCode=404; ResponseText={"value":{"error":"no such frame","message":"(Session infochrome=67.0.3396.99)","stacktrace":"Backtrace:\n\tOrdinal0 [0x00C0DF70+778096]\n\tOrdinal0 [0x00BBB42D+439341]\n\tOrdinal0 [0x00B97E1D+294429]\n\tOrdinal0 [0x00B9A2C1+303809]\n\tOrdinal0 [0x00B80787+198535]\n\tOrdinal0 [0x00B78349+164681]\n\tOrdinal0 [0x00B7F96B+194923]\n\tOrdinal0 [0x00B7824F+164431]\n\tOrdinal0 [0x00B61B45+72517]\n\tOrdinal0 [0x00B62F2A+77610]\n\tOrdinal0 [0x00B62ECC+77516]\n\tGetHandleVerifier [0x00CA9936+3478]\n\tOrdinal0 [0x00C188C3+821443]\n\tOrdinal0 [0x00BC7066+487526]\n\tOrdinal0 [0x00BC7393+488339]\n\tOrdinal0 [0x00BC74A3+488611]\n\tOrdinal0 [0x00C1AA67+830055]\n\tOrdinal0 [0x00BC6DAF+486831]\n\tOrdinal0 [0x00BD13FE+529406]\n\tOrdinal0 [0x00BDC57B+574843]\n\tOrdinal0 [0x00BDC6CD+575181]\n\tOrdinal0 [0x00BDB92B+571691]\n\tBaseThreadInitThunk [0x75FA8484+36]\n\tRtlValidSecurityDescriptor [0x77362FEA+282]\n\tRtlValidSecurityDescriptor [0x77362FBA+234]\n"}}

in the other.

Edit: Seems they are already aware of the issue.

Edited by Danp2
Link to post
Share on other sites

@BigDaddyO Regarding the enhancements to _WD_WaitElement, I understand the addition of the optional parameters to take full advantage of _WD_FindElement.

Not sure I see the real benefit of returning the found elements. Please elaborate on how you are using this and why you this change is more desirable than making another call to _WD_FindElement.

Link to post
Share on other sites
8 hours ago, Danp2 said:

@BigDaddyO Regarding the enhancements to _WD_WaitElement, I understand the addition of the optional parameters to take full advantage of _WD_FindElement.

Not sure I see the real benefit of returning the found elements. Please elaborate on how you are using this and why you this change is more desirable than making another call to _WD_FindElement.

I guess because I'm lazy and don't want to write another line when the _WD_WaitElement just performed the _WD_FindElement command. 

Granted, the extra two options "", True in the function are a little annoying, perhaps putting those to the end since someone would most likely use the Delay and Wait options more often.

Link to post
Share on other sites
  • 3 weeks later...

@Danp2 I created a _WD_WaitElementVisible() function based off your _WD_WaitElement() function.

For most elements I don't need this, but I came across a popup that seems to get created and is hidden in the background after logging into the website so the _WD_WaitElement() returned right away.  I needed to create this so it will wait for the element to exist AND be visible.  I included the link to where I found the code which is apparently what the JQuery code .is(':visible') uses.

OLD  updated function is 2 posts below!

; #FUNCTION# ====================================================================================================================
; Name ..........: _WD_WaitElementVisible
; Description ...: Wait for a element to be found in the current tab and that its visible before returning
; Syntax ........: _WD_WaitElementVisible($sSession, $sElement, $sStrategy, $sSelector[, $iDelay = 0[, $iTimeout = -1]])
; Parameters ....: $sSession            - Session ID from _WDCreateSession
;                  $sStrategy           - Locator strategy. See defined constant $_WD_LOCATOR_* for allowed values
;                  $sSelector           - Value to find
;                  $iDelay              - [optional] Milliseconds to wait before checking status
;                  $iTimeout            - [optional] Period of time to wait before exiting function
; Return values .: Success      - 1
;                  Failure      - 0 and sets the @error flag to non-zero
;                  @error       - $_WD_ERROR_Success
;                               - $_WD_ERROR_Timeout
; Author ........: Dan Pollak, BigDaddyO added visible
; Modified ......:
; Remarks .......:
; Related .......:
; Link ..........: https://stackoverflow.com/questions/19669786/check-if-element-is-visible-in-dom/28933648
; Example .......: No
; ===============================================================================================================================
Func _WD_WaitElementVisible($sSession, $sStrategy, $sSelector, $iDelay = 0, $iTimeout = -1)
    Local Const $sFuncName = "_WD_WaitElement"
    Local $bAbort = False, $iErr, $iResult

    If $iTimeout = -1 Then $iTimeout = $_WD_DefaultTimeout

    Sleep($iDelay)

    Local $hWaitTimer = TimerInit()

    While 1
        $FindRet = _WD_FindElement($sSession, $sStrategy, $sSelector)
        $iErr = @error

        If $iErr = $_WD_ERROR_Success Then
            Local $sJsonElement = '{"' & $_WD_ELEMENT_ID & '":"' & $FindRet & '"}'
            $sResponse = _WD_ExecuteScript($sSession, "return arguments[0].offsetWidth || arguments[0].offsetHeight || arguments[0].getClientRects().length", $sJsonElement)
            ;Not visible returns {"value":0}
            ;Visible returns a # for example {"value":115}
            $sJSON = Json_Decode($sResponse)            ;Use Json_Decode to pull just the value from the response
            $isVisible = Json_Get($sJSON, "[value]")
            If $isVisible > 0 Then
                $iResult = 1
                ExitLoop
            EndIf

        ElseIf $iErr = $_WD_ERROR_NoMatch Then
            If (TimerDiff($hWaitTimer) > $iTimeout) Then
                $iErr = $_WD_ERROR_Timeout
                ExitLoop
            EndIf
        Else
            ExitLoop
        EndIf

        Sleep(1000)
    WEnd

    Return SetError(__WD_Error($sFuncName, $iErr), $iResult)
EndFunc

 

Edited by BigDaddyO
Danp2 found a better option
Link to post
Share on other sites
3 hours ago, Danp2 said:

@BigDaddyO I haven't tested this, but the w3c specs show that you can check the element's visibility like this --

/session/{session id}/element/{element id}/displayed

 

 

That worked! at least in Chrome.  I did have to update the _WD_ElementAction() Function to include 'displayed' as a valid command

    Switch $sCommand
        Case 'name', 'rect', 'text', 'selected', 'enabled', 'displayed'
 

; #FUNCTION# ====================================================================================================================
; Name ..........: _WD_WaitElementVisible
; Description ...: Wait for a element to be found in the current tab and that its visible before returning
; Syntax ........: _WD_WaitElementVisible($sSession, $sElement, $sStrategy, $sSelector[, $iDelay = 0[, $iTimeout = -1]])
; Parameters ....: $sSession            - Session ID from _WDCreateSession
;                  $sStrategy           - Locator strategy. See defined constant $_WD_LOCATOR_* for allowed values
;                  $sSelector           - Value to find
;                  $iDelay              - [optional] Milliseconds to wait before checking status
;                  $iTimeout            - [optional] Period of time to wait before exiting function
; Return values .: Success      - 1
;                  Failure      - 0 and sets the @error flag to non-zero
;                  @error       - $_WD_ERROR_Success
;                               - $_WD_ERROR_Timeout
; Author ........: Dan Pollak
; Modified ......:
; Remarks .......:
; Related .......:
; Link ..........:
; Example .......: No
; ===============================================================================================================================
Func _WD_WaitElementVisible($sSession, $sStrategy, $sSelector, $iDelay = 0, $iTimeout = -1)
    Local Const $sFuncName = "_WD_WaitElementVisible"
    Local $bAbort = False, $iErr, $iResult, $isVisible

    If $iTimeout = -1 Then $iTimeout = $_WD_DefaultTimeout

    Sleep($iDelay)

    Local $hWaitTimer = TimerInit()

    While 1
        $FindRet = _WD_FindElement($sSession, $sStrategy, $sSelector)
        $iErr = @error

        If $iErr = $_WD_ERROR_Success Then
            $isVisible = _WD_ElementAction($sSession, $FindRet, 'displayed')
            if $isVisible = True Then
                $iResult = 1
                ExitLoop
            EndIf

        ElseIf $iErr = $_WD_ERROR_NoMatch Then
            If (TimerDiff($hWaitTimer) > $iTimeout) Then
                $iErr = $_WD_ERROR_Timeout
                ExitLoop
            EndIf
        Else
            ExitLoop
        EndIf

        Sleep(1000)
    WEnd

    Return SetError(__WD_Error($sFuncName, $iErr), $iResult)
EndFunc

 

Edited by BigDaddyO
change needed to _WD_ElementAction()
Link to post
Share on other sites

Yeah, that would make sense.

I'm not sure yet if it should default to IsVisible or not.  I'm still using my updated _WD_WaitElement that returns an elementID but it gets errors occasionally about stale elements so I have been meaning to change my code back to your Standard, it's just going to be a lot of work.

 

Also, It seems that the function can use = True instead of = "true" so I'm going to update the function in the above post

Link to post
Share on other sites

Here's the revised _WD_WaitElement with the optional visibility check. Seems to work well in my testing. Let me know if you encounter any issues.

; #FUNCTION# ====================================================================================================================
; Name ..........: _WD_WaitElement
; Description ...: Wait for a element to be found  in the current tab before returning
; Syntax ........: _WD_WaitElement($sSession, $sStrategy, $sSelector[, $iDelay = 0[, $iTimeout = -1[, $lVisible = False]]])
; Parameters ....: $sSession            - Session ID from _WDCreateSession
;                  $sStrategy           - Locator strategy. See defined constant $_WD_LOCATOR_* for allowed values
;                  $sSelector           - Value to find
;                  $iDelay              - [optional] Milliseconds to wait before checking status
;                  $iTimeout            - [optional] Period of time to wait before exiting function
;                  $lVisible            - [optional] Check visibility of element?
; Return values .: Success      - 1
;                  Failure      - 0 and sets the @error flag to non-zero
;                  @error       - $_WD_ERROR_Success
;                               - $_WD_ERROR_Timeout
; Author ........: Dan Pollak
; Modified ......:
; Remarks .......:
; Related .......:
; Link ..........:
; Example .......: No
; ===============================================================================================================================
Func _WD_WaitElement($sSession, $sStrategy, $sSelector, $iDelay = 0, $iTimeout = -1, $lVisible = False)
    Local Const $sFuncName = "_WD_WaitElement"
    Local $bAbort = False, $iErr, $iResult, $sElement, $lIsVisible = True

    If $iTimeout = -1 Then $iTimeout = $_WD_DefaultTimeout

    Sleep($iDelay)

    Local $hWaitTimer = TimerInit()

    While 1
        $sElement = _WD_FindElement($sSession, $sStrategy, $sSelector)
        $iErr = @error

        If $iErr = $_WD_ERROR_Success Then
            If $lVisible Then
                $lIsVisible = _WD_ElementAction($sSession, $sElement, 'displayed')
            EndIf

            If $lIsVisible = True Then
                $iResult = 1
                ExitLoop
            EndIf

        ElseIf $iErr <> $_WD_ERROR_NoMatch Then
            ExitLoop
        EndIf

        If (TimerDiff($hWaitTimer) > $iTimeout) Then
            $iErr = $_WD_ERROR_Timeout
            ExitLoop
        EndIf

        Sleep(1000)
    WEnd

    Return SetError(__WD_Error($sFuncName, $iErr), $iResult)
EndFunc

 

Link to post
Share on other sites

Looks like the Return isn't setup properly in the _WD_WaitElement() 

It is returning the $iResult in @Extended.  I think it should be something like this:  Return SetError(__WD_Error($sFuncName, $iErr), 0, $iResult)

 

Also, in the Local line, should $iResult be preset to 0 to return 0 if it errors?

Edited by BigDaddyO
Link to post
Share on other sites
  • Danp2 changed the title to WebDriver UDF (W3C compliant version) - 2022/10/03
  • 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
  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...