Ensuring sane tab ordering is part of motor and dexterity impairment accessibility, as it makes sure that alternate UI navigation via the keyboard is not only possible, but also logically comprehensible. The following example shows how you can utilize Squish to automate accessibility testing.
Tab Order Sanity and Completeness
This article (part of the University of Washington’s IT Accessibility Checklist) nicely outlines the requirements for proper tab ordering. “Users […] expect to move sequentially from left to right and top to bottom through the focusable elements“, which can be translated into onscreen coordinate checks of focusable widgets.
We are going to use the Qt
paymentform example included in the Squish installation as our Application Under Test (AUT). After creating a new test suite and test case, click record, navigate to the first element of the tab order you want to test, and click it. In this example, select the invoice combo box and Squish will return a test script similar to this:
import names def main(): startApplication("paymentform") mouseClick(waitForObject(names.invoice_QComboBox), ...)
We will come back to that later.
Let’s create a function for our check and name it
check_taborder_sanity. It will only receive an optional
up_threshold parameter to allow some space for widgets to go against the top to bottom movement and will go through every widget in the tab order and compare its top left origin with that of the last widget. Finally,
True is returned if all of the widgets are placed according to the rules stated above and
def check_taborder_sanity(up_threshold = 0): first = QApplication.focusWidget() current, bbox, minimum = taborder_next(first) while current != first: if bbox.y < minimum.y - up_threshold: return False elif bbox.x < minimum.x and bbox.y - minimum.y <= 0: return False current, bbox, minimum = taborder_next(current) return True
At the beginning, we retrieve the currently focused element with
QApplication.focusWidget() and store it in the variable
first and then get the next in the order by calling
taborder_next (which is explained below). This gives us the next widget
current, its screen coordinates
bbox and the screen coordinates of the previous widget that act as the
minimum that is tested against. By comparing the object references of
first, we can check whether the loop was completed as we go through the tab order. We then compare the origins of both the current and the previous widget in order to allow only that:
- The current widget is either below or to the right of previous one, unless:
- It moves both down and left at the same time (following a downward z-stroke path).
taborder_next function takes the current widget, performs the
Tab key press on it and returns the next widget, its bounding box and the bounding box of the previous widget:
def taborder_next(from_obj): type(from_obj, "<Tab>") next_obj = QApplication.focusWidget() return (next_obj, object.globalBounds(next_obj), object.globalBounds(from_obj))
After that, let’s overwrite the recorded
main function with a call to our new tab order sanity check function:
import names def main(): startApplication("paymentform") mouseClick(waitForObject(names.invoice_QComboBox)) mouseClick(waitForObjectItem(names.invoice_QComboBox, "AXV-12000")) if check_taborder_sanity(): test.passes("Tab order coordinates are sane") else: test.fail("Tab order coordinates are not sane")
One last thing before we are finished: interacting with the initial element might block us from proceeding in the intended tab order. In this example, clicking on the invoice combo box opens the respective dropdown list. It is easy to fix by simply selecting an item from the list, but we have to be aware that this might happen.
At last! Running this test should result in a pass with a
"Tab order coordinates are sane" message!
Here are some checks that could be added to the above test:
- Is the focused object visible as such? (See Screenshot Verifications)
- Are all of our focusable widgets part of the tab order?
Let’s go over the tools used to create the above test:
- We used GUI toolkit script bindings for Qt to retrieve the focused widget with
- We used
type(..., "<Tab>")(documentation) to navigate through our tab order, and,
- We computed screen coordinates for widgets with
While some accessibility requirements will still require a certain level of manual testing (e.g. photosensitive content/seizure prevention), the Squish GUI introspection capabilities give you the tools to increase your automation coverage. Remember that improving accessibility not only benefits those with disabilities, but are nice quality of life improvements for any user.