CodeWriter Posted May 1, 2021 Posted May 1, 2021 (edited) @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. expandcollapse popupLocal $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 May 1, 2021 by CodeWriter
seadoggie01 Posted May 1, 2021 Posted May 1, 2021 @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 expandcollapse popupFunc _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 functionsAcro.au3 UDF - Automating Acrobat ProToDo Finder - Find #ToDo: lines in your scriptsUI-SimpleWrappers UDF - Use UI Automation more Simply-erKeePass UDF - Automate KeePass, a password managerInputBoxes - Simple Input boxes for various variable types
CodeWriter Posted May 1, 2021 Posted May 1, 2021 @seadoggie01Thank you. I guess that "current" tab in the WebDriver notes doesn't mean currently active on screen, but just the last handle that WebDriver was using. seadoggie01 1
CodeWriter Posted May 2, 2021 Posted May 2, 2021 @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.
seadoggie01 Posted May 2, 2021 Posted May 2, 2021 @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! (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! expandcollapse popup; #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 functionsAcro.au3 UDF - Automating Acrobat ProToDo Finder - Find #ToDo: lines in your scriptsUI-SimpleWrappers UDF - Use UI Automation more Simply-erKeePass UDF - Automate KeePass, a password managerInputBoxes - Simple Input boxes for various variable types
CodeWriter Posted May 2, 2021 Posted May 2, 2021 (edited) @seadoggie01Thank you, get some sleep. Edited May 2, 2021 by CodeWriter seadoggie01 1
Blueman Posted May 2, 2021 Posted May 2, 2021 (edited) 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 expandcollapse popup; #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 expandcollapse popup; #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 May 2, 2021 by Blueman
Danp2 Posted May 2, 2021 Author Posted May 2, 2021 @Blueman Glad you were able to find a solution. FWIW, the Webdriver W3C specs dealing with shadowroot aren't "official" yet. Thus YMMV when it comes to functionality / support. Hopefully this will make it into their next working draft. Latest Webdriver UDF Release Webdriver Wiki FAQs
Danp2 Posted May 2, 2021 Author Posted May 2, 2021 @HJL It's a new style available in _WD_HighlightElement Latest Webdriver UDF Release Webdriver Wiki FAQs
Danp2 Posted May 2, 2021 Author Posted May 2, 2021 @HJL Are you using the latest version? From the header -- ; $iMethod - [optional] an integer value. Default is 1. ; 0=style -> Remove highlight ; Latest Webdriver UDF Release Webdriver Wiki FAQs
Marlon13 Posted May 3, 2021 Posted May 3, 2021 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 expandcollapse popup#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
Schmand Posted May 3, 2021 Posted May 3, 2021 (edited) 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 May 3, 2021 by Schmand
Danp2 Posted May 3, 2021 Author Posted May 3, 2021 @Marlon13 You may want to check the file msedge.log to see if there's any explanation for why it starts returning empty strings at row 20. On you new issue... You're really going to make me ask you again for the details necessary to help you? 😦 Latest Webdriver UDF Release Webdriver Wiki FAQs
Danp2 Posted May 3, 2021 Author Posted May 3, 2021 @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. 😉 Schmand 1 Latest Webdriver UDF Release Webdriver Wiki FAQs
Schmand Posted May 3, 2021 Posted May 3, 2021 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
Schmand Posted May 3, 2021 Posted May 3, 2021 (edited) @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? 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 May 3, 2021 by Schmand
seadoggie01 Posted May 3, 2021 Posted May 3, 2021 @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. Schmand 1 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 functionsAcro.au3 UDF - Automating Acrobat ProToDo Finder - Find #ToDo: lines in your scriptsUI-SimpleWrappers UDF - Use UI Automation more Simply-erKeePass UDF - Automate KeePass, a password managerInputBoxes - Simple Input boxes for various variable types
Schmand Posted May 3, 2021 Posted May 3, 2021 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
Recommended Posts