Document Information

Last modified:
2010/02/03 18:38 by dwagner

How to Write qooxdoo Tests with Selenium

Once you’ve set up all the infrastructure for Selenium, you want to put it all to practice and write tests for you qooxdoo apps. This page currently contains an assortment of tips and tricks that shall aid you through this process.

Since Selenium performs a sequence of actions on the web application, and most of these actions operate on specific elements of the web app (”targets”), specifying those targets through so called “locators” is crucial for the success of the test actions. A lot of the recipies are therefore concerned with locators.

Many examples also use HTML id’s in the same format as qooxdoo’s auto-generated id’s (see further down). Except where explicitely stated this is only to shorten the sample locators and doesn’t mean the given example is limited to these locators. E.g. in XPaths any location step specified with an @id attribute can be replaced in working code with another location step that sufficiently specifies the desired element.

Using Auto-generated IDs (qooxdoo 0.7 only)

Specifying the action target through its HTML id attribute is one of the fastest and most reliable cross-browser locator methods. To cater for that qooxdoo was augmented with the ability to auto-generate id’s which should be stable between invocations, browsers and source and build versions. When generating your qooxdoo application you add the following option:

--use-setting qx.widgetDebugId:true

This can be achieved through one or both of the following lines in your Makefile:

APPLICATION_ADDITIONAL_SOURCE_OPTIONS = --use-setting qx.widgetDebugId:true
APPLICATION_ADDITIONAL_BUILD_OPTIONS     = --use-setting qx.widgetDebugId:true

This will result in all the qooxdoo widgets in your app having a unique id. You can then use a common tool like Firebug, DOM Inspector or IE‘s DebugBar to look up the specific id of the widget you want your test to interact with, and use it as a locator in the corresponding Selenium commands.

A thing to note here is the fact that this generation of id’s will blow up your application code. All the strings representing the id’s have to be kept in the code. Lets say you have a qooxdoo application with about 2,000 widgets. With an average length of 20 bytes per id (which is a lower bound guess) you quickly wind up with an additional payload of 40K or more in code size. So if you are concerned about application size you might want to disable id auto-generation in the build version of your application.

Entering Text

Using standard Selenium commands

For the Selenium type and typeKeys commands the target has to be precisely specified. If your targets are qooxdoo qx.ui.form.TextField or qx.ui.form.TextArea widgets, they use native form fields underneath, i.e. in the generated DOM there will be TEXTAREA or INPUT descendant elements. Since the exact depth of the descendant elements is browser-dependent, a loose coupling in XPath is recommended. Supposing you use id’s for the qooxdoo widgets, these XPath would be suitable locators:

xpath=//div[@id=qx.ui.form.TextField.64]//input
xpath=//div[@id="qx.ui.form.TextArea.99"]//textarea

