16.1. Scripting

16.1.1. Accessing Qt API
16.1.2. Accessing Tk API
16.1.3. Accessing XView API
16.1.4. Accessing Web API
16.1.5. Accessing Java™ API
16.1.6. Test Statements
16.1.7. Event Handlers
16.1.8. Synchronization Points
16.1.9. Testing Multiple AUTs from a Single Test Script, Working with ApplicationContext
16.1.10. Testing Qt Widgets
16.1.11. Testing Tk Widgets
16.1.12. Testing XView Widgets
16.1.13. Testing Web Elements
16.1.14. Testing Java™ applications
16.1.15. Semi-Automatic Tests: Querying User Input

This chapter discusses Squish's scripting support, the different supported scripting languages and the script APIs which are available when working with test scripts.

16.1.1. Accessing Qt API

One of Squish's most useful features is the ability to access the complete Qt API (and optional application API) from test scripts. This gives test engineers an amount of flexibility allowing to test about anything in the AUT.

With Squish's Qt API it is possible to find and query objects, call methods, and access properties and enums.

In addition, Squish provides a Qt Convenience API (Section 16.1.1.5) to execute common GUI actions such as clicking a button or selecting a menu item

The chapter Testing Qt Widgets (Section 16.1.10) later in this manual shows using different examples how to use the scripting Qt API to access and test complex Qt widgets

16.1.1.1. Finding and Querying Qt Objects

Squish provides a function called findObject which returns the object for a given symbolic or real object name. Depending on the used naming scheme, a symbolic name is a name looking like Addressbook.Add/Change Entry.Add_QPushButton and a real name may look like {type='QPushButton' text='Add' windowCaption='Addressbook' windowType='ABMainWindow' containerLabel='Add/Change Entry' containerType='QTabWidget'} (multi-property naming) or Addressbook.ABCentralWidget1.AddButton (qualified naming).

In the multi-property naming scheme, an object is identified by property tuples matching the property values of the object to identify. The dot notation of qualified names is used to separate object hierarchy.

To find out the name of an object, you can use the Spy to introspect the application. See the chapter Spy (Section 16.8.3) for details.

Querying on object using findObject in Python looks like this:

button = findObject("Addressbook.ABCentralWidget1.AddButton")

The same in JavaScript would look like this:

var button = findObject("Addressbook.ABCentralWidget1.AddButton");

In Tcl you would use the following code:

set button [findObject "Addressbook.ABCentralWidget1.AddButton"]

If findObject can't find the specified object, a script error is thrown which stops the script execution. In some situations it might be desirable to first check if the object exists and execute an action on the object. For this purpose, Squish provides an object.exists function.

As example, we want to find the AddButton like above, and click it if it exists. Th Python this looks like this:

if object.exists("Addressbook.ABCentralWidget1.AddButton"):
    button = findObject("Addressbook.ABCentralWidget1.AddButton")
    clickButton(button)

The equivalent in JavaScript is:

if (object.exists("Addressbook.ABCentralWidget1.AddButton")) {
    var button = findObject("Addressbook.ABCentralWidget1.AddButton");
    clickButton(button);
}

In Tcl the same functionality would be implemented like this:

if {[object exists "Addressbook.ABCentralWidget1.AddButton"]} {
    set button [findObject "Addressbook.ABCentralWidget1.AddButton"]
    invoke clickButton $button
}

This allows test engineers to query and access the complete object tree of the AUT. Additionally Qt's QObject::child() and QObject::queryList() functions can be used on objects. But more about calling Qt functions on object in the next section.

16.1.1.2. Calling Functions on Qt Objects

With Squish it is possible to call every public function on any Qt object. In addition it is possible to call static functions provided by Qt.

In the example below we change the button text of the button we queried in the previous section using QButton::setText(). The Python code for this looks like this:

button = findObject("Addressbook.ABCentralWidget1.AddButton")
button.setText("Changed Button Text")

The same in JavaScript looks like this:

var button = findObject("Addressbook.ABCentralWidget1.AddButton");
button.setText("Changed Button Text");

And in Tcl the following code can be used:

set button [findObject "Addressbook.ABCentralWidget1.AddButton"]
invoke $button setText "Changed Button Text"

Similarly, static Qt functions can be called. As an example, we will query the currently active modal widget (i.e. a dialog box) using the static QApplication::activeModalWidget() function. If this returns a valid object, we will call QWidget::close() on it to close it. To check if the object is valid (not null), we can use Squish's isNull function.

Let's first look at the code in Python:

widget = QApplication.activeModalWidget()
if not isNull(widget):
    widget.close()

The same in JavaScript looks like this:

var widget = QApplication.activeModalWidget();
if (isNull(widget) == false) {
    widget.close();
}

Finally the same can be implemented in Tcl like this:

set widget [invoke QApplication activeModalWidget]
if {[isNull $widget] == false} {
    invoke $widget close
}

16.1.1.3. Accessing Properties of Qt Objects

In addition to calling functions it is also possible to access all of Qt's properties. This is very similar to calling functions but allows to write better readable code especially in Python and JavaScript.

In the example we will change and query the text property of a QLineEdit (QLineEdit::text) and print the value out. In Python we use the following code:

lineedit = findObject("Addressbook.ABCentralWidget1.FirstName")
lineedit.text = "A new text"
text = lineedit.text
print(unicode(text))

In JavaScript the following code will do the same:

var lineedit = findObject("Addressbook.ABCentralWidget1.FirstName");
lineedit.text = "A new text";
var text = lineedit.text;
print(String(text))

In Tcl we use the following code:

set lineedit [findObject "Addressbook.ABCentralWidget1.FirstName"]
property set $lineedit text "A new text"
set text [property get $lineedit.text]
puts [toString $text]

16.1.1.4. Accessing Qt Enums

In C++ it is possible to declare enumerators to give numbers a name. So, instead of label.setAlignment(1) you can write label.setAlignment(Qt::AlignLeft).

Qt defines quite a few of such enumerators and many functions take enumerator arguments. It is also possible to use these enums in test scripts. To stay with the above example, we will set the alignment of a label to AlignLeft in Python:

label = findObject("Addressbook.ABCentralWidget1.labelFirstName")
label.setAlignment(Qt.AlignLeft)

In JavaScript the code looks quite similar:

var label = findObject("Addressbook.ABCentralWidget1.labelFirstName");
label.setAlignment(Qt.AlignLeft);

The Tcl code is a bit more different

set label [findObject Addressbook.ABCentralWidget1.labelFirstName]
invoke $label setAlignment [enum Qt AlignLeft]

16.1.1.5. Qt Convenience API

This section describes the script API Squish offers on top of Qt to make it easy to perform common user actions such as clicking a button. A complete list of this API is available in the chapter Qt Convenience API (Section 17.1.3) in the Squish Reference Guide (Chapter 17). Here we will show some examples.

In the example below, we click a button, double click a list view item and activate a menu item. As mentioned, there are more convenience functions, but the examples below should give a good overview of how it works.

Let's first look at Python again:

clickButton("Addressbook.AddButton")
doubleClickItem("Addressbook.addressList", "Max|Mustermann|*", 0, 0, 0, Qt.LeftButton)
activateItem("Addressbook.menubar", "Quit")

In JavaScript the code looks very similar:

clickButton("Addressbook.AddButton");
doubleClickItem("Addressbook.addressList", "Max|Mustermann|*", 0, 0, 0, Qt.LeftButton);
activateItem("Addressbook.menubar", "Quit");

