Tutorials

The Sikuli tutorials have sufficient information to get started, but it is useful to have a GUI to create tests with, so using http://www.pharocasts.com/2011/02/pharo-gui-with-polymorph.html, the Pharo class WidgetExamples new exampleBasicControls can quickly make a GUI that works good enough (for Smalltalk, there is also an event recorder that does something similar to this). One major advantage of using Smalltalk for this is an older version of Smalltalk can be found that has a UI that matches the expectation (for example the window resize tutorial fails because Apple has updated the bottom right corner and scrollbars for safari).

Sikuli Tutorials:

  1. Uncheck check-boxes - something like this is needed:
    A bunch of checkboxes
    A do-it for the below is sufficient:

    | builder dialog checkBoxes |
    builder := ExampleBuilderMorph new.
    dialog := (builder newPluggableDialogWindow: 'Example groups') useDefaultOKButton.
    checkBoxes := Array new: 5.
    1 to: 5 do: [ :index | checkBoxes at: index put: (dialog newCheckboxFor: (ValueHolder new contents: true)
    						getSelected: #contents setSelected: #contents:
    						label: 'The ', index asString,' Checkbox'
    						help: 'This is a checkbox') ].
    dialog contentMorph: ((dialog newRow: {
    	dialog newGroupbox: 'A bunch of checkboxes' forAll: checkBoxes})
    		vResizing: #spaceFill);
    	model: nil.
    builder openModal: dialog.

    The Sikuli code is:

    click(Example groups)        # Necessary to ensure the window has focus
    for x in findAll([] The)
      click(x)

  2. Check the Right Checkbox
    Example goups - The Second Checkbox is right

    | builder dialog |
    builder := ExampleBuilderMorph new.
    dialog := (builder newPluggableDialogWindow: 'Example groups') useDefaultOKButton.
    dialog contentMorph: ((dialog newRow: {
    	dialog newLabelGroup: {
    		'Wrong Checkbox Groupbox'-> (dialog newCheckboxFor: (ValueHolder new contents: false)
    						getSelected: #contents setSelected: #contents: label: 'First Wrong Checkbox'
    						help: 'This is a checkbox').
    		'Groupbox'->(dialog newGroupbox: 'A groupbox' forAll: {
    			dialog newButtonFor: nil action: nil label: 'First Wrong Button' help: 'This is a button'.
    			dialog newButtonFor: nil action: nil label: 'Second Wrong Button' help: 'This is a button'}).
    		'The Right Group' -> (dialog newGroupbox: 'A bunch of checkboxes' forAll: {
    			dialog newCheckboxFor: (ValueHolder new contents: false)
    						getSelected: #contents setSelected: #contents:
    						label: 'The First Checkbox'
    						help: 'This is a checkbox'.
    			dialog newCheckboxFor: (ValueHolder new contents: false)
    						getSelected: #contents setSelected: #contents:
    						label: 'The Second Checkbox is the right one'
    						help: 'This is a checkbox'.
    			dialog newCheckboxFor: (ValueHolder new contents: true)
    						getSelected: #contents setSelected: #contents:
    						label: 'The Third Checkbox is the wrong one'
    						help: 'This is a checkbox'.
    			})}})
    		vResizing: #spaceFill);
    	model: nil.
    builder openModal: dialog.

    Unfortunately this example wouldn't work based on the tutorial.

  3. Working with Sliders
    Slider Controls
    Smalltalk Code:

    | dialog builder  |
    builder := WidgetExamples new exampleBuilder.
    dialog := (builder newPluggableDialogWindow: 'Slider Controls') useDefaultOKButton.
    dialog
    	contentMorph:
    			((dialog
    					newRow:
    						{dialog
    								newLabelGroup:
    									{('Slider'
    											->
    												(dialog
    													newSliderFor: (ValueHolder new contents: 0.5)
    													getValue: #contents
    													setValue: #contents:
    													help: 'This is a slider'))}
    						}) vResizing: #spaceFill);
    		model: nil.
    	builder openModal: dialog

    Opening multiple dialogs and then running the example scripts generally updated only the dialog at the top of the screen.

    Another dialog that has two sliders:

    | dialog builder  |
    builder := WidgetExamples new exampleBuilder.
    dialog := (builder newPluggableDialogWindow: 'Slider Controls') useDefaultOKButton.
    dialog
    	contentMorph:
    			((dialog
    				newRow:
    					{dialog
    						newLabelGroup:
    							{('Slider 1'
    								->
    									(dialog
    										newSliderFor: (ValueHolder new contents: 0.6)
    											getValue: #contents
    											setValue: #contents:
    											help: 'This is a slider')).
    							('Slider 2'
    								->
    									(dialog
    										newSliderFor: (ValueHolder new contents: 0.4)
    											getValue: #contents
    											setValue: #contents:
    											help: 'This is a slider'))}
    					}) vResizing: #spaceFill);
    		model: nil.
    	builder openModal: dialog

  4. Listening to Global Hotkeys - Could not get the combination of keys to work for:

    Env.addHotkey(Key.F1, KeyModifier.ALT+KeyModifier.CTRL, openAppleMenu)

    Was successful by changing this to:

    Env.addHotkey(Key.F5, KeyModifier.CTRL, openAppleMenu)

  5. Desktop Scanner


    Suppose one wants to be more focused, however there are so many possible distractions. Having a measure of how many possible distractions one has open can help - similar to how displaying fuel efficiency changes driving behavior.

    Furthermore, one may have certain windows open when working in a non-impacting test environment, and other windows open when touching production. This monitoring can minimize the number of mistakes and improve the use of read-only accounts.

    The below code runs by searching for screenshots placed in the same directory as the script - these are png files (note that they need to be re-copied each time the script is updated). It runs as:

    Bordens-iMac% ./SikuliX-IDE.app/Contents/runIDE -r ./scan_desktop_impact.sikuli
    running SikuliX-IDE: -Xmx512M -Dapple.laf.useScreenMenuBar -Dfile.encoding=UTF-8 -Dsikuli.FromCommandLine
    -jar ./SikuliX-IDE.app/Contents/sikuli-ide.jar -r ./scan_desktop_impact.sikuli
    [info] add hotkey: ⌃ F5
    Press Control-F5 to quit
    _______________
    ****
    _______________
    **
    _______________
    *

    Every 3 seconds the code prints two lines - a line of underscores for the number of screenshots in the directory, and a star for each of the screenshots that are matched. A video of this is:


    The code is:

    from sikuli import *
    import sys
    import os
    
    # Scan a directory for images and show the number of counts of matching on the screen.
    # Exit when Control-F5 is hit.
    
    Settings.UserLogs = True
    Settings.UserLogTime = True
    
    # Added hotkey to quit easily
    # http://doc.sikuli.org/globals.html#listening-to-global-hotkeys
    def quitFromKey(event):
        Debug.user("User selected to exit")
        exit(0)
        # exit            # stop the current script, using exit(0) will close the IDE.
    
    # When the user pressed Ctrl+F5 exit this script
    
    Env.addHotkey(Key.F5, KeyModifier.CTRL, quitFromKey)
    print "Press Control-F5 to quit"
    
    # Revert this with:
    # Env.removeHotkey(Key.F5, KeyModifier.CTRL)
    
    # run for 8 hours, sleeping every 3 seconds.
    # run_time = 60 / 3 * 60 * 8
    # run for 25 minutes - 1 pomodoro
    run_time = 25 * 60 / 3
    while run_time > 0:
        count = 0
        found_images = 0
        for f in os.listdir(getBundlePath()):
            # Debug.user(f)
            if f.find(".png") != -1:
                    count = count + 1
                    if exists(f):
                        found_images = found_images + 1
                           # Debug.user("Found a prod image")
    
        if(count == 0):
            print "ERROR: Images not found - place files in ",getBundlePath()
        else:
            print " ","_" * count
            spaces = " " * (count - found_images)
            stars = "*" * found_images
            print spaces,stars
        sleep(3)
        run_time = run_time - 1
    exit(0)