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
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. For example, an application like qooxdoo’s Showcase will easily amount to above 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
For the Selenium type command 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).
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
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.
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.
