at-spi-linux
Authors Linux desktop UI tests via AT-SPI - the DBus-based Assistive Technology Service Provider Interface implemented by `at-spi2-core` (registry daemon + `libatspi` C library + ATK GTK bridge). Covers enabling toolkit accessibility (`gsettings set org.gnome.desktop.interface toolkit-accessibility true`), driving GTK + Qt apps through Python `dogtail` (object-oriented + procedural APIs), inspecting the tree with Accerciser, scripting via `pyatspi`, and CI integration on headless Linux runners with `Xvfb` + `dbus-launch`. Use for Linux-side desktop tests of GTK applications, Qt apps with QAccessible enabled, and Electron apps on Linux.
Install with skills.sh (any agent)
npx skills add testland/qa --skill at-spi-linuxat-spi-linux
Overview
AT-SPI (Assistive Technology Service Provider Interface) is the Linux desktop accessibility stack - a DBus-based protocol that exposes an application's UI tree to assistive technologies (screen readers, magnifiers) and to test clients. Per the at-spi2-core README (opens in new window):
"AT-SPI2-Core is the core of an accessibility stack for free software systems."
It provides (atspi2coreraw (opens in new window)) "DBus interface definitions for the Assistive Technology Service Provider Interface" plus four runtime pieces:
| Component (atspi2coreraw (opens in new window)) | Role |
|---|---|
registryd | "Daemon managing accessible applications and enabling communication between assistive technologies and applications." |
atspi | "C language binding for DBus accessibility interfaces." |
atk | "GObject-based API for implementing accessible applications and GUI toolkits." |
atk-adaptor | "Translates ATK API calls to the atspi API layer." |
GTK applications publish accessibility via ATK; Qt applications publish via Qt's own QAccessible infrastructure, which exposes an AT-SPI surface on Linux. Either path lets a test client walk the tree.
Strategic frame: desktop-test-strategy-reference places AT-SPI alongside Windows UIA and macOS XCTest as the three OS- native accessibility-tree backends.
When to use
Step 1 - Enable toolkit accessibility
Per the dogtail README (opens in new window):
gsettings set org.gnome.desktop.interface toolkit-accessibility trueWithout this setting, GTK applications publish nothing to the AT-SPI bus and tree-walking clients see an empty desktop.
For Qt apps, the equivalent is exporting QT_ACCESSIBILITY=1 in the environment that launches the Qt binary - without it Qt's QAccessible infrastructure stays inactive and the AT-SPI tree contains no Qt children.
For Electron / Chromium, set --force-renderer-accessibility on the binary launch (or --enable-blink-features=AccessibilityAriaVirtualContent for newer Chromium accessibility surfaces).
Step 2 - Install dogtail
Per dogtailraw (opens in new window):
# From PyPI
sudo python3 -m pip install dogtail
# From source
git clone https://gitlab.com/dogtail/dogtail.git
cd dogtail
python3 -m build
sudo pip3 install dist/dogtail-2.*-py3-none-any.whlThe dogtail README (opens in new window) notes that "for Wayland support specifically: Install gnome-ponytail-daemon":
dnf install -y gnome-ponytail-daemon python3-gnome-ponytail-daemonThis bridges synthetic input events on Wayland sessions where direct X-style event injection isn't available.
Step 3 - Inspect the tree (Accerciser)
Before writing tests, walk the live tree with Accerciser (the GNOME accessibility inspector). It's the AT-SPI analogue of Inspect.exe (Windows) and Accessibility Inspector (macOS, per desktop-test-strategy-reference).
# Most distros:
sudo apt install accerciser # Debian / Ubuntu
sudo dnf install accerciser # Fedora
accerciserAccerciser walks the same registryd-published tree the test client sees, and lets the author copy out the exact role + name + description triple for each widget - which is what dogtail queries against.
Step 4 - Author a dogtail test (procedural API)
Per dogtailraw (opens in new window), dogtail "uses Accessibility (AT-SPI) technologies to interact with desktop applications". The procedural API is the closest mirror to the underlying AT-SPI tree:
#!/usr/bin/env python3
from dogtail.procedural import run, focus, click, type, keyCombo
from dogtail.utils import screenshot
# Launch the app - dogtail starts it and attaches to the AT-SPI tree
run('gnome-calculator')
# Resolve via the accessibility tree (focus + click are role-based)
focus.application('gnome-calculator')
focus.frame('Calculator')
click('7') # button named "7"
click('+')
click('3')
click('=')
# Assert via the result widget
focus.text(roleName='editbox')
assert focus.widget.text == '10', f'Expected 10, got {focus.widget.text!r}'
screenshot('calc-success.png')The role-based primitives (focus.application, focus.frame, focus.text, click, type) map onto AT-SPI roles published by the application - same primitives Orca screen reader uses.
Step 5 - Author a dogtail test (object-oriented API)
For larger suites where a Page-Object-style structure is appropriate, the object-oriented tree API exposes the registry as a navigable graph:
from dogtail.tree import root
calc = root.application('gnome-calculator')
frame = calc.child(roleName='frame')
frame.button('7').click()
frame.button('+').click()
frame.button('3').click()
frame.button('=').click()
result = frame.child(roleName='editbox')
assert result.text == '10'root is the AT-SPI desktop entry point - dogtail's wrapper over the libatspi get_desktop() function described in the libatspi reference (opens in new window):
"init() … connects to the accessibility registry and initializes the SPI."
"get_desktop() and get_desktop_list() to access the accessibility tree once connected."
Step 6 - Direct pyatspi for fine-grained control
For tests that need to listen for events on the AT-SPI bus rather than poll the tree, drop down to pyatspi:
import pyatspi
def on_state_change(event):
if event.type == 'object:state-changed:focused' and event.detail1:
print(f'Focus moved to: {event.source.name} ({event.source.getRoleName()})')
pyatspi.Registry.registerEventListener(
on_state_change,
'object:state-changed:focused',
)
pyatspi.Registry.start() # blocks; Ctrl-C to exitThis uses the AT-SPI event-listener pattern documented in atspi2docs (opens in new window):
"AtspiEventListener operates through a callback mechanism. The library defines a generic interface implemented by objects for the receipt of event notifications."
Step 7 - Run
# Standalone - assumes accessibility is enabled + an X / Wayland session
python3 tests/test_calculator.py
# Under pytest with JUnit output
pytest tests/ --junitxml=reports/atspi-junit.xmlStep 8 - Parsing results
JUnit XML from pytest feeds junit-xml-analysis (in the qa-test-reporting plugin) for the cross-runner aggregation pipeline.
For dogtail-specific diagnostics, every failing run captures a screenshot (see Step 4 - screenshot() call) plus a dogtail session log under ~/.dogtail/logs/.
Step 9 - CI integration
The Linux runner needs a display server + session DBus bus before AT-SPI clients can connect:
# .github/workflows/atspi.yml
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- name: Install AT-SPI + dogtail + app + Accerciser deps
run: |
sudo apt-get update
sudo apt-get install -y \
at-spi2-core \
python3-dogtail python3-pyatspi \
xvfb dbus-x11 gnome-calculator
- name: Enable accessibility for GTK
run: |
gsettings set org.gnome.desktop.interface toolkit-accessibility true \
|| true # the schema needs a session bus; the env block below ensures it
- name: Run tests under Xvfb + dbus-launch
env:
QT_ACCESSIBILITY: '1'
run: |
xvfb-run --auto-servernum --server-args='-screen 0 1280x1024x24' \
dbus-launch --exit-with-session \
pytest tests/ --junitxml=reports/atspi-junit.xml
- uses: actions/upload-artifact@v4
if: always()
with:
name: junit
path: reports/xvfb-run provides the X display, dbus-launch --exit-with-session spawns the session DBus bus (at-spi2-registryd requires the session bus per atspi2coreraw (opens in new window) - without it the registry daemon refuses to start). This matches the CI guidance in the desktop-test-strategy-reference anti-patterns table.
Anti-patterns
| Anti-pattern | Why it fails | Fix |
|---|---|---|
Running dogtail tests without toolkit-accessibility=true | GTK apps publish nothing; tree is empty | gsettings set org.gnome.desktop.interface toolkit-accessibility true (dogtailraw (opens in new window)) |
Driving Qt apps with QT_ACCESSIBILITY unset | QAccessible stays inactive; AT-SPI tree has no Qt children | Export QT_ACCESSIBILITY=1 in the launch env (Step 1) |
CI runner without dbus-launch | registryd refuses to start; sessions hang on attach | xvfb-run … dbus-launch … pytest … (Step 9) |
| Locating by visible label only | Localisation breaks the locator | Combine roleName= + accessible name; set Atk.set_accessible_name(...) from app code |
time.sleep(2) between actions | Flaky; brittle | Use dogtail's doDelay config (config.searchBackoffDuration) and tree polling helpers (dogtailraw (opens in new window)) |
Wayland session without gnome-ponytail-daemon | Synthetic input events get dropped | Install daemon per dogtailraw (opens in new window) |
| Mixing X test runner with Wayland app under test | Event injection mismatch | One session type per CI job; matrix-build over Xorg + Wayland separately |
| Test relies on Accerciser running concurrently | Two clients on the same registry race on tree refresh | Use Accerciser interactively for authoring; remove from CI runs |
Limitations
References
Related skills
appium-windows-driver
Authors and runs Appium 2.x tests against the Windows driver, the actively-maintained Node.js proxy in front of Microsoft's WinAppDriver: `appium driver install windows`, capabilities (`platformName: windows`, `appium:automationName: windows`, `appium:app`, `appium:appTopLevelWindow`, `appium:appArguments`), Windows gestures (`windows: scroll` / `clickAndDrag` / `keys`), PowerShell prerun/postrun hooks, and CI. Use when the stack already uses Appium for iOS / Android / Mac2 and Windows fits the existing client + capability model; to drive WinAppDriver directly from a Selenium-style client use winappdriver, and for a C#-only FlaUI client use flaui-tests.
desktop-test-strategy-reference
Pure-reference catalog of desktop GUI test strategies across Windows, macOS, and Linux. Defines the three accessibility-tree backends (Microsoft UI Automation on Windows, Apple Accessibility / XCTest on macOS, AT-SPI on Linux), the wrapper-tools that drive each backend (WinAppDriver, Appium-Windows, XCUIApplication, AT-SPI clients), the cross-toolkit Electron + Qt paths, and a per-OS decision matrix with accessibility-first locator strategy. Deep operational detail (per-OS asynchronous-wait hierarchies, parallel-test policy, foreground-lock / UAC / TCC / AT-SPI elevation hazards, and the high-DPI / per-monitor test matrix) lives in references/. Use when choosing how to test or automate a desktop GUI application (desktop app testing, GUI automation, automate desktop UI) on Windows, macOS, or Linux - the strategic reference before picking a desktop test stack, ahead of the per-tool implementation skills.
electron-playwright
Authors Playwright `_electron` tests for packaged Electron desktop apps - launches the app via `electron.launch({ args })`, returns an `ElectronApplication` handle, drives renderer windows as Playwright `Page` objects, and probes the main process via `electronApp.evaluate(({ app, BrowserWindow }) => …)`. Distinct from ordinary browser page automation: this wraps the `_electron` API for launching packaged Electron apps and probing main + renderer processes. Use for end-to-end tests of Electron apps where main-process state, IPC, and renderer DOM must all be asserted from one suite.
electron-spectron
Legacy reference for Spectron - Electron's original ChromeDriver-based testing framework, officially deprecated 2022-02-01 at v19.0.0. Documents what Spectron was, the architectural reason it became unmaintainable, the migration path to Playwright `_electron`, and the residual support contract for projects still on Spectron. Use only when auditing a legacy suite or planning a migration off Spectron - for new work use Playwright's `_electron` API.
flaui-tests
Authors and runs FlaUI-based Windows UI tests - the .NET-native wrapper around Microsoft UI Automation (UIA2 + UIA3). Covers the `FlaUI.Core` / `FlaUI.UIA2` / `FlaUI.UIA3` NuGet packages, `Application.Launch` / `Application.Attach` lifecycles, `ConditionFactory` + `FindFirstDescendant` locator patterns, `Retry` waits, and xUnit / NUnit / MSTest harness integration. Use when the test stack is C# / .NET-first and the team wants idiomatic in-process UIA calls rather than the HTTP/JSON wire protocol of `winappdriver` or the Appium proxy layer of `appium-windows-driver`.
qt-test-framework
Authors and runs Qt Test - the first-party C++ unit + GUI test framework that ships with Qt 6 (via the `QtTest` module header). Covers the `QTEST_MAIN` / `QTEST_APPLESS_MAIN` / `QTEST_GUILESS_MAIN` entry-point macros, the `QObject` private-slot test pattern, `QVERIFY` / `QCOMPARE` / `QFETCH` assertions, GUI event simulation (`QTest::mouseClick`, `QTest::keyClick`, `QTest::touchEvent`), `QSignalSpy` for signal introspection, `QBENCHMARK` for performance regression, and the `-o file,junitxml` CI output. Use for in-process testing of Qt widgets, QObject signal/slot chains, and Qt Quick / QML application logic; for out-of-process Qt-app driving, use an OS-native accessibility driver instead.
winappdriver
Authors and runs Windows UI tests against WinAppDriver, Microsoft's W3C-WebDriver service for UWP / WPF / WinForms / Win32 apps: installing + launching `WinAppDriver.exe` on `127.0.0.1:4723`, declaring `app` / `platformName` / `appArguments` / `appTopLevelWindow` capabilities, finding elements by `AccessibilityId` / `Name` / `ClassName`, and Windows-runner CI. Use when driving a native Windows app from a Selenium-style client (C#, Java, Python, Ruby, JS); for the actively-maintained Appium 2.x wrapper over the same server use appium-windows-driver, for a C#-only FlaUI client use flaui-tests, and to choose among Windows desktop drivers first use desktop-test-strategy-reference.
xctest-mac-desktop
Authors and runs XCTest UI + unit tests for macOS desktop apps - the Apple-first-party test framework that ships with Xcode. Covers the `XCTestCase` subclass + `test*` method-naming convention, `XCUIApplication` / `XCUIElement` / `XCUIElementQuery` for UI tests, accessibility-identifier-based locators (the stable replacement for label-based queries), `XCTAssert*` macros, `measureBlock:` for performance regressions, and `xcodebuild test` for CI execution. Use when the macOS app is built with Xcode and the test target is in-tree alongside the app - for cross-OS sharing see Appium Mac2 driver as a separate path.