Automa Blog

Showing posts tagged with: Python, Show all

Taking screenshots with Automa

We're happy to announce that we just released version 2.5.5 of our next generation GUI automation tool, Automa. Besides a few small improvements, the new version features a command called save_screenshot(...). This command lets you save a screenshot of your entire screen contents to a file. This is very useful for debugging or further processing of the outcomes of a test / automation run.

The syntax of the new command is very simple. You merely pass a string identifying the path of where the screenshot should be saved. Automa saves the file as a PNG file, so we recommend the file ending .png. For instance:

save_screenshot(r'C:\screenshot.png')

Please note the leading r before the quotation mark in the example. This is always required when working with file paths as it instructs Automa's built-in Python interpreter to treat any backslashes in the string as plain-text backslashes (not, as normally, as escape characters).

After playing around with the new command ourselves, we found it surprisingly useful and fun to use. We hope you will feel the same. You can as always download the new version from our download page.

Happy automating!

Automa now supports Python 2.7.6

Python 2.7.6

After comprehensive regression testing, we are happy to announce that we just released a new version of Automa. Besides some small bug fixes and stability improvements, this release adds support for Python 2.7.6, the latest version in the Python 2.x series. You can download the Python 2.7.6 installer here and the latest version of Automa from our download page.

Happy automating! :-)

Automa 2.4: Update to Python 2.7.5

We just released a new version of our next generation GUI testing and automation tool Automa. In addition to some stability improvements, the major feature of the new version is that it is based on Python 2.7.5. Python 2.7.5 includes many bug fixes and stability improvements over version 2.7.3, which was used for previous versions of Automa.

If you use Automa as a Python library, then you need to update your Python version to 2.7.5. This is very easy: You can just download and install the correct version using this direct link. If you download the new version of Python from another location, please ensure that you are selecting the 32 bit and not the 64 bit version of Python as explained in our documentation section. Python version 2.7.5 is fully backwards-compatible with version 2.7.3, so you should not have any trouble updating.

By updating Automa to the latest version, you can now benefit from the significantly improved stability of the latest Python 2.7 release. We therefore highly recommend the update. As always, you can download the latest version of Automa by simply visiting our download page.

Happy automating! :-)

Automa 1.9.0: A Sikuli Alternative

Many big news for us these weeks :-) We just released version 1.9.0 of Automa, our next generation GUI automation tool. For this version, we completely rewrote Automa's image search algorithms. These are used when you use the Image predicate to find (and interact with) an image shown on the screen. The new algorithms are more robust with respect to differences between the sought image and the actual image on screen. What's more, the new algorithms are compatible with the widely used Sikuli image automation tool.

Sikuli is a visual technology to automate and test graphical user interfaces using images (screenshots). It is open source and cross-platform. It supports text recognition using OCR, but unlike Automa does not support distinguishing different types of GUI elements, or to extract data from them.

The image search algorithm used in the new version of Automa is fully compatible with that of Sikuli. That is, if you used Sikuli to search for an image "my_button.png" with a minimum similarity of 0.8 (say), then the following Automa command will return exactly the same results:

Image("my_button.png", min_similarity=0.8)

As always, you can combine this command with Automa's other functions. For example:

click(Image("my_button.png", min_similarity=0.8))

We like to think that this makes Automa's GUI element search strictly more powerful than Sikuli's. With the new algorithm, Automa can find any image that Sikuli can find, plus it lets you:

So, what are you waiting for? Grab your copy of Automa from our download page!

Happy automating ;-)

Automa 1.8.0: wait_until

Automa is all about simplicity. One way where this manifests itself are its smart waiting algorithms: Unlike other GUI automation tools, which require you to explicitly perform some sort of wait(some amount of ms) after each action, Automa uses dedicated algorithms to automatically wait until an action has completed. This lets you, the user, not worry about the details of how many milliseconds a particular action is going to take, and rather focus on the essence of the task you are trying to automate.

Despite Automa's smart waiting capabilities, there are some cases where you really do explicitly want to wait for a particular condition to become true. Enter Automa's new command wait_until. This command takes a condition as parameter, say Window("Notepad").exists, and suspends execution of the Automa script currently being executed until the condition becomes true:

# Wait for the Notepad window to open:
wait_until(Window("Notepad").exists)
# Now the Notepad window exists

The nice thing about wait_until is that it can be used with any function, and in particular Python lambda functions. Want to wait until an image is no longer shown on screen? No problem:

wait_until(lambda: not Image("Progress.png").exists())

There are two optional parameters that can be passed to wait_until. The first is called timeout_secs. If the given condition does not become true in the specified number of seconds, Automa aborts the wait and raises a TimeoutExpiredException:

