Integration test: end-to-end screen reader session simulation
epic-screen-reader-support-core-services-task-016 — Use AccessibilityTestHarness screen reader simulation to write integration tests covering the two Blindeforbundet pilot scenarios: (1) activity registration flow announces loading, success, and error states via LiveRegionAnnouncer in correct order; (2) sensitive contact field triggers SensitiveFieldWarningDialog and focus is correctly managed throughout. Run these tests as part of the flutter_test integration suite.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 5 - 253 tasks
Can start after Tier 4 completes
Implementation Notes
The two pilot scenarios map directly to the Blindeforbundet requirements in the source documentation: screen reader support is listed as particularly critical for Blindeforbundet and the activity registration flow is the #1 priority across all organisations. Structure the integration test file so each scenario can run independently (no shared mutable state between tests). For scenario 1, create a mock Supabase activity repository that returns a delayed Future (200ms) then a success result; repeat with an error result for the error path branch. For scenario 2, use a real widget tree fragment (ContactDetailScreen or equivalent) rather than a stub, to catch real focus management edge cases.
Document the harness interaction model with a short comment block at the top of the file explaining how AccessibilityTestHarness wraps the standard widget tree and intercepts semantics calls.
Testing Requirements
Integration tests under integration_test/ using flutter_test's IntegrationTestWidgetsFlutterBinding. Mock Supabase at the repository layer using mocktail so scenario 1 can simulate loading, success, and error responses deterministically. Use AccessibilityTestHarness to capture the announcement log and focus trace. Assert announcement content, order, and count using typed matchers (avoid fragile substring matching).
Each scenario should be a separate test() with a descriptive name that matches the Blindeforbundet pilot use case. Run the suite in CI on both iOS simulator and Android emulator targets. Target zero flaky tests — if timing is involved, use FakeAsync or explicit pump sequences.
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.
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.
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.