Automate interaction with Windows dialogs/windows using AutoIt - not needed SOAtest 9.0 and higher

LegacyForumLegacyForum Posts: 1,669 ✭✭
edited December 2016 in SOAtest
Plus IE modal/modeless dialogs (now handled natively)
During recording and playback of functional tests in the browser, SOAtest can handle web applications that create multiple windows. During recording, SOAtest detects the window in which you perform an action (click, type, etc.). SOAtest stores the window information, which is then used to find the element in the appropriate window during playback. You can manually configure window locators in existing tests.

Web applications primarily use the JavaScript function window.open to create a new window. SOAtest handles these windows as expected.

Internet Explorer -- and more recently, Firefox -- provide two additional functions for creating windows, window.showModalDialog and window.showModelessDialog. For technical reasons, SOAtest (current version 6.2) can neither record nor playback actions on a window created through these two methods.

You can handle modal/modeless dialogs during playback of web functional tests by using an AutoIt script. SOAtest can run the AutoIt script that then waits for and interacts with the modal/modeless dialog. In AutoIt, you can simulate the clicks necessary to interact with the dialog.

AutoIt allows you to create scripts to automate Windows tasks. From the AutoIt website:

It [AutoIt] uses a combination of simulated keystrokes, mouse movement and window/control manipulation in order to automate tasks in a way not possible or reliable with other languages (e.g. VBScript and SendKeys). AutoIt is also very small, self-contained and will run on all versions of Windows out-of-the-box with no annoying "runtimes" required!

You can download AutoIt here: http://www.autoitscript.com/

To utilize AutoIt in SOAtest, do the following:
  1. Create and compile an AutoIt script. You now have the file yourScriptName.exe.
  2. Test the script while manually testing the application.
  3. Once you have determined that the script does what you want, add an External Tool in SOAtest to run the script. The External Tool can run any program. You must add the External Tool before the browser test that creates the user dialog, so that the AutoIt script is waiting in the background for the dialog to appear. For example, the following scenario:
    1. Test 1: Some browser test
    2. Test 2: External Tool to start AutoIt script.
    3. Test 3: Browser test with user action that creates the modal/modeless dialog.
  4. Open the External Tool and configure the following options:
    • Executable: Path to compiled .exe you created with AutoIt.
    • Flags/Arguments: Whatever command line parameters are necessary to run the script. In the case of handling modal dialogs, typically these would provide a means to identify the dialog to use and the button to click.
    • Wait for executable to finish: uncheck this option, so that the script runs in the background.
AutoIt provides an extensive library of functions and excellent documentation. For more information on how to create and compile scripts, see the AutoIt documentation.

To get you started, consider the following script. It uses command line arguments to find the dialog and button to use, making the script general enough to handle many dialogs.

CODE
; For methods that start with _IE.
#include <IE.au3>

; To create an exe that outputs to the console, compile with Aut2Exe
; with the "Console" option selected.

; Clicks on a specified button within an IE modal dialog -- that is,
; a dialog created with window.showModalDialog.

; Also works with IE modeless dialogs -- that is, a dialog created
; with window.showModelessDialog.  Modal is more common, hence the
; name of the script.

; Identify the modal dialog using a substring of the window title.

; Identify the button to click on within the dialog by specifying
; either the id or value attribute value.
; An example button:
; <input type="button" id="closeButton" value="close"/>
; You can locate the button using either the "closeButton" id
; attribute value or the "close" value attribute value.

; See ParseArgs for the expected command line parameters.

; Require variable declaration to increase script maintainability.
Opt("MustDeclareVars", 1);0=no, 1=require pre-declare

; Break all functionality into functions to avoid global variables.
Main()

Func Main()
    Local $windowName = ""
    Local $elementId = ""
    Local $buttonValue = ""
    Local $timeout = 60
    Local $noForceClose = False
; Make a command line argument if useful.
    Const $sleepTime = 5000

    ParseArgs($windowName, $elementId, $buttonValue, $timeout, $noForceClose)
    $buttonValue = StringStripWS($buttonValue, 3)

    ConsoleWrite("Waiting for window with title = " & $windowName & @CRLF)
    Local $windowFound = WinWait($windowName, "", $timeout)
    If $windowFound Then
        ConsoleWrite("Found window." & @CRLF)
    Else
        ConsoleWrite("Could not find window: " & $windowName & @CRLF)
        Exit(1)
    EndIf

; Assume that this will always succeed because WinWait succeeded.
; Use window handle to avoid searching by window name each time,
; so as to ensure that we are consistenly working with the same
; window in the unlikely event that another window with the same
; name may appear.
    Local $dialogHandle = WinGetHandle($windowName, "")