>>> wait_until(Image("Welcome.png").exists, timeout_secs=30)
# After 30 seconds of the image not being shown:
TimeoutExpiredException: Timeout of 30 seconds expired while waiting for
condition to become true.
>>> 

The second optional parameter of the new command is called interval_secs. It lets you specify how long interval_secs should wait between consecutive checks of the condition function. So, if you specify interval_secs=0.5, the function checks the given condition every 500ms.

API Changes

In order to be able to nicely implement wait_until, one small API change was required: In previous versions of Automa, the properties .exists, .is_enabled, .is_checked and .is_readonly could all be used without parentheses. That is, you could for instance write

if Window("Updates available").exists:
    # Close updates window...

This is no longer possible. Instead, you need to use parentheses after .exists(), .is_enabled(), .is_checked() and .is_readonly(). The above example for instance would become:

if Window("Updates available").exists():
    # Close updates window...

This is in line with the convention of many programming languages that verbs (such as "exists") represent methods, which require parentheses in order to be called.

Despite its simplicity, Automa's new wait_until command is very powerful, and covers a lot of otherwise awkward cases. We are therefore very pleased with this addition. However, if you have any other suggestions, questions or remarks, please let us know in the comments below.

Happy automating!

Using Automa as a Python library

We just released version 1.4.10 of our next generation GUI automation tool Automa. It includes improved support for being used as a Python library, so we thought this would be a good moment to explain how to incorporate Automa into Python.

To use Automa as a Python library, you need to choose the ZIP version of Automa on our download page:

Downloading Automa as a ZIP archive

You also need 32 bit Python version 2.7.3. All of these numbers are important; in particular, please ensure that you are using a 32 bit and not a 64 bit version of Python. You can download the correct installer from Python's official home page here.

Once you have extracted the Automa ZIP archive and installed Python, you need to update your PYTHONPATH environment variable to point to the library.zip file in the directory you extracted Automa.zip into. Suppose you extracted Automa.zip into c:/Tools. Then your library.zip file lies at a location similar to

c:/Tools/Automa 1.4.10/library.zip

You need to set your PYTHONPATH environment variable to this value:

Adding Automa\s library.zip to the PYTHONPATH

And that's it! To verify you have done everything correctly, start python.exe from the directory you installed Python into, and type:

from automa.api import *

Your Python window should then look similar to the following:

Successful import of Automa into Python

If you have any questions or problems, please feel free to drop us a line on contact page.

Happy automating!

Website testing using Python and Automa

After each release, we download the newest version of Automa from our website, in exactly the same way as our users would do. We then quickly launch it to make sure everything works. We do this to make sure that the newest version of our product was deployed to the server and that it is not corrupted in any way.

The steps required to download the file can be easily automated using.. Automa! And, by using Python's unittest library, we can add some assertions to make sure that the website is working correctly and that the file was indeed downloaded. Today I quickly wrote the appropriate test script which I would like to share.

In order to start, you need to download Automa ZIP archive and extract it somewhere to your disk (for example, extract contents of Automa.zip into C:\Automa). Then edit (or add) the PYTHONPATH environment variable so that it contains the path to the library.zip file (for example,  PYTHONPATH=C:\Automa\library.zip). This allows importing and using the Automa API in your Python scripts:

from automa.api import *

Then, in any text editor (for example, notepad) you can write a test script which uses Automa's API, save it with the *.py extension and run using Python interpreter. In order to test the downloading of Automa works fine, I wrote the following test case:

from automa.api import *
from os.path import expanduser, exists, join
import unittest
 
class TestAutomaWebsite(unittest.TestCase):
    def setUp(self):
        start("firefox")
 
    def test_download(self):
        # get downloads directory
        zip_path = join(expanduser("~"), "Downloads", "Automa.zip")
        self.assertFalse(exists(zip_path ))
 
        click(Image("location.png"))
        write("http://www.getautoma.com/")
        press(ENTER)
 
        # assert that the website is up and running and has been loaded
        self.assertTrue(Image("automa_logo.png").exists)
        click("Download")
        click(Image("name_field.png"))
        write("Tytus")
        press(TAB)
        write("BugFree Software")
        press(TAB)
        write("tytus@getautoma.com")
 
        # if zip is not selected, then select it
        zip_radio_button = Image("zip_radio_button.png")
        if (zip_radio_button.exists):
            click(zip_radio_button)
        click(Image("download_button.png"))
 
        # if Firefox opens window asking what to do with the file..
        if (Image("download_window.png").exists):
            click("Save File")
            click("OK")
 
        # close the "Downloads" window
        press(ALT + F4)
 
        # make sure that the correct page is displayed
        self.assertTrue(Image("registration_complete.png"))
 
        # check that Automa.zip has been downloaded
        self.assertTrue(exists(zip_path))
 
    def tearDown(self):
        # close Firefox
        press(ALT + F4)
 
