Skip to content

Find the right selector

Accessibility names are frequently something other than the label painted on screen, so a guessed selector is a coin flip. Read the tree first, every time.

For the syntax itself, see Selectors. For the CLI commands used here, see CLI.

The CLI dumps a whole application in one command and behaves the same whatever language you are writing in, so start there.

Terminal window
# 1. What's running, and under what name?
xa11y apps
# 2. See the full tree
xa11y tree --app MyApp
# 3. Try a selector against the live app
xa11y find "button[name='Submit']" --app MyApp
# 4. Narrow it until exactly one element matches
xa11y find "dialog button[name='Submit']" --app MyApp

xa11y find prints every match and a parenthesised count, so a selector that matched seven things when you wanted one is visible before it reaches your code. A selector matching nothing is an error (no elements matched selector: …) with a non-zero exit, rather than an empty list.

dump() and tree() are available on App, Locator, and Element in every binding. dump() returns the indented string, and tree() returns the same data as a structured snapshot.

import xa11y
calc = xa11y.App.by_name("Calculator")
print(calc.dump()) # full indented tree, rooted at the app
print(calc.dump(max_depth=2)) # limit depth
snapshot = calc.tree() # same data as a nested dict
# Locators and elements also support dump() / tree()
print(calc.locator("group[name='Keypad']").dump())

max_depth of 0 returns the root node alone, 1 adds its direct children, and so on. Omit it for the whole subtree.

Start from the error. A timeout or a selector miss already names what the locator was resolving, what it last observed, and which elements almost matched, so read the candidates list before reaching for the CLI. See Errors and diagnosis.

When you need to look at the live application:

Terminal window
# What does the tree actually look like right now?
xa11y tree --app MyApp
# Is the element there under a different name?
xa11y find "button" --app MyApp
# Watch events while you interact, to see what the app reports changing
xa11y events --app MyApp

Three failures account for most of them:

  • The name differs from the on-screen label. Copy it from the tree output character for character, or switch to a substring operator: button[name^='Save'].
  • The element is there but not actionable. The diagnosis says matched button "Export" (visible=false, enabled=true, focused=false) rather than selector never matched. The selector is right and the wait condition is what is failing.
  • The element belongs to a different accessibility application. Some toolkits register dialogs as their own application sharing the host process. xa11y apps shows them as separate rows. See Attaching to the app under test.
Terminal window
# What apps are running?
xa11y apps
# What's the structure?
xa11y tree --app Slack
# What can I interact with?
xa11y find "[focusable='true']" --app Slack

If the tree comes back holding only an application and a window, the application is not publishing its contents rather than being empty. On Linux that is the Electron and Chromium default. See Platform Details.