; Give us some time to see the dialog for simple debugging.
    Sleep($sleepTime)
; Get a handle to the IE instance.  Use "DialogBox" search mode
; because it is a modal (or modeless) dialog that we are searching for.
    Local $ie = _IEAttach($dialogHandle, "DialogBox")
    If $ie == 0 Then
        ConsoleWrite("Window is not instance of IE: " & $windowName & @CRLF)
        Exit(1)
    EndIf

; Find the button we want and click it.
    Local $button = 0

    If $button == 0 Then
        $button = FindButtonByValue($ie, $buttonValue)
    EndIf

    If $button == 0 Then
        $button = FindButtonById($ie, $elementId)
    EndIf

    If $button == 0 Then
        ConsoleWrite("Could not find element using any parameters" & @CRLF)
    Else
        ConsoleWrite("Performing click action." & @CRLF)
        _IEaction($button, "click")
    EndIf

; Give window time to close.
    Sleep($sleepTime)

; If the window still exists then:
; We didn't find anything to click or...
; Our clicking didn't close the dialog.  Abort!  We don't want the dialog
; hanging around preventing further interaction with the other IE
; windows (assuming the dialog is modal).
    If WinExists($dialogHandle) Then
        If $noForceClose Then
            ConsoleWrite("Window is still open." & @CRLF)
        Else
            WinClose($dialogHandle)
            ConsoleWrite("Forced window to close." & @CRLF)
        EndIf
        Exit(1)
    Else
        ConsoleWrite("Closed window via click." & @CRLF)
    EndIf
EndFunc

Func ParseArgs(ByRef $windowName, ByRef $elementId, ByRef $buttonValue, _
    ByRef $timeout, ByRef $noForceClose)
    Local $argc = $CmdLine[0]
    For $i = 1 to $argc
        Local $arg = $CmdLine[$i]
        Switch $arg
        Case "--window"
            $i += 1
            $windowName = $CmdLine[$i]
             ; id attribute value of button to click on in dialog.
        Case "--element-id"
            $i += 1
            $elementId = $CmdLine[$i]
             ; value attribute value of button to click on in dialog.
        Case "--button-value"
            $i += 1
            $buttonValue = $CmdLine[$i]
        Case "--timeout"
            $i += 1
            $timeout = Int($CmdLine[$i])
             ; Don't forcibly close the dialog if we found the dialog but not the
             ; button, or if clicking on the button did not close the dialog.
        Case "--no-force-close"
            $noForceClose = True
        Case Else
            ConsoleWrite("Unknown option " & $arg & @CRLF)
        EndSwitch
    Next
EndFunc

Func FindButtonByValue($ie, $buttonValue)
    Local $button = 0

    If Not StringIsSpace($buttonValue) Then
        Local $inputs = _IETagNameGetCollection($ie, "input")
        For $element In $inputs
            Local $value = StringStripWS($element.value, 3)
            If $value == $buttonValue And $element.type == "button" Then
                $button = $element
                ConsoleWrite("Found element using button value = '" & _
                    $buttonValue & "'" & @CRLF)
                ExitLoop
            EndIf
        Next
    EndIf

    If $button == 0 Then
        ConsoleWrite("Could not find element using button value = '" & _
            $buttonValue & "'" & @CRLF)
    EndIf

    Return $button
EndFunc

Func FindButtonById($ie, $elementId)
    Local $button = 0

    If Not StringIsSpace($elementId) Then
        $button = _IEGetObjById($ie, $elementId)
    EndIf

    If $button <> 0 Then
        ConsoleWrite("Found element using element id = '" & _
            $elementId & "'" & @CRLF)
    Else
        ConsoleWrite("Could not find element using element id = '" & _
            $elementId & "'" & @CRLF)
    EndIf

    Return $button
EndFunc
Tagged:

