Skip to main content

Making your UI testable

For frontend devs. If an automated test can't reliably find your element, the fix is almost always a small, additive change in the FE — not a brittle selector in the test. This page is the contract: expose semantics, not styling.

In the frontend repo there is a Claude skill for a11y auditing, use this to find the gaps in your frontend code or for an app repository.

The rule: target meaning, not appearance

Tests should locate elements by what they are, not how they look. Hashed CSS-module classes and FontAwesome icon classes change without warning and break tests silently. A stable role, label, or data-testid does not.

Locator preference order (tests follow this; make the top options possible):

  1. Role + accessible name — get_by_role("button", name="Save")
  2. Label / text — get_by_label(...), get_by_text(...)
  3. data-testid — when there's no good semantic handle
  4. CSS / XPath — last resort, brittle

What to add (with real examples)

  • aria-label on repeated/ambiguous elements so the accessible name is unique. #801 — tree items got aria-label + menuitem-<id> testids so get_by_role("treeitem", name="Files") resolves to exactly one row.
  • Expose state as a data- attribute, not a colour class. #1023 — added data-status="RUNNING|COMPLETED|…" instead of leaving status in a hashed CSS-module class.
  • Use the right ARIA state for the right concept. #866aria-current for the active workspace, instead of tests reverse-engineering a computed box-shadow.
  • Give text a real role when it acts like one. #832 — a plain <span> became role="heading"; without it, get_by_role("heading", …) timed out for 50s even though the UI had rendered fine.
  • Apply a naming convention across a whole surface. #917 / #924composer-* testids + ARIA across every Composer surface.

Why it's worth it

These are small, additive, non-visual changes. They make the product more accessible and the tests durable — the same investment buys both. A missing role isn't just an a11y gap; #832 shows it's a real test failure.