if __name__ == "__main__":
    unittest.main()

As you can see, I used multiple *.png images in the test script. With Automa, you always have the choice of using either the image-based or text-based automation. Normally, the text-based automation is preferred as it is more robust and test scripts do not break if GUI elements change visually. However, in some cases, for example when it is difficult to refer to UI elements by text (for example, when they don't have any label assigned to them), you can always fall back to *.png images. In the above script I used the following images:

location.png:location.png
automa_logo.png:automa_logo.png
name_field.png:name_field.png
zip_radio_button.png:zip_radio_button.png
download_button.png:download_button.png
download_window.png:download_window.png
registration_complete.png:registration_complete.png

In order to run the test script, I saved it together with all the images in one directory and run the python interpreter from the command prompt:

Test Run Success

And this is it! After running this automated test script I am sure that our website is up and running and that Automa.zip can be downloaded without any issues. You can try it yourself by downloading the archive below which contains the test_automa_download.py script together with all the images.

TestAutomaDownload.zip

Happy testing and automating! As always, we'd be very happy to hear what you think about our tool in the comments below :-)

New version of Automa with improved API

There are only two hard things in Computer Science: cache invalidation and naming things.

-- Phil Karlton

In previous versions of Automa, we had two functions for generating keystrokes: type and enter. They were synonyms so you could write

type("Hello World!")
type("{ENTER}")
type("http://www.getautoma.com{ENTER}", into="Location")

just as well as

enter("Hello World!")
enter("{ENTER}")
enter("http://www.getautoma.com{ENTER}", into="Location")

The problem was that the two functions being synonyms was confusing to our users and that our 'type' overwrote a built-in function in Python, which irritated some more experienced programmers.

After asking for a good name for our functions on StackOverflow, Programmers dot SE, Google Groups and Python Forums in English and German, we received a huge number of suggestions (only the 10 most popular ones are shown):

send_keys
type_keys
generate_keystrokes
enter
type
keypress
keyboard_input
send
strike
press
...

StackOverflow: useful but repellent

The responses at StackOverflow and (the related) Programmers dot SE were pretty repellent at first. Instead of answering our question, the other users at StackOverflow went to great lengths to tell us that poll-like questions are not permitted and closed our question immediately. Then they went on to actually answer our question, however not in the form of "StackOverflow answers" but in the form of comments to our question. We found this situation to be somewhat strange - first we're told the question isn't allowed but then in the next sentence we do get an answer. As we eventually managed to find out, the reason why poll-like questions such as ours aren't allowed are that their answers are likely not relevant to other users of SO. That's a reson we can understand, however at the end of the day we believe it would have been easier for everyone if our question had just been answered in the first place.

Automa's new API

Particularly through the help from the users at the Google Group 'comp.lang.python', we arrived at the following new API for generating keystrokes with Automa:

Plain text input can be entered using write:

write("Hello World!")

If you wish to write the plain text into a particular text field, supply the optional parameter into=:

write("Hello World!", into="File name")

Unlike for enter or type, it is no longer possible to mix plain text with special keys as in enter("Hello World{TAB}"). Pressing particular keys is what press is for:

press(ENTER)
press(ALT + 'f', 's')

This effectively splits what we previously called enter into two functions: One for writing plain text and another for pressing special (combinations of) keys. The advantage this has is that it allows us to get rid of the curly braces that had to be used for special keys as in press("{ENTER}"). Furthermore, it improves IDE support as the IDE can now auto-complete the (constant) key names ENTER, TAB, ALT etc. for you. The name write, while not that commonly used for generating keystrokes, avoids the name clash of type with a built-in function while at the same time playing along well with the Unix mantras that Everything is a file. Finally, both press and write are nice and short and could be used in an everyday conversation - one of the goals of Automa's API.

The difference between an API and a Domain Specific Language

In designing Automa's API, it was tempting to disregard the conventions of its host language Python. The most important example of this was with type, which is normally a very fundamental built-in function in Python. We eventually decided to obey the conventions of our host language, in particular to avoid confusing more experienced programmers. Nevertheless, some of the choices we made do not feel like designing an API in a technical programming language: In choosing the names write and press, we created a vocabulary that is very non-technical and much closer to the language you would use when talking about our problem domain - GUI automation - in an everyday conversation. We believe it is this property that places Automa's Python interface more in the realm of (internal) Domain Specific Languages than simply APIs.

As always, do let us know your thoughts in the comments!

New version of Automa with improved Unicode support

In the past week, we've been working on switching Automa to Unicode. This allows it to seamlessly work with characters of languages other than English, such as German umlauts ö, ä, ü or French accents ê, é, à etc.... Our goal was to make Unicode support as seamless as possible: using special characters should 'just work'.

The need for Unicode can briefly be explained as follows: When computers were first developed in the Western hemisphere, they could only work with texts consisting of characters of the English alphabet. The computer-internal representation of these texts was given by the ASCII encoding standard. As computers spread throughout the world, the need for working with texts written in languages and characters other than English arose. This eventually lead to the development of the Unicode standard, which describes how text written in any language is to be treated and represented in a computer. Unicode is the standard for representing arbitrary text in a computer, however for legacy reasons its adoption is still somewhat slow. For instance, if you are using a Western version of Windows 7 and type something into the Windows shell "cmd.exe", it is most likely that Windows will represent the text you enter using a code page that can only encode characters from a Western alphabet, but not for example Cyrillic or Asian characters.

Since Automa is based on Python 2.7.3, its support for international languages hinges on that of this environment. In Python 2.7.3, the default string encoding is ASCII. However, when you enter some text into the Python console on Windows, the current Windows code page is used to represent the string. Consider the following example:

Python 2.7.3's standard encoding behaviour

When we have Python evaluate the string 'ö', it tells us that the result is '\x94'. When we have it print this string, it correctly displays ö. When we look at the encoding that is used for text typed into the console (sys.stdin.encoding) we get the result 'cp850', which is Windows' code page 850. Likewise for the output encoding sys.stdout.encoding that is used for text displayed in the console.

The fact that the string 'ö' evaluates to '\x94' means that this string is represented internally as the hexadecimal number 94. If you look at the layout of code page 850, you will see that this is because the hexadecimal number 94 represents the character ö in this encoding.

You may wonder why Python is able to display the character ö when we ask to have it printed via the command print 'ö'. If you recall from above, the default encoding for strings in Python 2.7.3 is ASCII, which cannot encode ö. However, the encoding that is actually used, code page 850, is an extension of the ASCII encoding: ASCII stores each character in 7 bits. Since modern computers work with multiples of 8 bits (1 byte), this leaves one bit unused. Code page 850 exploits this unused bit to make it possible to encode more characters, such as for instance ö.

When you download the latest version of Automa and execute the above commands, you will get the following, different output:

>>> 'ö'
'ö'
>>> print 'ö'
ö
>>> import sys
>>> sys.stdin.encoding
'cp850'
>>> sys.stdout.encoding
'utf-8'

As you would expect from a seamless integration of special characters, the string 'ö' is simply displayed as 'ö' and printing it results in ö being displayed. The input encoding is still 'cp850' (as this encoding is effectively given by your Windows keyboard layout), however the output encoding is now 'utf-8'. This is an encoding specified by the Unicode standard and can be used to represent characters of any language.

An interesting side-effect of the new implementation is a slight change in the output of Automa's history() function:

>>> history()
u'ö'
print u'ö'
import sys
sys.stdin.encoding
sys.stdout.encoding

You can see that even though we entered the commands 'ö' and print 'ö', the history function displays these as u'ö' and print u'ö', respectively. The reason for this is the following: when you type a string '...' into the console, the input encoding (cp850 above) determines which byte values are used to store the string in memory. When the string subsequently has to be displayed, the output encoding is used to convert the byte values back into a displayable sequence of characters. Now however, the output encoding (utf-8) differs from the input encoding (cp850), and in this encoding the meaning of the byte values (are most likely to) differ, leading to not the original string but rather a sequence of strange symbols being displayed. The solution to this problem consists of storing strings internally in an encoding-independent way, done in Python using Unicode strings u'...'. Rewriting history by changing normal strings '...' into Unicode strings u'...' ensures the interoperability of Automa scripts and normal Python scripts, as given by Automa's Python Integration.

Summary

The new implementation of Automa's unicode support makes it possible to seamlessly work with international languages. For example, pointing the mouse cursor at a menu entry "Datei"/"Öffnen" (German for "File"/"Open") and calling get_name_under_mouse() now yields 'Öffnen' instead of u'\xd6ffnen'. Likewise, it is now possible in Automa's console to call

write('ö')

instead of

write(u'ö')

While these may seem like small differences, we believe it is such little hurdles that make or break a fluent user experience. In line with our goal of (meeting and) exceeding user expectations (see our previous post), we couldn't help but to improve Automa's behaviour in this respect. After all, when you see a special character on your screen, you would also expect a GUI automation tool such as Automa to display this character in the same way as you see it on the screen, and you expect to be able to refer to the text containing the character by simply typing it, without any escape codes or other special treatment.

References

In addition to the standard Python documentation, the documentation of the codecs package and Wikipedia's article on code page 850, the following resources were very useful for adding Unicode support to our Python 2.x application: