Query Reference¶
This page provides comprehensive documentation for all query functions in aria-testing.
Query Variants¶
Each query comes in four variants for different use cases:
Variant |
Returns |
Not Found |
Multiple Found |
|---|---|---|---|
|
Single element |
❌ Raises error |
❌ Raises error |
|
Element or None |
✅ Returns None |
❌ Raises error |
|
List of elements |
❌ Raises error |
✅ Returns all |
|
List (possibly empty) |
✅ Returns |
✅ Returns all |
When to Use Each¶
get_by_*: When element MUST exist and be unique (most common in tests)query_by_*: When checking if element exists (conditional logic)get_all_by_*: When multiple elements MUST existquery_all_by_*: When finding zero or more elements (exploratory queries)
Query by Role ⭐¶
Most Recommended - Find elements by their ARIA role, matching how screen readers interact with your app.
from aria_testing import get_by_role, query_by_role, get_all_by_role, query_all_by_role
# Find by role
button = get_by_role(document, "button")
heading = get_by_role(document, "heading", level=1)
# Find by role with accessible name
link = get_by_role(document, "link", name="Home")
# Pattern matching with regex
import re
link = get_by_role(document, "link", name=re.compile(r"Home|About"))
Supported Roles¶
Landmark Roles¶
Define page structure and navigation:
banner-<header>(when not in article/section)complementary-<aside>contentinfo-<footer>(when not in article/section)form-<form>(when has accessible name)main-<main>navigation-<nav>region-<section>(when has accessible name)search- Search landmark
Document Structure Roles¶
Organize content:
article-<article>list-<ul>,<ol>listitem-<li>heading-<h1>through<h6>table-<table>row-<tr>cell-<td>columnheader-<th scope="col">rowheader-<th scope="row">
Widget Roles¶
Interactive elements:
button-<button>,<input type="button|submit|reset">checkbox-<input type="checkbox">link-<a href="...">textbox-<input type="text">,<textarea>radio-<input type="radio">searchbox-<input type="search">combobox-<select>
Finding by Name¶
The name parameter matches the element’s accessible name:
# Link text becomes accessible name
link = get_by_role(document, "link", name="About")
# aria-label provides accessible name
button = get_by_role(document, "button", name="Close dialog")
# Pattern matching
link = get_by_role(document, "link", name=re.compile(r"Home|About"))
Finding by Level¶
Headings support a level parameter:
# Find specific heading level
h1 = get_by_role(document, "heading", level=1)
h2 = get_by_role(document, "heading", level=2)
# Combine with name
title = get_by_role(document, "heading", level=1, name="Welcome")
Query by Label Text ⭐¶
Highly Recommended - Find form elements by their associated label, matching how users identify form fields.
from aria_testing import get_by_label_text, query_by_label_text
result = html(t"""
<form>
<label for="username">Username:</label>
<input id="username" type="text" />
<label>
Email:
<input type="email" />
</label>
</form>
""")
# Find by associated label
username = get_by_label_text(result, "Username:")
email = get_by_label_text(result, "Email:")
# Pattern matching
username = get_by_label_text(result, re.compile(r"user", re.IGNORECASE))
# Case-insensitive search
email = get_by_label_text(result, "email", exact=False)
Query by Text¶
Find elements by their text content:
from aria_testing import get_by_text, query_by_text
# Exact match (default)
element = get_by_text(document, "Welcome to our site")
# Pattern matching with regex
import re
element = get_by_text(document, re.compile(r"Welcome.*"))
# Case-insensitive substring match
element = get_by_text(document, "welcome", exact=False)
Query by Tag Name¶
Find elements by HTML tag name with optional attribute filtering. Useful for non-semantic elements like <link>, <meta>, <script>:
from aria_testing import get_by_tag_name, get_all_by_tag_name
# Find by tag name only
title = get_by_tag_name(document, "title")
# Find with exact attribute matching
favicon = get_by_tag_name(document, "link", attrs={"rel": "icon"})
# Find all matching elements
stylesheets = get_all_by_tag_name(document, "link", attrs={"rel": "stylesheet"})
# Multiple attributes
viewport = get_by_tag_name(document, "meta", attrs={
"name": "viewport",
"content": "width=device-width, initial-scale=1"
})
Special Attribute: in_class¶
Use in_class for substring matching within the class attribute:
result = html(t"""
<div>
<header class="pico-layout is-fixed-above-lg header-main">Header</header>
<div class="is-fixed-above-lg sidebar">Sidebar</div>
</div>
""")
# Find by class substring
fixed_header = get_by_tag_name(result, "header", attrs={"in_class": "is-fixed-above-lg"})
# Combine with other attributes
pico_header = get_by_tag_name(
result,
"header",
attrs={"in_class": "pico-layout"}
)
The in_class attribute is useful for:
Multi-class elements with space-separated values
Framework naming conventions (e.g.,
btn-primary,is-active)Partial matching rather than exact token equality
Query by Test ID¶
An escape hatch for when semantic queries aren’t possible:
from aria_testing import get_by_test_id, query_by_test_id
result = html(t"""
<div data-testid="custom-element">
<span>Content</span>
</div>
""")
element = get_by_test_id(result, "custom-element")
# Query variant returns None if not found
maybe_element = query_by_test_id(result, "missing")
Note
Use test IDs sparingly. Prefer queries by role, label, or text that match how users interact with your app.
Query by ID¶
Find elements by their HTML id attribute:
from aria_testing import get_by_id, query_by_id
result = html(t"""
<div>
<h1 id="title">Welcome</h1>
<button id="save">Save</button>
</div>
""")
# Get throws if not found
save_button = get_by_id(result, "save")
# Query returns None if not found
maybe_title = query_by_id(result, "missing")
assert maybe_title is None
Note
Only get_by_id and query_by_id variants exist (no get_all_* or query_all_*) since IDs should be unique.
Query by Class¶
Find elements by a CSS class token (exact token matching in space-separated class attribute):
from aria_testing import (
get_by_class,
query_by_class,
get_all_by_class,
query_all_by_class,
)
result = html(t"""
<div>
<h1 class="title hero">Welcome</h1>
<button class="btn primary">Save</button>
<div class="button other"></div>
<p class="btn muted">Another</p>
</div>
""")
# Single element queries (raises if multiple)
save_button = get_by_class(result, "primary")
maybe_title = query_by_class(result, "missing") # -> None
# Multiple element queries
all_btns = query_all_by_class(result, "btn") # [<button ...>, <p ...>]
btns_required = get_all_by_class(result, "btn") # raises if none found
# Token matching: 'btn' does not match 'button'
button_div = query_by_class(result, "button")
assert button_div.attrs["class"] == "button other"
Warning
Class queries use exact token matching, not substring matching. "btn" will not match "button".
For substring matching, use get_by_tag_name() with the in_class attribute.
Error Handling¶
ElementNotFoundError¶
Thrown when get_by_* or get_all_by_* finds no matching elements:
from aria_testing import ElementNotFoundError
try:
element = get_by_role(container, "button", name="Missing")
except ElementNotFoundError as e:
print(f"Could not find element: {e}")
MultipleElementsError¶
Thrown when get_by_* or query_by_* finds multiple matching elements:
from aria_testing import MultipleElementsError
try:
element = get_by_role(container, "button") # Multiple buttons exist
except MultipleElementsError as e:
print(f"Found multiple elements: {e}")
Utility Functions¶
get_text_content¶
Extract all text content from an element and its descendants:
from aria_testing import get_text_content
element = html(t"""
<div>
<h1>Title</h1>
<p>Paragraph text</p>
</div>
""")
text = get_text_content(element) # "Title Paragraph text"
normalize_text¶
Normalize whitespace in text (collapse multiple spaces, strip leading/trailing):
from aria_testing import normalize_text
text = normalize_text(" Hello World ") # "Hello World"