high priority medium complexity testing pending testing specialist Tier 2

Acceptance Criteria

Test file compiles and all tests pass with flutter test
Test: a widget tree containing an unlabelled IconButton produces a violation report entry with violationType == 'missing_semantic_label'
Test: a widget tree containing a widget with no Semantics role produces a violation entry with violationType == 'missing_role'
Test: a widget tree containing a tap target smaller than 44×44dp produces a violation entry with violationType == 'insufficient_touch_target'
Test: a widget tree with no violations produces an audit report with violations == []
Test: audit report contains the correct widgetType, location (key or debug label), and severity fields for each violation
Test: multiple simultaneous violations in one tree all appear in the report (report is exhaustive, not short-circuiting)
Test: AccessibilityAuditService.runAudit() returns a typed AuditReport object (not raw Map or dynamic)
Test: AuditReport.summary includes a totalViolations count matching violations.length
Test: audit correctly ignores widgets explicitly excluded via ExcludeSemantics or Semantics(excludeSemantics: true)
All tests run without network calls, Supabase connections, or BankID dependencies
Branch coverage for AccessibilityAuditService reaches ≥90%

Technical Requirements

frameworks
Flutter
flutter_test
apis
AccessibilityTestHarness
AccessibilityAuditService.runAudit()
SemanticsNode
tester.ensureSemantics()
data models
AuditReport
AuditViolation
ViolationType (enum)
ViolationSeverity (enum)
performance requirements
Each test must complete within 3 seconds
Audit must not trigger real screen reader engines — use test semantics binding
ui components
IconButton (unlabelled)
GestureDetector (small target)
custom Semantics widgets

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

AccessibilityAuditService likely traverses the semantics tree using SemanticsNode.visitChildren(). In tests, the semantics tree is only populated after tester.pumpAndSettle() + tester.ensureSemantics(). Create a reusable helper buildViolatingTree(type: ViolationType) that returns the minimal widget for each violation type — this makes tests readable and reduces duplication. For the touch target size test, be aware that Flutter's default minimum tap target enforcement (GestureBinding.instance.resamplingEnabled, or MaterialTapTargetSize) may automatically upscale some targets — use a raw GestureDetector inside a SizedBox to bypass this.

For the 'missing role' test, verify what AccessibilityAuditService considers a 'role' — it may be a Semantics(role: ...) property or a combination of flags. Align the test expectation with the actual service implementation rather than assumptions.

Testing Requirements

Pure flutter_test suite using testWidgets. Call tester.ensureSemantics() at the start of each test to activate the semantics tree. Build the minimum widget tree that reproduces the violation being tested — e.g., for the touch target test, use a SizedBox(width: 20, height: 20, child: GestureDetector(onTap: () {})). For the clean tree test, use a fully labelled, role-assigned, adequately sized widget.

Assert on typed fields of AuditViolation, not string matches. Group tests as: 'violation detection', 'clean tree', 'report structure', 'edge cases (ExcludeSemantics)'. Document the minimum touch target size constant (44dp) used in assertions as a shared const to keep tests in sync with the service implementation.

Component
Accessibility Audit Service
service high
Epic Risks (3)
high impact high prob technical

Flutter's build pipeline and SemanticsService.announce() operate asynchronously. Announcements triggered too early (before the semantic tree settles) may be swallowed silently on both platforms, causing acceptance criteria around the 500ms window to fail intermittently in CI and on device, which would block pilot launch.

Mitigation & Contingency

Mitigation: Implement the LiveRegionAnnouncer with a post-frame callback delay and an internal timing guard. Write integration tests using WidgetTester.pump() sequences that verify announcement delivery across multiple frame boundaries. Validate on physical devices at each sprint boundary, not only in CI.

Contingency: If consistent announcement timing cannot be achieved within Flutter's semantic pipeline, switch to a platform channel approach that calls native UIAccessibility.post (iOS) and AccessibilityManager.announce (Android) directly, bypassing Flutter's intermediary.

high impact medium prob technical

Flutter does not natively emit a focus-gain event to Dart code when VoiceOver or TalkBack moves focus to a specific widget. If the intercept mechanism for the SensitiveFieldWarningDialog relies on an unsupported or undocumented hook in the semantics tree, it may miss focus events for some field types or in some navigation contexts, leaving sensitive data unprotected.

Mitigation & Contingency

Mitigation: Prototype the focus-intercept mechanism during the first sprint of this epic, before building the dialog UI. Evaluate Flutter's SemanticsBinding.instance callbacks and custom SemanticsActions as intercept points. Document the chosen mechanism with platform compatibility notes.

Contingency: If no reliable focus-intercept is available, implement an alternative where sensitive fields show a static 'Screen reader active — tap to reveal' overlay instead of an OS dialog, which is less seamless but achieves equivalent privacy protection without relying on an unreliable event hook.

medium impact low prob dependency

The AccessibilityTestHarness depends on internal flutter_test semantic tree APIs that can change between Flutter minor versions. If the project upgrades Flutter during this epic, the harness may break silently, causing CI accessibility tests to pass while actually skipping assertions.

Mitigation & Contingency

Mitigation: Pin the Flutter SDK version in pubspec.yaml for the duration of this epic. Document which flutter_test APIs are used and their stability tier. Add a canary test that explicitly fails if the semantic tree API surface changes.

Contingency: If a forced Flutter upgrade breaks the harness, prioritise patching the harness as a blocking task before any other epic work continues, using the canary test failure as the trigger.