Comments

  • LegacyForumLegacyForum Posts: 1,669 ✭✭
    SOAtest 9.0 and higher handle modal and modeless dialogs natively: you no longer need to use AutoIt. That said, AutoIt remains a useful tool for handling windows and dialogs from the browser that are not HTML pages. For example, to handle a "Save As" dialog you would need to use AutoIt to get a handle to the dialog, type into it, and click the desired button. Browser test offer means to interact with HTML documents, not any arbitrary window created by the operating system.
  • LegacyForumLegacyForum Posts: 1,669 ✭✭
    Windows print dialog: select printer, then print

    Here is an example on how to interact with a print dialog in Windows. The script waits for a window with the name "Print", then chooses the desired printer. Modify the script to press the button to actually print the document: pressing the button to print is commented out to make it easy to see that it selects the proper printer (and otherwise debugging your script) without using reams of paper.

    Two print types of print dialogs are common, one with a combo box (drop-down list) of available printers and an "OK" button to print; the other with a list view of printers and a "Print" button to print. This script attempts to handle both.

    To determine the criteria to use to find a control in a given window, use the "AutoIt Window Info" application. This application provided the information to create control locators such as "[CLASS:SysListView32; Text:FolderView]" and "[CLASS:Button; Text:&Print]" used below.

    You may be tempted to use simple commands to send keystrokes. However, note that sending keystrokes does not work as expected if the application you want to interact with is running without a display, such as if you run the application using a Hudson job. Finding the controls to interact with and using AutoIt functions to send commands to the controls will work whether or not the application is visible.

    CODE
    ; Require variable declaration to increase script maintainability.
    Opt("MustDeclareVars", 1);0=no, 1=require pre-declare

    Main()

    ; Avoid global variables by implementing everything in functions
    ; and using the "Local" keyword.  In this script it doesn't matter (there
    ; is only one function), but this is a good practice to maintain to make
    ; your scripts less mysterious.
    Func Main()
        Local Const $dialogTitle = "Print"
        Local Const $timeout = 20
       ; Printer name should be a command line argument.
        Local Const $printerName = "Microsoft XPS Document Writer"
        Local $windowFound = WinWait($dialogTitle, "", $timeout)
       ; For some reason if the print dialog contains the list view (see below)
       ; and the window did not exist before starting WinWait, then the
       ; window returned seems to be something transient that we can't work
       ; with.  Therefore, try to find the window again.  Unclear why this
       ; would be necessary, but this seems peculiar to this type of print
       ; dialog.
        $windowFound = WinWait($dialogTitle, "", $timeout)
        Local $windowHandle

        If $windowFound Then
           ; Get the last window used by an AutoIt function, namely the window
           ; we were waiting for.
            $windowHandle = WinGetHandle("[LAST]")
        Else
            ConsoleWrite("Could not find window " & $dialogTitle & @CRLF)
            Exit(1)
        EndIf

        Local $listViewHandle = ControlGetHandle($windowHandle, "", "[CLASS:SysListView32; Text:FolderView]")
        If $listViewHandle <> "" Then
           ; ControlListView sometimes doesn't work if this script is compiled as 32-bit
           ; and the application that is printing is 64-bit, or vice versa.
           ; See the documentation for ControlListView.
            ConsoleWrite("Using listview" & @CRLF)
            Local $printerIndex = ControlListView($windowHandle, "", $listViewHandle, "FindItem", $printerName)
            ConsoleWrite("index: " & $printerIndex & @CRLF)
            If $printerIndex < 0 Then
                ConsoleWrite("Could not find printer " & $printerName & " in print list view" & @CRLF)
                Exit(1)
            EndIf
            ControlListView($windowHandle, "", $listViewHandle, "Select", $printerIndex)
           ; Enable the statement below to print.
           ; ControlClick($windowHandle, "", "[CLASS:Button; Text:&Print]")
        Else
           ; We have an old-style print dialog.
           ; Unlike the print dialog with the list view used above, a
           ; 32-bit/64-bit combination of script and application (or vice versa)
           ; is not a problem.
            ConsoleWrite("Using combo" & @CRLF)
            ControlCommand($windowHandle, "", "[CLASS:ComboBox; INSTANCE:1]", "SelectString", $printerName)
           ; Enable the statement below to print.
           ; ControlClick($windowHandle, "", "[CLASS:Button; Text:OK]")
        EndIf

       ; Enable the statement below to cancel printing.
       ; ControlClick($windowHandle, "", "[CLASS:Button; Text:Cancel]")
    EndFunc
  • LegacyForumLegacyForum Posts: 1,669 ✭✭

    Windows print dialog: select printer, then print

    Here is an example on how to interact with a print dialog in Windows. The script waits for a window with the name "Print", then chooses the desired printer. Modify the script to press the button to actually print the document: pressing the button to print is commented out to make it easy to see that it selects the proper printer (and otherwise debugging your script) without using reams of paper.

    Two print types of print dialogs are common, one with a combo box (drop-down list) of available printers and an "OK" button to print; the other with a list view of printers and a "Print" button to print. This script attempts to handle both.

    To determine the criteria to use to find a control in a given window, use the "AutoIt Window Info" application. This application provided the information to create control locators such as "[CLASS:SysListView32; Text:FolderView]" and "[CLASS:Button; Text:&Print]" used below.

    You may be tempted to use simple commands to send keystrokes. However, note that sending keystrokes does not work as expected if the application you want to interact with is running without a display, such as if you run the application using a Hudson job. Finding the controls to interact with and using AutoIt functions to send commands to the controls will work whether or not the application is visible.

    CODE
    ; Require variable declaration to increase script maintainability.
    Opt("MustDeclareVars", 1);0=no, 1=require pre-declare

    Main()

    ; Avoid global variables by implementing everything in functions
    ; and using the "Local" keyword.  In this script it doesn't matter (there
    ; is only one function), but this is a good practice to maintain to make
    ; your scripts less mysterious.
    Func Main()
        Local Const $dialogTitle = "Print"
        Local Const $timeout = 20
      ; Printer name should be a command line argument.
        Local Const $printerName = "Microsoft XPS Document Writer"
        Local $windowFound = WinWait($dialogTitle, "", $timeout)
      ; For some reason if the print dialog contains the list view (see below)
      ; and the window did not exist before starting WinWait, then the
      ; window returned seems to be something transient that we can't work
      ; with.  Therefore, try to find the window again.  Unclear why this
      ; would be necessary, but this seems peculiar to this type of print
      ; dialog.
        $windowFound = WinWait($dialogTitle, "", $timeout)
        Local $windowHandle

        If $windowFound Then
          ; Get the last window used by an AutoIt function, namely the window
          ; we were waiting for.
            $windowHandle = WinGetHandle("[LAST]")
        Else
            ConsoleWrite("Could not find window " & $dialogTitle & @CRLF)
            Exit(1)
        EndIf

        Local $listViewHandle = ControlGetHandle($windowHandle, "", "[CLASS:SysListView32; Text:FolderView]")
        If $listViewHandle <> "" Then
          ; ControlListView sometimes doesn't work if this script is compiled as 32-bit
          ; and the application that is printing is 64-bit, or vice versa.
          ; See the documentation for ControlListView.
            ConsoleWrite("Using listview" & @CRLF)
            Local $printerIndex = ControlListView($windowHandle, "", $listViewHandle, "FindItem", $printerName)
            ConsoleWrite("index: " & $printerIndex & @CRLF)
            If $printerIndex < 0 Then
                ConsoleWrite("Could not find printer " & $printerName & " in print list view" & @CRLF)
                Exit(1)
            EndIf
            ControlListView($windowHandle, "", $listViewHandle, "Select", $printerIndex)
          ; Enable the statement below to print.
          ; ControlClick($windowHandle, "", "[CLASS:Button; Text:&Print]")
        Else
          ; We have an old-style print dialog.
          ; Unlike the print dialog with the list view used above, a
          ; 32-bit/64-bit combination of script and application (or vice versa)
          ; is not a problem.
            ConsoleWrite("Using combo" & @CRLF)
            ControlCommand($windowHandle, "", "[CLASS:ComboBox; INSTANCE:1]", "SelectString", $printerName)
          ; Enable the statement below to print.
          ; ControlClick($windowHandle, "", "[CLASS:Button; Text:OK]")
        EndIf

      ; Enable the statement below to cancel printing.
      ; ControlClick($windowHandle, "", "[CLASS:Button; Text:Cancel]")
    EndFunc
  • LegacyForumLegacyForum Posts: 1,669 ✭✭
    Here is a script to close the "The web page you are viewing is trying to close the window" dialog box:

    Global $foundOne
    Global $sleepVal
    $sleepVal = 1000

    While 1

    process_window_if_it_exists("Windows Internet Explorer", "The webpage you are viewing is trying to close the window.","Button1") ;IE7
    process_window_if_it_exists("Microsoft Internet Explorer", "The Web page you are viewing is trying to close the window.","Button1") ;IE6
    process_window_if_it_exists("Warning: Unresponsive script", "A script on this page may be busy, or it may have stopped responding.", "Continue") ;FireFox 2

    if $foundOne < 10 Then
    $sleepVal = 100
    $foundOne = $foundOne + 1
    Else
    $foundOne = 11
    $sleepVal = 5000 ;5 seconds
    EndIf
    sleep($sleepVal)

    wend

    func process_window_if_it_exists($winTitle, $checkMsg, $buttonName)
    if WinExists($winTitle,"") Then
    if StringLeft(ControlGetText($winTitle,"","Static2"),StringLen($checkMsg)) = $checkMsg then
    ControlClick($winTitle,"",$buttonName)
    sleep(1000)
    ControlClick($winTitle,"",$buttonName)
    Exit(1)
    endif
    sleep(100)
    $foundOne = 1
    EndIf
    endfunc
  • LegacyForumLegacyForum Posts: 1,669 ✭✭
    Handle Internet Explorer file download dialog

    For when clicking (or some other action) results in Internet Explorer prompting you whether you want to save a file and where. Note that for SOAtest to trigger the file save dialog in IE, you must enable "Automatic prompting for file downloads" in the IE settings. That is, select the following in IE: Tools > Internet Options > Security > Internet (or whatever zone your tested site is in) > Custom Level... > Downloads > Automatic prompting for file downloads > Enable.

    As with other AutoIt scripts, compile the script into a .exe file and use an external tool to start the .exe before the user action that generates the file save dialog.

    CODE
    Opt("MustDeclareVars", 1);0=no, 1=require pre-declare

    Const $defaultTimeoutSeconds = 20
    Const $defaultSleepMs = 2000

    Main()

    Func Main()
        Local $outputFileName = "c:\tmp\output.txt"
        Local $downloadWaitSeconds = 60
        Local $cancel = False
        ParseArgs($outputFileName, $downloadWaitSeconds, $cancel)

        Local $fileDownloadWindowHandle = GetWindowHandle("File Download")

        If $cancel Then
            ClickButtonInWindow($fileDownloadWindowHandle, "Cancel")
        Else
            ClickButtonInWindow($fileDownloadWindowHandle, "&Save")
            CompleteSaveAsDialog($outputFileName)
            HandleConfirmationDialog()
            CloseDownloadCompleteDialog($downloadWaitSeconds)
        EndIf
    EndFunc

    Func ClickButtonInWindow($windowHandle, $buttonText)
        WinActivate($windowHandle)
       ; Unclear why sleeping is necessary, but if you perform the click too
       ; soon then the active control changes to the save button but it
       ; does not appear to be clicked.
        Sleep($defaultSleepMs)
        ControlClick($windowHandle, "", "[CLASS:Button; TEXT:" & $buttonText & "]")
    EndFunc

    Func CompleteSaveAsDialog( _
        $outputFileName, _
        $timeoutSeconds = $defaultTimeoutSeconds)
        Local $windowHandle = GetWindowHandle("Save As", $timeoutSeconds)
        WinActivate($windowHandle)
        Sleep($defaultSleepMs)
        ControlSetText($windowHandle, "", "[CLASS:Edit; INSTANCE:1]", $outputFileName)
        ControlClick($windowHandle, "", "[CLASS:Button; TEXT:&Save]")
    EndFunc

    Func HandleConfirmationDialog($timeoutSeconds = 5)
       ; This won't appear if there there is not a preexisting file with
       ; the same name.  Therefore, it is not an error if the wait times out.
        Local $windowHandle = WinWait("Confirm Save As", "", $timeoutSeconds)
        If $windowHandle <> 0 Then
            WinActivate($windowHandle)
            Sleep($defaultSleepMs)
            ControlClick($windowHandle, "", "[CLASS:Button; TEXT:&Yes]")
        EndIf
    EndFunc


    Func CloseDownloadCompleteDialog($downloadWaitSeconds)
       ; Whether the dialog appears upon the download completing is dependent
       ; on your settings.  Therefore, it is not an error if the wait times out.
        Local $windowHandle = WinWait("Download complete", "", $downloadWaitSeconds)
        If $windowHandle <> 0 Then
            WinClose($windowHandle)
        EndIf
    EndFunc

    Func GetWindowHandle($windowTitle, $timeoutSeconds = $defaultTimeoutSeconds)
        Local $windowHandle = WinWait($windowTitle, "", $timeoutSeconds)
        If $windowHandle == 0 Then
            ConsoleWrite("Could not find window " & $windowTitle & @CRLF)
            Exit(1)
        EndIf

        Return $windowHandle
    EndFunc

    Func ParseArgs( _
        ByRef $outputFileName, _
        ByRef $downloadWaitSeconds, _
        ByRef $cancel)
        Local $argc = $CmdLine[0]
        For $i = 1 to $argc
            Local $arg = $CmdLine[$i]
            Switch $arg
            Case "--file"
                $i += 1
                $outputFileName = $CmdLine[$i]
            Case "--download-wait"
                $i += 1
                $downloadWaitSeconds = Int($CmdLine[$i])
            Case "--cancel"
                $cancel = True
            Case Else
                ConsoleWrite("Unknown option " & $arg & @CRLF)
            EndSwitch
        Next
    EndFunc
Sign In or Register to comment.