Automating Accessibility Testing: How to Check for Sane Tab Order

Automating Accessibility Testing: How to Check for Sane Tab Order

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():
    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 False otherwise.

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 current and 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).

The 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,

After that, let’s overwrite the recorded main function with a call to our new tab order sanity check function:

import names

def main():
    mouseClick(waitForObjectItem(names.invoice_QComboBox, "AXV-12000"))
    if check_taborder_sanity():
        test.passes("Tab order coordinates are sane")
    else:"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?

Wrap up

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 QApplication.focusWidget(),
  • We used type(..., "<Tab>") (documentation) to navigate through our tab order, and,
  • We computed screen coordinates for widgets with object.globalBounds(my_obj) (documentation).

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.

Since joining froglogic as a developer in 2017, he's been mostly tinkering with Qt, Python and web technologies. His free time is consumed by playing Dungeons&Dragons with his friends and trying out the latest in VR gaming.


Leave a reply

Your email address will not be published. Required fields are marked *