(Mind the second ‘//’ that allows for intermediate elements).

Using the qooxdoo Selenium extension

The qooxdoo user extension’s qxType and qxTypeKeys commands behave like the corresponding Selenium commands, with some convenient enhancements: The commands will work with both the standard Selenium locators and the qx... locators and it’s not necessary to locate the INPUT or TEXTAREA child element, the widget’s content element (usually a DIV) will suffice. Also supported are widgets like qx.ui.form.ComboBox that have a child control with an input element. Some examples:

A qx.ui.form.TextArea that has been assigned an ID using textArea.getContentElement().getDomElement().id = “textArea23”:

qxTypeKeys("textArea23", "Some text");

A qx.ui.form.ComboBox in a composite container:

qxType("qxh=qx.ui.container.Composite/qx.ui.form.ComboBox", "Some text");

Radiobuttons and Checkboxes

Radiobuttons and Checkboxes are click targets, and are thus more robust than type targets. Although they too are implemented using native input elements, it suffices to specify the enclosing qooxdoo widget as the target. If you are using id’s for the qooxdoo widgets that could look like this:

id=qx.ui.form.CheckBox.108

Using XPaths without id’s to that end can be more tricky. A good strategy seems to locate the text label of the respective input element and then navigate from there. Here are two examples using checkbox and radiobutton:

xpath=//div[text()="Text of the checkbox I want to click"]/preceding-sibling::input
xpath=//div[text()="Text of the radiobutton I want to click"]/preceding-sibling::input

Opera and Checkboxes

With the current implementation, in the case of checkboxes Opera seems to prefer the standard click command of Selenium over the more specific qxClick from the qooxdoo extensions. (TODO: qxClick should be adapted to work with Opera in these cases too).

Context Menu

To get a context menu use onContextMenu and onContextMenuAt. These are available in release 1.0 and later of Selenium. Note that the FireFox addon All-in-One Gestures will cause problems with context menu events not being received consistently.

Opening TreeFolders

To open a tree folder in a Tree widget, you need to click on one of its descendant elements. A TreeFolder composes of the +/- image that denotes visible or hideable children; the opened or closed folder icon; and the folder label. Clicking on the TreeFolder widget itself will not trigger the desired action. Clicking the following locator

id=qx.ui.tree.TreeFolder.636

will not yield the desired result of opening the tree folder. Rather, using one of the beforementioned subelements will do the job:

xpath=//*[@id="qx.ui.tree.TreeFolder.636"]//img

This will hit the +/- image since it is the first img descendant of the TreeFolder node (Using this is mandatory for closing the folder again!). Alternatively, you might click on the label of the tree folder which would also result in the label being selected:

xpath=//*[@id="qx.ui.tree.TreeFolder.636"]//*[text()="My Folder Label"]

This also adds a bit of self-documenting code to your tests since the folder label is easily recognizable.

Clicking in tables

By coordinates

A fundamental problem of Selenium is that the mouse actions have screen X and Y coordinates set to 0. However, qooxdoo’s table uses the coordinates to calculate the row and column that has been clicked on.

The qooxdoo extensions include a qxClickAt command which solves this problem. The screen position of the selected element is calculated and provided in the mouse click sent to the browser.

In qooxdoo 0.8, tables use HTML divs to render their cells, so you can’t use a qxh locator to select a specific cell. Instead, you can issue a qxClick command on the table’s clipper widget and specify the viewport coordinates of the cell you want to click:

selenium.qxClick("qxh=qx.ui.table.Table/qx.ui.container.Composite/qx.ui.table.pane.Scroller/qx.ui.table.pane.Clipper", "clientX=350,clientY=270");

By row/column (qooxdoo 0.8+)

Using the qxTableClick command, you can specify a cell to be clicked on:

selenium.qxTableClick("qxh=qx.ui.table.Table", "row=12,col=4");

Instead of specifying the the column index using the col parameter, you can locate a column using it’s column ID or the column name which is displayed in the table header. Use the parameters colId or colName, respectively

To simulate a double click, add the double=true parameter to your option string. Use button=right to send a contextMenu event.

Table header cells (qooxdoo 0.8+)

Use the qxTableHeaderClick command to click a column header cell (e.g. to sort the table data by this column). The syntax is like qxTableClick except of course you only specify a column.

Drag and drop (qooxdoo 0.8)

Use the “qxDragAndDropToObject” command to drag one widget over another:

qxDragAndDropToObject("qxh=qx.ui.basic.Atom", "qxh=qx.ui.form.TextArea");

The first argument is a locator for the drag target, the second argument is a locator for the drop target.

Clicking in ComboBoxEx

ComboBoxEx uses a drop-down which is a table. If using qxClickAt fails to click on an item in the drop-down, use mouseDownAt and mouseUpAt instead with parameters set to 2,2.

This works because the parameters are interpreted as offsets to the coordinates of the selected item. The raw coordinates are outside the bounding pane and so are rejected when qxClickAt is used.

IE and XPath

IE (7, in my case) seems to have a strange take on XPaths when performance is concerned (This I heard mention by several others, too). For example, while a command using the following locator

xpath=//*[@id="qx.ui.tree.TreeFolder.668"]

takes about 1 seconds to return on my test system, this one

xpath=//*[@id="qx.ui.tree.TreeFolder.668"]/div[1]/div[1]/img

takes 4-5 times as long! This is especially surprising since the second locator only adds “deterministic” elements to the path where there is little or no search involved.

That means: You might want to pay close attention to how your tests are running under IE if timing issues come up, e.g. when you schedule multiple tests or monitor runtime performance. This is especially crucial when system pop-ups from the browser that relate to script responsiveness (”There is a script taking a loooong time ... do you want to continue?”) interfer with your tests. You might have to do some XPath tuning and tweaking for IE.

Testing Inline Applications

The widget hierarchy of qooxdoo applications extending qx.application.Inline differs from standalone GUI apps in that there isn’t a single root widget that is the ancestor of all the GUI‘s widgets, but rather there is one or more “qooxdoo islands” that widgets can be attached to. This means the hierarchical qxh=... locator won’t work unless you specify the DOM element that the “qooxdoo island” (AKA inline root) is attached to. The syntax for this is as follows:

qxh=inline:rootElementLocator//qxhLocator
  • rootElementLocator is a standard Selenium locator that must find the “island” element
  • qxhLocator is a hierarchical qooxdoo locator

An example: If you create a fresh Inline skeleton, the index.html file contains a div element with the id “isle”. The qooxdoo application attaches a qx.ui.root.Inline to it and places a qx.ui.container.Composite with a qx.ui.form.Button inside. To find this button, you could use the following locator:

qxh=inline:isle//qx.ui.container.Composite/qx.ui.form.Button

Information

Last modified:
2010/02/03 18:38 by dwagner

Account

Not logged in

 
 

Rich Ajax Platform (RAP)

RAP uses qooxdoo, Java and the Eclipse development model to build rich web applications. Read more...

qooxdoo Web Toolkit (QWT)

Similar to GWT this framework allows to create impressive qooxdoo applications just using Java. Read more...

Pustefix

Pustefix is a MVC-based web application framework using Java and XML/XSLT. Read more...

 
SourceForge.net Logo

Bad Behavior has blocked 0 potential spam attempts in the last 7 days.