5.6. How to Use the Tk API

Table of Contents

5.6.1. How to Find and Query Tk Objects
5.6.2. How to Access Tk Object Properties
5.6.3. How to Use tcleval
5.6.4. How to Use the Tk Convenience API
5.6.5. How to Test Tk Widgets

One of Squish's most useful features is the ability to access the toolkit's API from test scripts. This gives test engineers sufficient flexibility to allow them to test just about any aspect of 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 convenience API (see How to Use the Tk Convenience API (Section 5.6.4)) to execute common GUI actions such as clicking a button or selecting a menu item.

The How to Test Tk Widgets (Section 5.6.5) section later in this manual presents various examples that show how to use the scripting Tk API to access and test complex Tk widgets.

[Note]Tk Object Names (Qualified Names)

Squish uses a completely different object naming scheme for Tk applications than for other toolkits. Tk identifies objects using qualified names, e.g., “myapp.myframe.mylabel”. Squish takes advantage of Tk's existing naming scheme and uses it for identifying objects in Tk tests.

A qualified object name is a name like myapp.frame1.okbutton. The period notation is used as a separator (rather like / or \ in file paths), that is used to identify a particular object by its position in the object hierarchy. The application's main window is the root of the hierarchy, and contains all the application's top-level widgets, some of which contain child widgets, and so on. In the example above, the okbutton is a child of frame1, which in turn is a child of myapp (the applicaton's main window).

5.6.1. How to Find and Query Tk Objects

Squish provides the waitForObject function which returns a reference to the object with the given qualified object name.

To find out the name of an object, you can use the Spy tool to introspect the application. See the How to Use the Spy (Section 5.21.3) section for details. Alternatively, record a quick throw-away test in which you interact with all the AUT objects you are interested in: this will populate the Object Map (Section 7.11) with the objects' names.

To get a reference to an object—which can then be queried to check the object's properties, or which can be used to interact with the object—use the waitForObject function. For example, in Tcl you would use code like this:

Tcl
set button [waitForObject "myapp.frame1.okbutton"]

If waitForObject can't find the specified object—or if the object is not available before the timeout, for example if it is hidden—a script error is thrown which stops the script execution. In some situations it might be desirable to check to see if the object exists and only interact with the object if it is found. This can be done by using the object.exists function.

For example, suppose we want to find the okbutton as we did before, and click it—but only if it exists. In Tcl we can achieve this with the following code:

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

Using qualified object names with the waitForObject function, means that test engineers can query and interact with all the objects in the AUT's object hierarchy.

5.6.2. How to Access Tk Object Properties

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

For example, if we want to change the text in an entry widget, we can do so using the following Tcl code, and of course, substituting the qualified object name and the new text appropriately:

Tcl
set entry [waitForObject "myapp.frame1.e1"]
property set $entry text "New text"
set text [property get $entry text]
test log [toString $text]

The first two lines set the new text; the third line creates a new variable, text, and the last line prints the text to the Test Results (Test Results view (Section 8.2.17)).

5.6.3. How to Use tcleval

Although Squish test scripts can access the Tk widget properties, this is not sufficient for testing purposes, because not all the information we want to query is available through these properties. Fortunately, Squish provides a solution for this: the tcleval function. This function can execute arbitrary Tcl code which is interpreted within the scope of the AUT.

[Important]Only available with Tcl/Tk

This function is only available with Tcl/Tk applications, but not with Perl/Tk.

For example, if we want to retrieve the contents of a Tk text widget, we cannot do so through the widget's properties since the text is not available as a property. What we can do instead is to call the text widget's get function, since this returns the text widget's text between given indices. So to get the entire text we use indices 1.0 and end. Here's how we can use the tcleval function to call get on a text widget:

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

Notice that the entire argument to tceval is passed as a string. The “.textfield” is the name of the text widget (recall that . is the root of the widget hierarcy in pure Tcl/Tk).

5.6.4. How to Use the Tk Convenience API

This section provides a glimpse of the script API Squish offers on top of Tk to make it easy to perform common user actions such as clicking a button. Details of the full API are given in the Tk Convenience API (Section 6.5) section of the Tools Reference Manual (Chapter 7). Here we will just show a few examples to give a taste of what the API offers and how to use it.

Tcl
invoke clickButton [waitForObject \
    ":addressbook\\.tcl.dialog.buttonarea.ok"]
invoke type [waitForObject ":addressbook\\.tcl.dialog.email"] "com"
waitForObjectItem ":addressbook\\.tcl.#menuBar" "File"
invoke activateItem ":addressbook\\.tcl.#menuBar" "File"
waitForObjectItem ":addressbook\\.tcl.#menuBar.#file" "Open..."
invoke activateItem ":addressbook\\.tcl.#menuBar.#file" "Open..."

Here, we click a button, type some text into an entry widget, and invoke the File|Open menu option. These are the most commonly used Tk convenience functions, although there are additional ones in the API. For more examples of testing a variety of Tk widgets in AUTs see How to Test Tk Widgets (Section 5.6.5).

5.6.5. How to Test Tk Widgets

This section illustrates how to test Tk applications using Tcl—and in particular, how to test some of the standard Tk widgets. Although only a few widgets are shown, the same principles and practices apply to all Tk widgets, so by the end of this section you should be able to test any of your AUT's widgets.

The most challenging aspect of implementing test scripts is usually when we want to create test verifications. As shown in the chapter Inserting Additional Verification Points (Section 4.1.1.4) in the Tutorial: Starting to Test Tk Applications (Section 4.10.1), this can be done using the Spy and its point & click interface. But in some cases it is actually more convenient—and more flexibile—to implement verification points directly in code.

To test and verify a widget and its properties or contents in code, first we need access to the widget in the test script. To obtain a reference to the widget, the waitForObject function is used. This function finds the widget with 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, and we can get the name using the Spy tool (see How to Use the Spy (Section 5.21.3)) and adding the object to the Object Map (Section 7.11) (so that Squish will remember it) and then copying the object's name (preferably its symbolic name) to the clipboard ready to be pasted into our test. If we need to gather the names of lots of widgets it is probably faster and easier to record a dummy test during which we make sure that we access every widget we want to verify in our manually written test script. This will cause Squish to add all the relevant names to the Object Map (Section 7.11), which we can then copy and paste into our code.

5.6.5.1. How to Test Widget States

One common requirement is to test the state of a widget, in particular whether it is enable or disabled. The widget's state property holds the information we want—here are a couple of examples that show it in use:

Tcl
set entry1 [waitForObject ":myapp.entry1"]
test compare [property get $entry1 state] "normal"

set entry2 [waitForObject ":myapp.entry2"]
test compare [property get $entry2 state] "disabled"

This code verifies that the entry1 widget is enabled and that the entry2 widget is disabled.

5.6.5.2. Checkbuttons and Radiobuttons

Although the need to verify whether a standard Tk radiobutton or checkbutton is checked is a common requirement, neither of these widgets has a convenient property that we can use, so we must write a little bit more code than might have been expected.

We will start by verifying that a particular radiobutton is checked. First we must retrieve the radiobutton's variable and value properties, and then we must evaluate the variable to see if it is equal to the value—if it is, then the radiobutton is checked.

Tcl
set radiobutton [waitForObject ":myapp.radiobutton"]
set variable [property get $radiobutton "variable"]
set value [property get $radiobutton "value"]
set actual_value [invoke tcleval "return \$$variable"]
test compare $actual_value $value

First we retrieve a reference to the radiobutton, then we retrieve the two properties we are interested in. Next we evaluate the variable to get its actual value using the tcleval function, and finally we compare the actual value with the property value to see if they're the same.

We must use a similar approach for checkbuttons, except that their relevant properties are onvalue and offvalue.

Tcl
set checkbutton [waitForObject ":myapp.checkbutton"]
set variable [property get $checkbutton "variable"]
set onvalue [property get $checkbutton "onvalue"]
set actual_value [invoke tcleval "return \$$variable"]
test compare $actual_value $onvalue

Here, we retrieve a reference to the checkbutton, and then to the checkbutton's variable and onvalue properties. And just like we did for the radiobutton, we evaluate the variable to get its actual value, and compare this with the onvalue to see if they are the same.

If we wanted to verify that a checkbutton was not checked, we would simply retrieve the offvalue property and compare that with the actual value—if they are the same, then the checkbutton is not checked.

5.6.5.3. Text fields

A standard Tk entry widget's contents can be queried using the getvalue property.

Tcl
set entry [waitForObject ":myapp.entry"]
test compare [property get $entry getvalue] "Houston"

Here we check that an entry contains the text “Houston”.

Querying the contents of Tk's multiline text widget is a bit more involved. For that we must call the widget's get method, giving it the start and end indexes for the text we want to check.

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

Rather than retrieve a reference to the multiline text widget, instead we have used the tcleval function to execute the widget's get method with indexes that span the entire contents—this will result in all of the widget's text being returned. We then check that the text contains exactly two lines (with texts, “line 1” and “line 2”).

Squish isn't limited to Tk's standard widgets—for example, we can test a BWidget Entry widget.

Tcl
set bentry [waitForObject ":myapp.bentry"]
test compare [property get $entry text] "Apollo"

Here we retrieve the BWidget's text using its text property, and compare it to the text “Apollo”.

5.6.5.4. Listbox

One common requirement is to check the text of a Tk listbox's active item. This is easily done using the listbox's get method.

Tcl
set active [invoke tcleval ".listbox get active"]
test compare $active "Gemini"

Similarly to what we did for the multiline text widget, rather than retrieve a reference to the listbox, instead we have used the tcleval function to execute the listbox's get method with an argument of active—this will result in the listbox's active item's text being returned. We then compare the text as usual, in this case with the literal text “Gemini”.

5.6.5.5. iwidget Radiobox

The iwidget Radiobox is different from the standard Tk radiobutton, in that it has a getvalue property that holds the text of its currently checked radiobutton.

Tcl
set radiobox [waitForObject ":myapp.rbox"]
test compare [property get $radiobox getvalue] "Mercury"

If the Radiobox has radiobuttons, “Mercury”, “Venus”, and “Mars”, we can verify that the “Mercury” radiobutton is checked by retrieving a reference to the Radiobox, and then comparing the value of its getvalue property to see if it matches the text of the radiobutton that should be checked.