In Tcl it looks slightly more different:

invoke clickButton "Addressbook.AddButton"
invoke doubleClickItem "Addressbook.addressList" "Max|Mustermann|*" 0 0 0 [enum Qt LeftButton]
invoke activateItem "Addressbook.menubar" "Quit"

16.1.2. Accessing Tk API

One of Squish's most useful features is the ability to access the toolkit's API from test scripts. This gives test engineers an amount of flexibility allowing to test about anything in the AUT.

With Squish's Tk-specific API it is possible to find and query objects, access properties and evaluate arbitrary Tcl code in the AUT's interpreter.

In addition, Squish provides a Tk Convenience API (Section 16.1.2.4) to execute common GUI actions such as clicking a button or selecting a menu item

The chapter Testing Tk Widgets (Section 16.1.11) later in this manual shows using different examples how to use the scripting Tk API to access and test complex Tk widgets

16.1.2.1. Finding and Querying Tk Objects

Squish provides a function called findObject which returns the object for a given qualified object name. A qualified object name is a name like myapp.frame1.okbutton. The dot notation is used to separate objects. In the example above okbutton is a child of frame, which in return is a child of myapp (the mainwindow).

To find out the name of an object, you can use the Spy to introspect the application. See the chapter Spy (Section 16.8.3) for details.

Querying on object using findObject in Tcl you would use the following code:

set button [findObject "myapp.frame1.okbutton"]

If findObject can't find the specified object, a script error is thrown which stops the script execution. In some situations it might be desirable to first check if the object exists and execute an action on the object. For this purpose, Squish provides an object.exists function.

As example, we want to find the okbutton like above, and click it if it exists. In Tcl this functionality would be implemented like this:

if {[object exists "myapp.frame1.okbutton"]} {
    set button [findObject "myapp.frame1.okbutton"]
    invoke clickButton $button
}

This allows test engineers to query and access the complete object tree of the AUT.

16.1.2.2. Accessing Properties of Tk Objects

Using script API it is possible to access most all of Tk's widget properties.

In the example we will change and query the text property of a BWidget Entry. The following Tcl code can be used for that:

set entry [findObject "myapp.frame1.e1"]
property set $entry text "A new text"
set text [property get $entry text]
puts [toString $text]

16.1.2.3. Using tcleval

Tk's widget don't privide all information and states one would like to query from test scripts through the property system. To be ably to do any arbitrary Tcl calls, which are interpreted in the AUT's scope, Squish provides the tcleval function.

To get, for example, the contents of a Tk multi-line text box, you can't just query a property. Instead get with several arguments needs to be called on the object. For occasions like this, tcleval can be used:

set text [invoke tcleval ".textfield get 1.0 end"]

16.1.2.4. Tk Convenience API

This section describes the script API Squish offers on top of Tk to make it easy to perform common user actions such as clicking a button. A complete list of this API is available in the chapter Tk Convenience API (Section 17.1.4) in the Squish Reference Guide (Chapter 17). Here we will show some examples.

In the example below, we click a button, double click a list item and activate a menu item. As mentioned, there are more convenience functions, but the examples below should give a good overview of how it works.

Here is the Tcl code for those examples:

invoke clickButton "myapp.button1"
invoke doubleClickItem "myapp.list1" "Banana" 0 0 0 1
invoke activateItem "myapp.filemenu" "Quit"

16.1.3. Accessing XView API

16.1.4. Accessing Web API

One of Squish's most useful features is the ability to access the toolkit's API from test scripts. This gives test engineers an amount of flexibility allowing to test about anything in the AUT.

With Squish's Web-specific API it is possible to find and query objects, access properties and methods and evaluate arbitrary JavaScript code in the Web-applications context.

In addition, Squish provides a Web Convenience API (Section 16.1.4.6) to execute common actions on Web sites such as clicking a button or entering some text.

The chapter Testing Web Elements (Section 16.1.13) later in this manual shows using different examples how to use the scripting Web API to access and test complex Web elemenst.

16.1.4.1. Finding and Querying Web Objects

Squish provides a function called findObject which returns the object (HTML or DOM element) for a given qualified object name.

The available API which can be called on Web objects can be found here.

There are several ways to indentify a Web object:

  • Multiple properties: A list of property/value pairs in curly braces defines the object. Squish will search the DOM tree of the document until it finds a matching object. Example: {tagName='INPUT' id='r1' name='rg' form='myform' type='radio' value='Radio 1'}

  • Single property: The value of one property is given. Squish will search the DOM tree of the document until it finds an object whose id, name or innerText matches the value.

  • Path: The path to the element is given. Example: DOCUMENT.HTML1.BODY1.FORM1.SELECT1

To find out the name of an object, you can use the Spy to introspect the document of the Web application. See the chapter Spy (Section 16.8.3) for details.

Querying on object using findObject in Python you would use the following code:

radio = findObject(":{tagName='INPUT' id='r1' name='rg' form='myform' type='radio' value='Radio 1'}")

If findObject can't find the specified object, a script error is thrown which stops the script execution. In some situations it might be desirable to first check if the object exists and execute an action on the object. For this purpose, Squish provides an object.exists function.

As example, we want to find the radio button like above, and click it if it exists. In Python this functionality would be implemented like this:

