high priority medium complexity testing pending testing specialist Tier 2

Acceptance Criteria

Unit tests cover ScreenReaderDetectionService.initialize() returning correct initial state when VoiceOver is active on iOS
Unit tests cover ScreenReaderDetectionService.initialize() returning correct initial state when TalkBack is active on Android
Unit tests verify that ScreenReaderDetectionService.initialize() returns false/disabled when no screen reader is active on either platform
Stream emission tests confirm that a state change event is emitted each time AccessibilityFeatures changes
Stream emission tests confirm no duplicate emissions when AccessibilityFeatures changes but screen reader state is unchanged
Foreground re-evaluation tests verify that didChangeAccessibilityFeatures callback triggers re-detection
Platform differentiation tests confirm accessibleNavigation flag is used for TalkBack (Android) and the correct equivalent flag for VoiceOver (iOS)
Tests mock WidgetsBinding.instance.accessibilityFeatures to simulate both active and inactive states
All tests pass with flutter_test without requiring a real device or emulator
Code coverage for ScreenReaderDetectionService is at or above 90%

Technical Requirements

frameworks
Flutter
flutter_test
Riverpod
apis
WidgetsBinding.instance.accessibilityFeatures
AccessibilityFeatures
WidgetsBindingObserver.didChangeAccessibilityFeatures
data models
ScreenReaderState
AccessibilitySettings
performance requirements
All unit tests complete within 500ms total
No real async delays; use fake timers or StreamController for stream tests
security requirements
No real user accessibility data used in tests; all mocked

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Use mockito or mocktail to create a mock WidgetsBinding. Because AccessibilityFeatures is a final class from Flutter internals, prefer creating a FakeWidgetsBinding that extends TestWidgetsFlutterBinding or wraps WidgetsBinding with a thin interface for testability. Avoid relying on platform channels in unit tests. For stream tests, use expectLater with emitsInOrder to assert emission sequence.

When testing foreground re-evaluation, call the WidgetsBindingObserver callback directly on the service instance rather than triggering a real lifecycle event. Ensure tests are grouped clearly: 'initial detection', 'stream emissions', 'foreground re-evaluation', 'platform differentiation'.

Testing Requirements

Unit tests using flutter_test with mocked AccessibilityFeatures. Use MockWidgetsBinding or a test double that exposes a settable accessibilityFeatures property to simulate VoiceOver (iOS: accessibilityFeatures.accessibleNavigation or platform-specific flag) and TalkBack (Android: accessibilityFeatures.accessibleNavigation). Create a StreamController to simulate state changes and verify correct stream emissions. Write integration-style tests within flutter_test that mount a ProviderContainer, inject the mock binding, and confirm the Riverpod-exposed stream reflects state changes.

Aim for 90%+ line coverage. Test file should be located in test/services/screen_reader_detection_service_test.dart.

Component
Screen Reader Detection Service
service medium
Epic Risks (2)
high impact medium prob technical

Flutter's SemanticsService behaves differently between iOS (VoiceOver) and Android (TalkBack) in edge cases — e.g., announcement queuing, focus-gain timing, and attribute support. If the facade does not correctly abstract these differences, announcements may be silent or misfired on one platform, causing regression on the other platform to go unnoticed until device testing.

Mitigation & Contingency

Mitigation: Write platform-divergence unit tests early using SemanticsServiceFacade mocks. Validate announcement delivery on a physical iPhone (VoiceOver) and Android device (TalkBack) at the end of each sprint. Document known platform differences in the facade's inline API comments.

Contingency: If a platform difference cannot be abstracted cleanly, expose a platform-specific override path in the facade and implement targeted workarounds per platform, accepting the added complexity in exchange for correct behaviour.

medium impact medium prob scope

Accessibility preferences stored in local storage may need new fields as higher-tier epics are implemented (e.g., announcement verbosity, sensitive-field guard toggle). Schema changes to an already-persisted store risk data migration failures or silent defaults on existing installs, breaking user preferences.

Mitigation & Contingency

Mitigation: Design the AccessibilitySettingsRepository with a versioned JSON schema from the start, using merge-with-defaults on read so new fields fall back gracefully. Define the full expected field list upfront based on all downstream epic requirements before writing the first record.

Contingency: If migration fails on a live install, fall back to full reset-to-defaults with a one-time in-app notification informing the user that accessibility preferences have been reset and inviting them to reconfigure.