high priority high complexity testing pending testing specialist Tier 3

Acceptance Criteria

AccessibilityAuditRunner.runKeyboardNavigationAudit() simulates Tab key presses via WidgetTester.sendKeyEvent and verifies focus advances to the next interactive element on each key press
Every interactive element (AppButton, AppTextField, DropdownButton, ToggleSwitch, BottomNavItem) on the tested screens is reachable via Tab-only navigation without requiring a mouse/pointer event
Focus indicator is detectable for each focused element: the currently focused FocusNode has hasPrimaryFocus == true and its corresponding widget has a visible decoration (border, highlight, or outline) as defined by the design token system
No focus trap detected: after N Tab presses (where N equals the count of interactive elements), focus cycles back to the first element without getting stuck
FocusNavigationViolation records include: violationType (unreachable | focus_trap | missing_indicator), routeName, widgetKey, widgetType, and description
The audit covers the activity registration wizard (all steps), contact detail screen, and settings screen
Audit correctly identifies when a modal dialog captures focus and releases it when dismissed — no false positive focus trap reported for modal dialogs
The audit completes without crashing even if a focus trap is encountered — the runner detects the cycle and breaks out after a configurable max-tab threshold (default: 200 presses)

Technical Requirements

frameworks
Flutter
flutter_test
apis
WidgetTester.sendKeyEvent
LogicalKeyboardKey.tab
LogicalKeyboardKey.shiftTab
FocusManager.instance.primaryFocus
FocusNode.hasPrimaryFocus
FocusTraversalGroup
data models
FocusNavigationViolation
AccessibilityAuditResult
performance requirements
Keyboard navigation audit for all three key flows must complete within 45 seconds on CI hardware
Focus trap detection must terminate within 200 iterations and not hang the test runner
security requirements
Key injection must not trigger unintended side effects such as form submission or navigation outside the audited flow
Test must run in an isolated widget tree with no network calls
ui components
AppButton
AppTextField
BottomNavigationBar
ActivityWizard
SettingsScreen

Execution Context

Execution Tier
Tier 3

Tier 3 - 413 tasks

Can start after Tier 2 completes

Implementation Notes

Use WidgetTester.sendKeyEvent(LogicalKeyboardKey.tab) in a loop, capturing FocusManager.instance.primaryFocus after each press. Maintain a visited-node set using the FocusNode identity (or its hashCode) to detect cycles. For focus indicator detection, find the widget under the primary focus node using find.byKey or find.ancestor, then inspect its decoration. Be aware that Flutter's default focus highlight only shows on non-touch platforms — you may need to configure the test environment with TargetPlatform.macOS or use a custom focus theme.

BLoC and Riverpod providers must be injected before screen pump. This is critical for WCAG 2.2 AA compliance (SC 2.1.1 Keyboard, SC 2.4.7 Focus Visible) — a requirement stated across all three client organizations.

Testing Requirements

Unit tests: verify FocusNavigationViolation model; verify the focus-trap cycle detection algorithm with a mock FocusNode graph that has a known cycle. Integration tests: pump a screen with all interactive elements in flutter_test, simulate Tab presses, assert focus advances through all elements; pump a screen with a deliberate focus trap and assert a FocusNavigationViolation with violationType=focus_trap is produced. Also test Shift+Tab reverse traversal for completeness. Coverage target: 85% of keyboard audit logic.

Critical edge case: test that a disabled button (isEnabled=false) is correctly skipped in traversal.

Component
Accessibility Audit Runner
infrastructure medium
Epic Risks (2)
high impact medium prob dependency

Flutter's SemanticsController used in integration tests is an internal or semi-internal API that can break between Flutter stable releases. If the audit runner relies heavily on undocumented semantics tree traversal, a Flutter upgrade could silently disable the audit checks without a build failure, creating false confidence.

Mitigation & Contingency

Mitigation: Use only the public flutter_test accessibility APIs (meetsGuideline, SemanticsController.ensureSemantics) and wrap all SemanticsController calls in a versioned helper class with explicit assertions that the expected semantics tree shape is still available. Pin the Flutter SDK range in pubspec.yaml.

Contingency: If SemanticsController APIs break on a Flutter upgrade, fall back to widget-level golden tests that include the semantics tree snapshot, combined with manual Switch Access and VoiceOver QA checklists executed before each release.

low impact medium prob resource

Flutter integration tests that simulate Switch Access traversal on multiple screens can be slow (30–120 seconds per test flow), which may make the audit runner impractical to run on every CI commit if the test suite already has long run times.

Mitigation & Contingency

Mitigation: Scope the audit runner to a dedicated integration test target that runs on pull requests targeting main and on nightly builds, not on every push. Parallelise test shards in CI to keep wall-clock time acceptable. Profile audit run times during development and trim any flows that duplicate coverage.

Contingency: If CI run times exceed acceptable thresholds, split the audit runner into a fast smoke suite (touch targets and semantic labels only, runs on every PR) and a thorough traversal suite (Switch Access simulation, runs nightly), with the nightly failure blocking the release branch rather than every PR.