if object.exists(":{tagName='INPUT' id='r1' name='rg' form='myform' type='radio' value='Radio 1'});
    radio = findObject(":{tagName='INPUT' id='r1' name='rg' form='myform' type='radio' value='Radio 1'}")
    clickButton(radio)

This allows test engineers to query and access the complete object tree of the Web document.

16.1.4.2. Using XPath

On each object retrieved using findObject it is possible the evaluate an XPath statement. The object on which the XPath statement is evaluated is used as the context node.

For example, to retrieve the reference to a link referring to the URL www.froglogic.com which is a child of the DIV element with the id 'mydiv', we can use the following JavaScript code:

var div = findObject("{tagName='DIV' id='mydiv'}");
var link = div.evaluateXPath("A[contains(@href, 'www.froglogic.com')]").snapshotItem(0);

Each XPath query can have a boolean (truth) value, a number, a string or a group of elements as the result. Consequently, the evaluateXPath function returns an HTML_XPathResult on which you can query the result of the XPath evaluation.

[Tip]Tip

For more information about how you can use XPath to create flexible and compact test scripts, refer to documents specialized on this topic. We can recommend the XPath Tutorial from the W3Schools Online Web Tutorials.

16.1.4.3. Accessing Properties of Web Objects

Using script API it is possible to access most of the DOM properties on any HTML/DOM element of the Web application.

The available API which can be called on Web objects can be found here.

In the example we will change and query the text property of a form's text element. The following Python code can be used for that:

entry = findObject("{tagName='INPUT' id='input' form='myform' type='text'}")
entry.text = "A new text"
print(text)

Squish provides such script bindings to all standard DOM properties of standard DOM elements. But it is also possible to access the properties of custom properties via a generic property function. E.g. to check if a DIV element is hidden, you can do

div = findObject("DOCUMENT.HTML1.BODY1......DIV")
test.compare(div.property("style.display"), "none")

16.1.4.4. Accessing Web Object Functions

In addition to properties, you can call standard DOM functions on all Web objects from test scripts.

The available API which can be called on Web objects can be found here.

For example to get the first child node of a DIV element, you could use the following Python test script code by calling the firstChild function:

div = findObject("DOCUMENT.HTML1.BODY1......DIV")
child = div.firstChild()
print(child.tagName)

Or to get the option object of a given index of a select form element, one could use the following JavaScript code:

sel = findObject(":{tagName='INPUT' id='sel' form='myform' type='select-one'}");
opt = sel.optionAt(3)
print(option.text)

Squish provides such script bindings to all standard DOM functions of standard DOM elements. But it is also possible to call custom functions via a generic invoke function. E.g. to call myOwnFunc on a DIV element:

div = findObject("DOCUMENT.HTML1.BODY1......DIV")
div.invoke("myOwnFunc", "my argument")

In addition to the DOM API bindings, Squish offers a Browser object which can be used to query in a test script which browser is being used:

print("We are running in " + Browser.name()) # will print out the name of the browser
if Browser.id() == InternetExplorer:
    ....
elif Browser.id() == Mozilla:
    ....
elif Browser.id() == Firefox:
    ....
elif Browser.id() == Safari:
    ....
elif Browser.id() == Konqueror:
    ....

16.1.4.5. Using evalJS

Besides accessing all properties and methods of DOM elements from the test scripts, it is also possible to let Squish execute arbitrary JavaScript code in the Web sites's JavaScript interpreter and get the result of that. For this purpose, Squish provides the evalJS function.

It can be used line this:

style_display = evalJS("var d = document.getElementById('busyDIV'); d ? d.style.display : ''"")

evalJS returns the result of the last statement from the code (in this case the last statement is d ? d.style.display : ''""),

16.1.4.6. Web Convenience API

This section describes the script API Squish offers on top of DOM's API to make it easy to perform common user actions such as clicking a link, entering text, etc. A complete list of this API is available in the chapter Web Convenience API (Section 17.1.6) in the Squish Reference Guide (Chapter 17). Here we will show some examples.

In the example below, we click a link, select an option and enter some text. As mentioned, there are more convenience functions, but the examples below should give a good overview of how it works.

Here is the Python code for those examples:

clickLink(":{tagName='A' innerText='Advanced Search'}")
selectOption(":{tagName='INPUT' id='sel' form='myform' type='select-one'}", "Banana");
setText(":{tagName='INPUT' id='input' form='myform' type='text'}", "Some Text")

16.1.4.7. Synchronization

A special function called isPageLoaded() is offered for Web testing allowing to synchronize a test script with the page loading status of the Web application.

To wait for a page to be loaded before clicking the Login button of the Web page, the following code could be used:

var ok = waitFor("isPageLoaded()", 5000);
if (ok)
    clickButton(":{tagName='INPUT' type='button' value=Login'}");
else
    test.fatal("Page loading failed")

Additionally, you can wait for any object to be ready using the waitForObject function:

waitForObject(":{tagName='INPUT' type='button' value=Login'}");
clickButton(":{tagName='INPUT' type='button' value=Login'}");

In advanced AJAX applications waiting for a page to be loaded often is not sufficient since parts of the page will be loaded using asynchroneous AJAX requests. So more sophisticated synchronization methods are needed.

In many cases waiting for a certain object to be available will be good enough. So the waitForObject will help.

But many AJAX toolkits refresh objects using AJAX requests in the background. So it might be necessary to wait until the background loading has finished in addition.

Most toolkits display a visual hint, such as a box which says "loading...", to indicate to the user that the application is loading. We can use this visual hint to synchronize our script by waiting until the loading hint disappeared.

As an example we will here implement such an AJAX synchronization for test scripts testing applications based on the Backbase AJAX toolkit (which Squish, among many other toolkits, provides dedicated support for).

So we need to add the following function which checks if the "loading..." box is displayed:

function isBackbaseLoading()
{
    if (!object.exists("{tagName='DIV' id='loading'}"))
        return false;
    
    var div = findObject("{tagName='DIV' id='loading'}");
    if (isNull(div) || isNull(div.parentElement()))
        return false;
    
    div = div.parentElement();
    if (div.property("style.display") == "none")
        return false;
    
    return true;
}

What this basically does it to check if a DIV with the ID loading exists and is displayed.

Now we always want to synchronize with the loading state after certain operations (certain clicks, etc. which trigger a background loading) are performed by the test script. But the loading might not start immediately but with a small delay after the actual operation. So we need a function which first waits for the "loading..." hint to appear. And if this is the case after a small delay, it will wait until it disappears again:

function synchBackbase()
{
    var ok = waitFor("isBackbaseLoading()", 2000);
    if (!ok)
        return;
    waitFor("! isBackbaseLoading()");
}

Finally, we don't want to insert calls to synchBackbase() everywhere in the script code (of course in some cases it will be useful to call it manually). We can implement a special function called waitUntilObjectReady(). If a test script implements such a function, Squish will call it automatically from every waitForObject call. So we implement it:

function waitUntilObjectReady(o)
{
    synchBackbase();
}

Now in every waitForObject call, Squish will call our implementation of waitUntilObjectReady which makes sure that the AJAX application is not loading anymore. This allows us to synchronize the test with the asynchroneous AJAX request of the application.

We can now put all that code into a shared script and just include that in all our test cases. This way this advanced synchronization can be used in all tests. See more on that in Shared Data and Scripts (Section 16.11).

The same can of course be implemented for every AJAX toolkit. The example above can be used to understand the concepts. You can also find an example utilizing thise functionality in examples/web/suite_examples/tst_backbase_pim.

More general information on synchronizations: Synchronization Points (Section 16.1.8)

16.1.5. Accessing Java™ API

One of Squish's most useful features is the ability to access the toolkit's API from test scripts. This gives test engineers an amount of flexibility allowing to test about anything in the AUT.

With Squish's Java™-specific API it is possible to find and query objects, access fields and methods. When we talk about properties, we mean fields in Java™ classes combined with member functions that have a

        Type getSomething();
        boolean isSomething();
        void setSomething( Type t );

pattern. In the above code the property would have been called something. When both getXxx and isXxx are there, then Squish will use the getXxx variant. The existence of getFoo/isFoo and/or setFoo sets whether the property is read-only or read-write, write-only properties are not generated by Squish.

In addition, Squish provides a Java™ Convenience API (Section 16.1.5.4) to execute common actions on GUI applications such as clicking a button or entering some text.

The chapter Testing Java™ applications (Section 16.1.14) later in this manual shows using different examples how to use the scripting Java™ API to access and test complex JavaGUI elements.

16.1.5.1. Finding and Querying Java™ Objects

Squish provides a function called findObject which returns the object for a given qualified object name.

There are two notations for an object qualified name:

  • Multiple properties: A list of property/value pairs in curly braces defines the object. Squish will search the GUI parent child hierarchy until it finds a matching object. Example: {type='javax.swing.JButton' caption='*' windowType='Calculator' windowName='frame0' windowCaption='Java Swing Calculator'}

  • Hierarchical notation: From the top Frame (or Shell in SWT) the path to the object, all parent GUI elements, are noted and separated by a dot. Example: :frame0.JRootPane.null_layeredPane.null_contentPane.JLabel.

To find out the name of an object, you can use the Spy to introspect the document of the Web application. See the chapter Spy (Section 16.8.3) for details.

Squish prefers the multiple property notation where ever possible. This notation has the advantage that scripts wont break if you need another layer (like a JPanel) in between somewhere.

The multiple property notation may be followed by a hierarchical notation.

As described in Object Map (Section 16.5), the multiple property notation names automatically get a shorter name in the “Object map”. In the above example that shorter name could be :frame0.*_javax.swing.JButton. You may use either the short or original name to reference the object.

Querying on object using findObject in Python you would use the following code:

multiply = findObject(":frame0.*_javax.swing.JButton")

If findObject can't find the specified object, a script error is thrown which stops the script execution. In some situations it might be desirable to first check if the object exists and execute an action on the object. For this purpose, Squish provides an object.exists function.

As example, we want to find the radio button like above, and click it if it exists. In Python this functionality would be implemented like this:

if object.exists(":frame0.*_javax.swing.JButton"):
    multiply = findObject(":frame0.*_javax.swing.JButton")
    clickButton(multiply)

This allows test engineers to query and access the complete object tree of the Web document.

16.1.5.2. Calling Functions on Java Objects

With Squish it is possible to call every public function on any Java object. Finding and Querying Java™ Objects (Section 16.1.5.1) describes how objects can be found. The following example shows how you can create an object in JavaScript:

var s = new java_lang_String("abc");
[Note]Note

The notation of Java™ objects are with a “_” instead of “.” for package directories separation. This is because a “.” has a meaning in most of the script languages that Squish support.

In the example below we change the button text of the multiply button of the calculator demo application. In JavaScript this can be written as:

var multiply = findObject(":frame0.*_javax.swing.JButton");
multiply.setText( "x" );

Static functions can also be called. The next example shows a JavaScript example of calling the static Integer.parseInt(String s):

var i = java_lang_Integer.parseInt("12");

16.1.5.3. Accessing Properties of Java™ Objects

Java™ objects can have fields. Public fields are accessible in Squish like in the next JavaScript example:

var p = new java_awt_Point( 5, 8 );
print( p.x );

In addition to public fields, Squish adds synthetic properties derived from method names with the Type getSomething(), boolean isSomething() and void setSomething(Type t) pattern (see Accessing Java™ API (Section 16.1.5) for details). In the example where we changed the button text with setText("x"), we could have written in JavaScript:

var multiply = findObject(":frame0.*_javax.swing.JButton");
multiply.text = "x";
And Squish will call the corresponding setText("x") for us.
[Note]Note

These extra generated properties allows you to add more verifications points in the test scripts, see Verification Points (Section 16.10) for more information.

16.1.5.4. Java™ Convenience API

This section describes the script API Squish offers on top of Java™'s API to make it easy to perform common user actions such as clicking a button, entering text, etc. A complete list of this API is available in the chapter Java™ Convenience API (Section 17.1.7) in the Squish Reference Guide (Chapter 17). Here we will show some examples.

In the example below, we click on a button, type some text and activate a menu. As mentioned, there are more convenience functions, but the examples below should give a good overview of how it works.

Here is the Python code for those examples:

clickButton(":frame0_Notepad$1")
type(":frame0_javax.swing.JTextArea", "Some text")
activateItem(":frame0_javax.swing.JMenuBar", "File")
activateItem(":frame0.File_javax.swing.JMenu", "Exit")

In Perl or JavaScript this looks like this:

clickButton(":frame0_Notepad$1");
type(":frame0_javax.swing.JTextArea", "Some text");
activateItem(":frame0_javax.swing.JMenuBar", "File");
activateItem(":frame0.File_javax.swing.JMenu", "Exit");

16.1.6. Test Statements

This section discusses the API Squish offers to perform tests which will create test results. Verification points use this test API. Different approaches and topics on verification points are discussed in the chapter Verification Points (Section 16.10). Working with the test result log is discussed in the chapter Processing Test Results (Section 16.7.3).

To compare two values and write the result to the test log, the test.compare is used. To just verify a boolean value, test.verify is used. To write some information to the test log, the test.log function can be used. If you want to emphasize a message, you might want to mark it as a warning message, which can be done with the test.warning function.

Here is a small example for Python:

lineedit = findObject("Addressbook.ABCentralWidget1.FirstName")
test.verify(lineedit.enabled)
test.compare(lineedit.text, "Max")
test.log("Important note", "This is an important note about the test")
test.warning("Suspicious warning", "This test is incomplete and should be extended!")

In JavaScript we of course can do the same:

lineedit = findObject("Addressbook.ABCentralWidget1.FirstName");
test.verify(lineedit.enabled);
test.compare(lineedit.text, "Max");
test.log("Important note", "This is an important note about the test");
test.warning("Suspicious warning", "This test is incomplete and should be extended!");

In Tcl it works too:

set lineedit [findObject "Addressbook.ABCentralWidget1.FirstName"]
test verify [property get $lineedit enabled]
test compare [property get $lineedit text] "Max"
test log "Important note" "This is an important note about the test"
test warning "Suspicious warning" "This test is incomplete and should be extended!"

There are of course more test commands available. A complete listing and API documentation can be found in the chapter Squish API (Section 17.1.2) in the Squish Reference Guide (Chapter 17).

16.1.7. Event Handlers

In Squish test scripts it is also possible to react on events in your AUT. For example, this is useful to react on unexpected dialogs popping up such as error message boxes.

The concept is to specify an event handler function for a certain event on a certain object, all objects or object type. The function to do this is called installEventHandler.

In this chapter we will give the code examples only in one script language. As we will discuss three different types of event handlers, there will be a code example in each script language.

A complete listing and API documentation of event handlers can be found in the chapter Squish API (Section 17.1.2) in the Squish Reference Guide (Chapter 17).

16.1.7.1. Global Events

A global event is for example the event MessageBoxOpened which is sent when a message box opens. This can be used to e.g. react on unexpected message boxes which pop up. The event handler below will log the message box's text in the test results and then close the message box so the test can continue. In Python this would look like this:

def handleMessageBox(msgBox):
    label = msgBox.child("messageBoxText")
    label = cast(label, QLabel)
    text = label.text
    caption = msgBox.caption
    test.log("Message box '" + str(caption) + "' opened", str(text))
    msgBox.close()

def main():
    installEventHandler("MessageBoxOpened", "handleMessageBox")

Another special event is Crash. This way it is possible to install an event handler which is called when the AUT crashes to do e.g. cleanups or restart the AUT. In JavaScript, this would look like:

function handleCrash()
{
    test.log("Deleting lock files after application crash");
    deleteLockFiles();
}

function main()
{
    installEventHandler("Crash", "handleCrash");
}

A third special event are Timeout events. These events are triggered whenever the AUT fails to respond to some Squish command within five minutes. This can happen if the application got stuck in an endless loop, or if there is some other reason which keeps it from being able to respond. You can install an event handler for this event in order to handle this event gracefully.

16.1.7.2. Events for Object Types

It is also possible to react on specific events on a specified object type. For example, we can install an event handler which is always called when a QMouseEvent occurs on a QCheckBox. This means, every time the test performs e.g. a click on any check box in the application, the event handler would be called. In JavaScript we could implement it as follows:

function handleCheckBox(obj) {
    test.log("QCheckBox clicked", "check box '" + objectName(obj) + "' clicked")
}

function main() {
    installEventHandler("QCheckBox", "QMouseEvent", "handleCheckBox");
}

16.1.7.3. Object Events

The third kind of event handling is to react on events on a specific instance of an object. As an example we could install an event handler which is called every time a line editor receives a QKeyEvent. This means the event handler will be called every time the test types some text into the line editor. In Tcl we would implement it like this:

proc handleKeyEvent {obj} {
    set firstName [property get $obj text]
    test log "First Name Changed" "First Name changed to $firstName"
}

proc main {} {
    set lineedit [findObject "Addressbook.ABCentralWidget.FirstName"]
    installEventHandler $lineedit QKeyEvent handleKeyEvent
}

16.1.8. Synchronization Points

When recording a script in Squish, the event recorder automatically inserts snooze statements into the script. Those statements make the script to wait for a specified amount of seconds. This is important to ensure that a script is replayed in the same speed as it was recorded. So if the user waited for e.g. a window to pop up, the script will do the same.

Using snooze statements is the simplest way of synchronizing the AUT and the script. But in many cases, just waiting for a certain amount of time isn't sufficient. E.g. when a script is recorded on a fast machine and later replayed on a slow computer a snooze might wait not long enough.

Therefor, in the record settings dialog, you can choose to not insert snooze statements but waitForObject statements instead. This way, before every action recorded, a waitForObject statement will be recorded for the object to be accessed. So on replay, Squish will then, instead of just waiting for a given amount of time, wait for the given object to exist and be accessible.

Alternatively, Squish offers a waitFor function. This function waits until a given condition becomes true, or optionally, until a specified time out expired.

The condition can be anything from a property to a complex script statement. The following Python code would wait until the dialog "Addressbook.FileSave" pops up. In case it doesn't pop up after 5 seconds, an error will be thrown:

ok = waitFor("object.exists('Addressbook.FileSave')", 5000)
if not ok:
    test.fatal("Dialog Addressbook.FileSave didn't pop up'")

The following JavaScript snippet will wait until the file "addresses.addr" exists in the AUT's directory (which is the current working directory). Since no timeout is specified, this call will wait forever in case the file gets never created:

waitFor("QFile.exists('addresses.tsv')");

The following Tcl code will wait for a maximum of 2 seconds for the Add button to become disabled:

set addButton [findObject "Addressbook.ABCentralWidget1.AddButton"]
set ok [waitFor {property get $addButton enabled} 2000]
if {$ok == false} {
    test fatal "Add button still enabled after 2 seconds"
}

The examples above have shown different variations of synchronization points. As the condition which is passed to the waitFor function can be any script code which can be evaluated, there are no limits to synchronization points.

16.1.9. Testing Multiple AUTs from a Single Test Script, Working with ApplicationContext

Usually, as also shown in the tutorials, one application under test is specified for a test suite. This AUT is then executed and accessed by each test case. In Squish it is also possible to start multiple applications and access and test all of them. This allows to test the interaction between different applications or multiple instances of the same application which is necessary to test client/server systems.

For each started application, an ApplicationContext object is created which is used as a handle to the application. Using the application context it is also possible to query information such as the command line, the running state, etc. Using a default application context it is also possible to query such information from a single AUT. So this information is also interesting for single-AUT tests.

16.1.9.1. Starting and Accessing Multiple AUTs

When testing multiple applications from one test script, the first step is to specify no application in the test suite settings of the test suite. To do that in the Squish IDE open Test Suite|Settings and go to the page Settings. Under AUT simply choose <No AUT>.

The script command to start an application is startApplication. This command starts the given application (assuming it is located in an application path- see AUTs and Settings (Section 16.3)) using the given command line arguments and returns its context. The context is a handle which refers to the application.

Optionally, as second and third parameter a host and port can be passed to startApplication. This way, startApplication will connect to the squishserver on the specified host listening to the specified port instead of using the default host and port (as specified in the Squish IDE's settings or squishrunner's command line). This allows to control multiple applications on multiple computers from one test script.

Using setApplicationContext the currently active application can be set. This means, that script function calls will be executed in this currently active application. When calling startApplication, the current context is automatically set to the newly started application.

The function applicationContextList returns a list of all application contexts. Using defaultApplicationContext the context of the default application (the one specified in the test suite settings), if there is one, is returned.

[Note]Note

If you want to record and access applications which are started by the AUT and not by Squish please see Recording on Sub-Processes started by the AUT (Section 16.12.1).

The next section will discuss the usage of ApplicationContext objects in more detail. Now we will look at an example of working with multiple applications in a test script.

Suppose we have an chat client/server system with an application called chatserver which needs to be running for the communication and a chatclient which can be used for chatting. In the test we first start the chat server. The we start two clients which automatically connect to the chat server. We then type something in the message editor of the first client and check that the second client received the message. We will use Python for the test. The comments in the program code give some more details:

# start server so clients can connect to it
startApplication("chatserver")

# start first client
client1 = startApplication("chatclient")

# start second client
client2 = startApplication("chatclient")

# need to switch to client1 to be able to access it
setApplicationContext(client1)

# type something into the message editor
editor = findObject("ChatWindow.messageEditor")
type(editor, "Message for client 2")

# switch context to client2 to be able to verify 
# that the message was received
setApplicationContext(client2)

# check that the message was received
msgView = findObject("ChatWindow.messageView")
test.compare(msgView.text, "Message for client 2")

16.1.9.2. Working with ApplicationContext

Using the ApplicationContext object information about the application it refers to can be retrieved. The application context of the running AUT as defined in the test suite settings can be retrieved using the function defaultApplicationContext. When starting an application using startApplication, this function returns the application context for the newly started AUT.

A complete list of properties and functions provided by the ApplicationContext object can be found in the chapter Squish API (Section 17.1.2) in the Squish Reference Guide (Chapter 17). Here are some examples.

To print out the command line and current working directory of the test's default application the following Python code can be used:

ctx = defaultApplicationContext()
print(ctx.commandLine)
print(ctx.cwd)

To start the application myapp and calculate its maximum memory usage as long it is running, we could use the following JavaScript code (The memoryUsage property is only available in conjunction with the Squish memory module add-on):

ctx = startApplication("myapp");
var maxcpu = 0;
while(ctx.isRunning) {
    maxcpu = Math.max(ctx.usedMemory, maxcpu);
}

To print out the process identifier of the application myapp and to check for a maximum time of 10 seconds if the application is responsive (not frozen), the following Tcl code would work:

set ctx [startApplication "myapp"]
puts [applicationContext $ctx pid]
set frozen [applicationContext $ctx isFrozen 10]
test compare $frozen false

To add everything which has been written to STDOUT and STDERR by the application to the test log, classifying all STDERR messages as warnings, the following Python code would work:

ctx = startApplication("myapp")
test.log("STDOUT", ctx.readStdout())
test.warning("STDERR", ctx.readStderr())

16.1.10. Testing Qt Widgets

This chapter shows using small example code snippets in Python, Tcl and JavaScript how to test specific Qt widgets in an application and verify that their properties have the expected values and the widgets contain the expected contents.

The hardest part when implementing test scripts is to create the test verifications. As shown in the chapter Inserting a Verification Point (Section 5.2) in the tutorial Tutorial: Creating the First Test with Squish for Qt (Chapter 5) most of that can be done using the Spy and its point & click interface. But in some cases it might be most desirable to implement a verification point using script code to gain more flexibility.

Often the biggest issue there is to find out how to retrieve the information from widgets which should be tested. These examples show how to do this on different widgets of different complexity. Using the principles shown in this chapter it should be possible to test any widget in your application under test.

16.1.10.1. Accessing widgets

To test and verify a widget and its properties or contents, first we need access to the widget in the test script. To obtain a reference to the widget, the findObject function is used. This functions locates a widgets of the given name and returns a reference to it.

For this purpose we need to know the name of the widget we want to test. To find out the name of a widget the Spy tool comes in very handy (for more details about using Spy see Spy (Section 16.8.3).

The steps to find out the name are the following:

  • Start the Squish IDE and make the test suite we are working in active

  • Start the Spy on the application under test

  • Switch the Spy in Pause mode

  • Switch to the AUT and work through the GUI until the widget we want to test is visible (e.g. open the dialog it is contained in)

  • Switch back to the Squish IDE and switch the Spy into Pick mode

  • Switch to the AUT and click on the widget you want to test

  • Switch back to the Squish IDE. In the Spy object view the selected widget and its tree will be displayed. Right-click onto the object name and choose "Copy to clipboard".

  • Exit the Spy

Now the object name we were looking for is saved in the clipboard and we can paste it into the script as the argument to findObject.

For more details about the findObject functions, see Finding and Querying Qt Objects (Section 16.1.1.1).

In the following sections we always assume that we have access to the widget we want to test.

16.1.10.2. Testing Widget States

Very often the state of a widget, such as its visibility, needs to be tested. Those states can be queried on a widget by testing the value of its respective properties. Here are some examples:

Verify that the widget has the input focus in Python:

test.compare(widget.focus, True)

Verify that the widget is visible in JavaScript:

test.compare(widget.visible, true);

Verify that the widget is enabled in Tcl:

test compare [property get $widget enabled] true

16.1.10.3. Testing QCheckBox

A common test is to verify that the state of a checkbox reflects the expected settings. This can be tested by querying the value of the checkboxes checked property (Python):

test.compare(checkbox.checked, True)

16.1.10.4. Testing QListView (Qt3)

This section describes how to test using script code whether the contents of a list view widget meets the expectations.

The first possibility is to iterate over the items in the list view and check the item text. Assuming we have a list view which has one item with the text "Apple" with two children called "Orange" and "Banana", we could use the following Python code to verify this:

listview = findObject("<name of list view>")
item = listview.firstChild()
test.compare(item.text(0), "Apple")
child = item.firstChild()
test.compare(child.text(0), "Orange")
sibling = item.nextSibling()
test.compare(sibling.text(0), "Banana")

In addition we want to check that there are no more toplevel items in the list view, meaning the first item has no siblings, so QListViewItem::nextSibling() returns 0 (in JavaScript):

var item = item.nextSibling();
test.verify(isNull(item));

So, we can use the QListViewItem::firstChild() and QListViewItem::nextSibling() functions to traverse the tree of list view items. To retrieve the item text of an item, we use the QListViewItem::text() function and pass the column whose text we want to get back into this function call.

Another possibility to retrieve an item is to use the QListView::findItem() function. This can be used to check whether an item is there or if we don't want to traverse the whole tree of items but we want to start at a specific one. To check that there exists an item with the text "Orange", we would write in Tcl:

set item [invoke $listview findItem "Orange" 0]
test compare [isNull $item] false

The second argument in QListView::findItem() is the column number we search in.

A list view can also contain more sophisticated items like QCheckListItems. Let's assume the "Orange" item is such a check item and we want to verify that this item is checked (in Python):

item = listview.findItem("Orange", 0)
checkitem = cast(item, QCheckListItem)
test.compare(checkitem.state(), QCheckListItem.On)

Since item is of the type QListViewItem, we need to cast it to a QCheckListItems using the cast command. If the cast fails, 0 would be returned. Then we can call the QCheckListItem::state() function on the item and test whether this returns QCheckListItem::On which means the item is checked.

16.1.10.5. Testing QModelIndex and QItemViews (Qt4)

This section describes how to test using script code whether the contents of an item view (QTreeView, QTable, etc.) widget meets the expectations.

The first possibility is to iterate over the items in the item view and check the item text. Assuming we have a list view which has one item with the text "Apple" with two children called "Orange" and "Banana", we could use the following Python code to verify this:

     # find the tree
     tree = findObject(":Simple Tree Model")
     # get hold of an item
     item = tree.indexAt(QPoint(0, 0))
     v = item.data().toString()
     test.compare(v, "Banana"")
     # next item below
     item = item.sibling(1, 0)
     v = item.data().toString()
     test.compare(v, "Apple"")

In addition we want to check that there are no more toplevel items in the list view, meaning the first item has no siblings, so QModelIndex::sibling() returns an invalid item (in JavaScript):

var item = item.sibling(1, 0);
test.verify(!item.isValid());

So, we can use the QModelIndex API to traverse the model of an item view. To retrieve the item text of a model index, we use the QModelIndex::data() function.

16.1.10.6. Testing QPopupMenu and QMenuBar (Qt3)

If you want to check states, etc. of the menu using script code and not use Squish IDE's point & click interface to insert such a verification point, it is also possible to directly access menus from script code.

Similar to QListView, menus contain items which can be retrieved by name and which provide functions to verify the state.

On froglogic's Web site you can find the article Squish: Testing Menus which explains how to test menus using script code in great detail.

16.1.10.7. Testing QTable (Qt3)

Similar to QListView, often the contents of a QTable widget needs to be tested. QTable again consists of items which can be retrieved using the QTable::item() function.

To test whether the cell 5/4 contains the text "Kiwi", the following Python code can be used:

table = findObject("<name of table>")
cell = table.item(5, 4)
test.compare(cell.text(), "Kiwi")

Similarly to QListView, it is again possible to cast the cell items to more sophisticated items like e.g. QCheckTableItem (in case such items are used in the table) to query properties on those.

16.1.10.8. Testing QAction, QMenu and QMenuBar (Qt4)

If you want to check states, etc. of the menu using script code and not use Squish IDE's point & click interface to insert such a verification point, it is also possible to directly access menus and actions from script code.

All items in a menu are QAction objects. So you can just normally find the QActions in the objects tree using findObject and access the complete QAction API to query and verify menu items' states.

Instead of looking up a QAction globally it can also be retrieved as part of the QMenu instance it has been added to. Here is a Perl function that finds the QAction by its text:

sub findActionInMenu {
    my $menu = $_[0];
    my $text = $_[1];
    my $count = $menu->count();
    for (my $index = 0; $index < $count; ++$index) {
        my $id = $menu->idAt($index);
        my $action = $menu->findItem($id);
        if ($action->text() . "" cmp $text) {
            return $action;
        }
    }
    return undef;
}

Find an example call of the function that also checks the enabled and checked state below:

my $fileMenu = findObject(":Application.File_QMenu");
my $a = findActionInMenu($fileMenu, "Save");
my $enabled = $a->isEnabled();
my $checked = $a->isChecked();

16.1.11. Testing Tk Widgets

This chapter shows using small example code snippets in Tcl how to test specific Tk widgets in an application and verify that their properties have the expected values and the widgets contain the expected contents.

The hardest part when implementing test scripts is to create the test verifications. As shown in the chapter Inserting a Verification Point (Section 5.2) in the tutorial Tutorial: Creating the First Test with Squish for Tk (Chapter 7) most of that can be done using the Spy and its point & click interface. But in some cases it might be most desirable to implement a verification point using script code to gain more flexibility.

Often the biggest issue there is to find out how to retrieve the information from widgets which should be tested. These examples show how to do this on different widgets of different complexity. Using the principles shown in this chapter it should be possible to test any widget in your application under test.

16.1.11.1. Accessing widgets

To test and verify a widget and its properties or contents, first we need access to the widget in the test script. To obtain a reference to the widget, the findObject function is used. This functions locates a widgets of the given name and returns a reference to it.

For this purpose we need to know the name of the widget we want to test. To find out the name of a widget the Spy tool comes in very handy (for more details about using Spy see Spy (Section 16.8.3).

The steps to find out the name are the following:

  • Start the Squish IDE and make the test suite we are working in active

  • Start the Spy on the application under test

  • Switch the Spy in Pause mode

  • Switch to the AUT and work through the GUI until the widget we want to test is visible (e.g. open the dialog it is contained in)

  • Switch back to the Squish IDE and switch the Spy into Pick mode

  • Switch to the AUT and click on the widget you want to test

  • Switch back to the Squish IDE. In the Spy object view the selected widget and its tree will be displayed. Right-click onto the object name and choose "Copy to clipboard".

  • Exit the Spy

Now the object name we were looking for is saved in the clipboard and we can paste it into the script as the argument to findObject.

In the following sections we always assume that we have access to the widget we want to test.

16.1.11.2. Testing Widget States

Very often the state of a widget, such as enabled and disabled, needs to be tested. Those states can be queried on a widget by testing the value of its respective properties. Here is any example:

Verify that an entry widget is enabled:

test compare [property get $entry state] "normal"

To check that the entry is disabled, we would use:

test compare [property get $entry state] "disabled"

16.1.11.3. Checkbuttons and Radiobuttons

When using Tk's standard radiobutton or checkbutton, querying its value (checked or not) is a bit more involved, since this widget doesn't provide any convenient property for that.

To check that a radiobutton is checked, first we need to write the following code which queries the variable and value properties of the button:

set rb [findObject ":myapp.radiobutton"]
set var [property get $rb "variable"]
set val [property get $rb "value"]

To verify that the button is checked, the value of the variable property has to be equal to the value property of the radiobutton. So we can do the following:

set v [invoke tcleval "return \$$var"]
test compare $v $val

For a checkbutton this works similarly. Just that a checkbutton has an on- and offvalue. So the first lines are the same again:

set cb [findObject ":myapp.checkbutton"]
set var [property get $rb "variable"]
set onval [property get $rb "onvalue"]
set ofval [property get $rb "offvalue"]

To verify that the button is checked, the value of the variable property has to be equal to the onvalue property of the checkbutton. So we can do the following:

set v [invoke tcleval "return \$$var"]
test compare $v $onval

To verify that the button is not checked, the value of the variable property has to be equal to the offvalue property of the checkbutton. So we can do the following:

set v [invoke tcleval "return \$$var"]
test compare $v $offval

16.1.11.4. Text fields

A standard Tk entry widget's contents can be queried using the getvalue property. To check that an entry contains the text hello, we use the following Tcl code:

set entry [findObject ":myapp.entry"]
test compare [property get $entry getvalue] "hello"

Querying the contents of Tk's multiline text widget is a bit more involved. For that we have to call get on the widget and pass the start- and end-index we want to query. To get the whole text and compare it, we can use the following Tcl code:

set text [invoke tcleval ".textfield get 1.0 end"]
test compare $text "line 1\nline 2"

If the BWidget's Entry widget is used, we can query and compare its contents like that:

set bentry [findObject ":myapp.bentry"]
test compare [property get $entry text] "hello"

16.1.11.5. Listbox

In this section we look at an example to check the currently selected item of a standard Tk listbox. This is done by calling get on the listbox. In the example we want to check that the item Banana is the active item:

set act [invoke tcleval ".listbox get active"]
test compare $act "Banana"

16.1.11.6. iwidget Radiobox

This example shows how to check which radio button in a radiobox from the Tk iwidget set is currently on. This is again done, as with many other widgets, using the getvalue property. We want to verify the currently the radio button Orange is on:

set rbox [findObject ":myapp.rbox"]
test compare [property get $rbox getvalue] "Orange"

16.1.12. Testing XView Widgets

16.1.13. Testing Web Elements

This chapter shows using small example code snippets in Python and JavaScript how to test specific HTML elements in a Web application and verify that their properties have the expected values and e.g. form elements contain the expected contents.

The hardest part when implementing test scripts is to create the test verifications. As shown in the chapter Introducing Verification Points (Section 9.3) in the tutorial Tutorial: Creating the First Test with Squish for Web (Chapter 9) most of that can be done using the Spy and its point & click interface. But in some cases it might be most desirable to implement a verification point using script code to gain more flexibility.

Often the biggest issue there is to find out how to retrieve the information from elements which should be tested. These examples show how to do this on different element types of different complexity. Using the principles shown in this chapter it should be possible to test any HTML element in your application under test.

16.1.13.1. Accessing elements

To test and verify a HTML element and its properties or contents, first we need access to the element in the test script. To obtain a reference to the element, the findObject function is used. This functions locates a element of the given name and returns a reference to it.

For this purpose we need to know the name of the element we want to test. To find out the name of a widget the Spy tool comes in very handy (for more details about using Spy see Spy (Section 16.8.3).

The steps to find out the name are the following:

  • Start the Squish IDE and make the test suite we are working in active

  • Set a breakpoint in the test script we are working with at the location where you want to insert a verification

  • Run the test until there

  • When the IDE pops up again, start the Spy

  • Switch the Spy into Pick mode

  • Switch to the Web browser and click on the element you want to test. Since left clicks on form elements and links will trigger actions in the Web page, click e.g. with the right mouse button on the element to pick it to avoid triggering any action in the Web site.

  • Switch back to the Squish IDE. In the Spy object view the selected widget and its tree will be displayed. Right-click onto the object name and choose "Copy to clipboard".

  • Stop the test run

Now the object name we were looking for is saved in the clipboard and we can paste it into the script as the argument to findObject.

In the following sections we always assume that we have access to the element we want to test.

16.1.13.2. Testing the state of Web elements

Very often the state of an element, such as enabled and disabled, needs to be tested. Those states can be queried on an element by testing the value of its respective properties. Here is an example in JavaScript:

Verify that an input form widget of type text is enabled:

entry = findObject("{tagName='INPUT' id='input' form='myform' type='text'}");
test.compare(entry.disabled, false);

To check that the entry is disabled, we would use:

entry = findObject("{tagName='INPUT' id='input' form='myform' type='text'}");
test.compare(entry.disabled, true);

16.1.13.3. Form checkboxes and radiobuttons

To verify that a radiobutton or checkbox is checked, we just need to query the checked property e.g. in Python:

radio = findObject(":{tagName='INPUT' id='r1' name='rg' form='myform' type='radio' value='Radio 1'}")
test.compare(radio.checked, True)

16.1.13.4. Form Text fields

The contents of text and textarea form elements can be queried using the text property. To check that an entry contains the text hello, we use the following JavaScript code:

var entry = findObject("{tagName='INPUT' id='input' form='myform' type='text'}");
test.compare(entry, "hello");

16.1.13.5. Form selection boxes

In this section we look at an example to check the currently selected item of single- and multi selection form elements (select and select-one form elements).

To check that in a single selection element (represented as combobox in the Web browser) the option at index 2 is selected, we can use the following Python code:

sel = findObject(":{tagName='INPUT' id='sel' form='myform' type='select-one'}")
test.compare(sel.selectedIndex, 2)

Alternatively, we can check that the option with the text Banana is selected:

sel = findObject(":{tagName='INPUT' id='sel' form='myform' type='select-one'}")
test.compare(sel.selectedOption, "Banana")

To verify in a multi selection element (represented usually using a listbox) the options at index 0 and 2 are selected and that the option 1 is not selected and contains the text "Orange" we can use the following JavaScript code:

sel = findObject(":{tagName='INPUT' id='sel' form='myform' type='select'}");
opts = sel.options();
test.compare(opts.optionAt(0).selected, true); // index 0 selected
test.compare(opts.optionAt(2).selected, true); // index 2 selected
test.compare(opts.optionAt(1).selected, true); // index 1 NOT selected
test.compare(opts.optionAt(1).text, "Orange"); // index 1 has text "Orange"

16.1.13.6. Non-form elements and synchronization

Of course it is also possible to verify the states and contents of any other element in the DOM tree of the Web applications.

One example could be that in the table with the ID result_table we want to verify the existence of the Text 'Total: 387,92' in JavaScript:

var table = findObject(":{tagName='TABLE' id='result_table]'}");
var contents = table.innerText;
test.verify(contents.find("Total: 387,92") <> -1);

Another example would be to check that the DIV with the ID syncDIV is hidden, you can do in Python:

div = findObject(":{tagName='DIV' id='syncDIV'}")
test.compare(div.property("style.display"), "hidden")

Often such DIV elements are used form synchronization. For example, after a new page is loaded, we want to wait until the DIV element exists and it is hidden (some JavaScript code in the HTML page hides the DIV, so when the DIV is hidden we know the browser is ready because the JavaScript has been executed). So this can be implemented in Python:

def isDIVReady(name):
    if not object.exists(":{tagName='DIV' id='" + name + "'}"):
       return False
    if findObject(":{tagName='DIV' id='syncDIV'}").property("style.display") <> "hidden":
        return False
    return True

....

    waitFor("isDIVReady('syncDIV')")

16.1.14. Testing Java™ applications

This chapter shows using small example code snippets in JavaScript how to test specific Java widgets in an application and verify that their properties have the expected values and the widgets contain the expected contents.

The hardest part when implementing test scripts is to create the test verifications. As shown in the chapter Introducing Verification Points (Section 10.3) and in the tutorial Tutorial: Creating the First Test with Squish/Java™ (Chapter 10), most of that can be done using the Spy and its point & click interface. But in some cases it might be most desirable to implement a verification point using script code to gain more flexibility.

Often the biggest issue there is to find out how to retrieve the information from widgets which should be tested. These examples show how to do this on different widgets of different complexity. Using the principles shown in this chapter it should be possible to test any widget in your application under test.

16.1.14.1. Accessing widgets

To test and verify a widget and its properties or contents, first we need access to the widget in the test script. To obtain a reference to the widget, the findObject function is used. This functions locates a widgets of the given name and returns a reference to it.

For this purpose we need to know the name of the widget we want to test. To find out the name of a widget the Spy tool comes in very handy (for more details about using Spy see Spy (Section 16.8.3).

The steps to find out the name are the following:

  • Start the Squish IDE and make the test suite we are working in active

  • Start the Spy on the application under test

  • Switch to the AUT and work through the GUI until the widget we want to test is visible (e.g. open the dialog it is contained in)

  • Switch back to the Squish IDE and switch the Spy into Pick mode

  • Switch to the AUT and click on the widget you want to test

  • Switch back to the Squish IDE. In the Spy object view the selected widget and its tree will be displayed. Right-click onto the object name and choose "Copy to clipboard".

  • Exit the Spy

Now the object name we were looking for is saved in the clipboard and we can paste it into the script as the argument to findObject.

For more details about the findObject functions, see Finding and Querying Java™ Objects (Section 16.1.5.1).

In the following sections we always assume that we have access to the widget we want to test.

16.1.14.2. Testing Widget States

Very often the state of a widget, such as its visibility, needs to be tested. Those states can be queried on a widget by testing the value of its respective properties. Here are some examples in the various scripting languages that Squish supports:

Verify that the widget has the input focus in Python:

test.compare(widget.focusowner, True)

Verify that the widget is visible in JavaScript:

test.compare(widget.visible, true);

Verify that the widget is enabled in Tcl:

test compare [property get $widget enabled] true

16.1.14.3. Testing CheckBox

A common test is to verify that the state of a checkbox reflects the expected settings.

16.1.14.3.1. AWT CheckBox

This can be tested by querying the value of the checkboxes state property in JavaScript:

test.compare(checkbox.state, true);
16.1.14.3.2. Swing JCheckBox

This can be tested by querying the value of the checkboxes selected property in JavaScript:

test.compare(checkbox.selected, true);
16.1.14.3.3.  SWT Button of type CHECK or RADIO

This can be tested by querying the value of the checkboxes selection property in JavaScript:

test.compare(checkbox.selection, true);

16.1.14.4. Testing List and ComboBoxes

Although the different GUI Java™ toolkits that Squish supports have different implementations for list and comboboxes, their items are wrapped by Squish in ItemProxy objects. These objects act as child objects of the list or combobox object. The child objects are called item_0, item_1 and so on.

The ItemProxy objects have the properties text and selected. For the SWT based List and Combo widgets, there is an extra property called control. The AWT/Swing based ones have an extra property called component.


A picture of the spy showing the items of a List with the
    SWT toolkit.
In the spy you get for each item an ItemProxy derived object.

In SWT, the List and Combo widget have the method getSelectionIndex(). Squish generates from this method a property calls selectionindex. So for the SWT toolkit, these two tests are equal:

test.compare(list.selectionindex, 2);
test.compare(list.item_2.selected, true);

Note however that the second test gives a script error if the list has less then three items. And that the first test only gives one of the selections if the list supports multiple selections. In that case you can only use the generated ItemProxy objects for verification points.

16.1.15. Semi-Automatic Tests: Querying User Input

While Squish allows to automate the GUI testing, in some cases it might still be of interest to have testers to input some data. E.g. when testing a software which works together with a hardware device, it might be necessary to ask a user when running the test if the device's state has been changed using the software as expected.

Let's assume we create a test for a printer software. The tests exercises the software and one of the results should be that the printer printed a page with the contents "This is a test."

Since Squish can't verify that, we could put the following code into the Python test script so the person running the test has to confirm that the page was printed correctly:

[Note]Note

The code below uses Qt API (QMessageBox) to display a message box. For editions other than Squish for Qt, different APIs (such as a JavaScript alert box or a Tk message box displayed using evalJS or tcleval could be used.

def main():
    ....

def main():
    ok = QMessageBox.question(0, "Require User Input", 
                              "Did the printer print a page with the contents 'This is a test'?", 
                              "Yes", "No") == 0
    if ok:
        test.passes("Printing worked", "Printer printed test page correctly")
    else:
        test.fail("Printing failed", "Printer didn't printed test page")
    ....

Another example would be to query the tester for the number of pages printed. In JavaScript this can be done like this:

function main()
{
    ....
    var num = QInputDialog.getInteger( "Require User Input", "How many pages have been printed?" );
    if ( num == 0 ) {
        test.fail( "No pages printed");
    } else {
        test.pass(num + " pages printed");
    }
    ....
}

The same in Tcl:

proc main {} {
    ....
    set num [invoke QInputDialog getInteger "Require User Input" "How many pages have been printed?"]
    if {$num == 0} {
        test fail "No pages printed"
    } else {
        test pass "$num pages printed"
    }
    ....

This section showed again that using Squish's powerful script bindings classes like QMessageBox and QInputDialog (or the respective DOM/HTML, Tk or XView APIs depending of the used Squish edition) can be used in test scripts to allow querying for user input.