# Architecture & Design Deep dive into the design and implementation of aria-testing. ## Overview aria-testing is built around three core systems: 1. **Query System** - Factory-based query generation with four variants per query type 2. **Role Mapping** - ARIA role to HTML element mapping with accessible name computation 3. **Traversal Engine** - Iterative DOM tree traversal ## Query System Design ### Query Factory Pattern All queries follow a consistent pattern using PEP 695 generic functions: ```python def make_query_functions[T]( *, find_elements: Callable[[Container], list[Element]], ) -> QueryFunctions[T]: """Factory to create all four query variants.""" def get_by(...) -> Element: elements = find_elements(container) if len(elements) == 0: raise ElementNotFoundError(...) if len(elements) > 1: raise MultipleElementsError(...) return elements[0] def query_by(...) -> Element | None: elements = find_elements(container) if len(elements) == 0: return None if len(elements) > 1: raise MultipleElementsError(...) return elements[0] def get_all_by(...) -> list[Element]: elements = find_elements(container) if len(elements) == 0: raise ElementNotFoundError(...) return elements def query_all_by(...) -> list[Element]: return find_elements(container) return QueryFunctions(get_by, query_by, get_all_by, query_all_by) ``` ### Benefits of Factory Pattern - **Single source of truth** - Logic defined once, four variants generated - **Type safety** - Full type hints with generic functions - **Consistency** - All queries behave the same way - **Maintainability** - Changes to error handling apply to all queries ### Query Implementation Example ```python # Define the search logic def find_by_role_impl( container: Container, role: str, *, name: str | Pattern | None = None, level: int | None = None, ) -> list[Element]: elements = get_all_elements(container) return [ elem for elem in elements if matches_role(elem, role, name, level) ] # Generate all four variants role_queries = make_query_functions(find_elements=find_by_role_impl) # Export with consistent naming get_by_role = role_queries.get_by query_by_role = role_queries.query_by get_all_by_role = role_queries.get_all query_all_by_role = role_queries.query_all ``` ## Role Mapping System ### Role Computation The role system maps HTML elements to ARIA roles: ```python def compute_role(element: Element) -> str | None: """Compute the implicit or explicit ARIA role of an element.""" # Explicit role (aria-role attribute) if explicit_role := element.attrs.get("role"): return explicit_role # Implicit roles based on tag and attributes tag = element.tag.lower() match tag: case "button" | "a" if "href" in element.attrs: return tag_to_role[tag] case "h1" | "h2" | "h3" | "h4" | "h5" | "h6": return "heading" case "input": return input_type_to_role.get( element.attrs.get("type", "text"), "textbox" ) case _: return tag_to_role.get(tag) ``` ### Role Mapping Tables Efficient role lookups using string interning: ```python # Landmark roles TAG_TO_ROLE = { "nav": "navigation", "main": "main", "aside": "complementary", "header": "banner", # context-dependent "footer": "contentinfo", # context-dependent } # Input type roles INPUT_TYPE_TO_ROLE = { "button": "button", "checkbox": "checkbox", "radio": "radio", "text": "textbox", "email": "textbox", "search": "searchbox", } ``` ### Accessible Name Computation Accessible names come from multiple sources, in priority order: 1. `aria-label` attribute 2. `aria-labelledby` reference 3. Associated `