Integration test: end-to-end dictation session flow
epic-speech-to-text-input-state-management-task-008 — Write an integration test that exercises the full dictation pipeline through TranscriptionStateManager using a fake NativeSpeechApiBridge. Test that starting dictation on two separate field IDs simultaneously yields independent states, that an interruption on one field persists its partial text while the other continues unaffected, and that recovery on re-open of the interrupted field restores the correct partial text. Verify that the WCAG live-region accessibility announcer (component 664) receives state-change notifications at each transition.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 6 - 158 tasks
Can start after Tier 5 completes
Implementation Notes
The key challenge is making the fake bridge controllable from the test body. Expose `pushTranscript(String fieldId, String text)` and `triggerError(String fieldId, Exception e)` methods on `FakeNativeSpeechApiBridge`. Inject via ProviderScope override: `transcriptionBridgeProvider.overrideWithValue(fakeBridge)`. For the live-region verification, the announcer should be a Riverpod provider itself (or an inherited widget), so it can be overridden with `FakeAccessibilityAnnouncer` in tests.
Use `pump()` calls between state mutations to allow the widget tree to rebuild and subscriptions to fire. Avoid `pumpAndSettle()` if animations are involved — pump a fixed number of frames instead to keep the test deterministic.
Testing Requirements
Use flutter_test's testWidgets() for this integration test so the full ProviderScope + widget tree is exercised. Create `FakeNativeSpeechApiBridge` that exposes a `StreamController
Assert announcement history after each pump cycle. Organize into a single test file `dictation_integration_test.dart` under `test/integration/`.
If a peer mentor rapidly switches between dictation-enabled fields while a session is still processing, the Riverpod family provider may share state or the SpeechRecognitionService may receive conflicting start/stop commands, causing orphaned recording sessions or state corruption.
Mitigation & Contingency
Mitigation: Design the state manager to enforce a single active dictation session globally via a shared active-field-key notifier. When a new field requests dictation while another session is active, automatically issue a stop command to the existing session and await completion before starting the new one. Test this concurrency scenario explicitly.
Contingency: If concurrency issues persist in integration testing, add a global dictation mutex at the SpeechRecognitionService level that serialises all start requests, with a short debounce to handle rapid field switching.
The recovery flow for interrupted dictation sessions (app restart with persisted partials) has ambiguous UX requirements. If recovery is automatic and the user has already typed different content into the field, the merge logic may overwrite or duplicate user input, causing data loss.
Mitigation & Contingency
Mitigation: Define the recovery behaviour explicitly: recovery should surface as an opt-in prompt ('You have unsaved dictation — insert or discard?') rather than an automatic merge. The state manager emits a dedicated recoverable-partial state that the UI layer renders as a non-blocking action sheet.
Contingency: If the recovery UI adds too much complexity for the initial release, scope recovery to display only the partial text in the preview field on re-open without auto-insertion, letting the user manually copy if needed, and defer the automatic recovery prompt to a subsequent iteration.