Testland
Browse all skills & agents

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-linux
View source

at-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

  • Linux GTK app under test (GNOME apps, GTK4 + GTK3 apps).
  • Qt application on Linux where in-process qt-test-framework doesn't fit (test must drive a separately-packaged binary).
  • Electron / Chromium app on Linux that needs out-of-process driving and Playwright _electron isn't acceptable (e.g., accessibility-conformance audit suites running against the shipped binary).
  • BDD-style acceptance tests via dogtail + behave (dogtailraw (opens in new window)).

Step 1 - Enable toolkit accessibility

Per the dogtail README (opens in new window):

gsettings set org.gnome.desktop.interface toolkit-accessibility true

Without 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.whl

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

This 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
accerciser

Accerciser 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 exit

This 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.xml

Step 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-patternWhy it failsFix
Running dogtail tests without toolkit-accessibility=trueGTK apps publish nothing; tree is emptygsettings set org.gnome.desktop.interface toolkit-accessibility true (dogtailraw (opens in new window))
Driving Qt apps with QT_ACCESSIBILITY unsetQAccessible stays inactive; AT-SPI tree has no Qt childrenExport QT_ACCESSIBILITY=1 in the launch env (Step 1)
CI runner without dbus-launchregistryd refuses to start; sessions hang on attachxvfb-run … dbus-launch … pytest … (Step 9)
Locating by visible label onlyLocalisation breaks the locatorCombine roleName= + accessible name; set Atk.set_accessible_name(...) from app code
time.sleep(2) between actionsFlaky; brittleUse dogtail's doDelay config (config.searchBackoffDuration) and tree polling helpers (dogtailraw (opens in new window))
Wayland session without gnome-ponytail-daemonSynthetic input events get droppedInstall daemon per dogtailraw (opens in new window)
Mixing X test runner with Wayland app under testEvent injection mismatchOne session type per CI job; matrix-build over Xorg + Wayland separately
Test relies on Accerciser running concurrentlyTwo clients on the same registry race on tree refreshUse Accerciser interactively for authoring; remove from CI runs

Limitations

  • Linux-only. AT-SPI runs only on Linux desktops; cross-OS parity lives in winappdriver (Windows) and xctest-mac-desktop (macOS).
  • Session-bus requirement. at-spi2-registryd requires a session DBus bus per atspi2coreraw (opens in new window); bare Docker containers (no session bus) need dbus-launch. GitHub-hosted Ubuntu runners ship with the bus but headless self-hosted runners don't necessarily.
  • GTK4 shadow rendering quirks require disabling window shadows in ~/.config/gtk-4.0/gtk.css per dogtailraw (opens in new window); otherwise click-coordinates land on the shadow region rather than the widget.
  • No first-party Wayland event-injection guarantee. Recent Wayland compositors (mutter, kwin) restrict synthetic input; gnome-ponytail-daemon (dogtailraw (opens in new window)) is the documented workaround on GNOME Wayland sessions.
  • Apps must publish accessibility. Custom-drawn widgets that bypass GTK/Qt/Chromium accessibility (e.g., raw OpenGL canvases, custom-painted Cairo surfaces) are opaque to AT-SPI - same caveat as desktop-test-strategy-reference.
  • dogtail API docs incomplete. Per dogtailraw (opens in new window), "API docs (incomplete for both versions, in progress for 2.x)" - some method signatures need source-spelunking.

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.