End-to-end widget and integration tests for export UI flow
epic-bufdir-report-export-user-interface-task-012 — Write flutter_test widget tests and integration tests covering the full user journey from opening BufdirExportScreen to holding a downloaded file. Test cases must include: scope + period selection triggers preview fetch; preview panel renders category rows correctly; warning section appears for unmapped types; confirmation dialog blocks export when there are blocking errors; progress indicator displays during export; success state triggers FileDownloadHandler; back-navigation correctly resets phase state; WCAG live-region announcements fire at each phase transition. Use the accessibility-test-harness for all screen reader assertions.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 5 - 253 tasks
Can start after Tier 4 completes
Implementation Notes
Use fakeAsync and pump(Duration) instead of pumpAndSettle() everywhere streams or timers are involved — pumpAndSettle times out when streams never complete. For BLoC-dependent widget tests, provide a pre-seeded MockCubit using the whenListen pattern from bloc_test so you can control exact state sequences. For Riverpod overrides, use ProviderScope with overrides: [] in each test's pumpWidget call. The accessibility test harness should call tester.ensureSemantics() at the start of each accessibility test and dispose the SemanticsHandle at the end.
For the live-region announcement test, listen to SemanticsController.onSemanticsUpdate and assert that an announcement containing the expected readiness text was fired after the phase transition pump.
Testing Requirements
Use flutter_test with WidgetTester for all widget-level tests. Use bloc_test's emitsInOrder for cubit state transition assertions. Use mocktail (preferred) or mockito for service and handler mocks. Organise tests in a single test file per major flow: export_screen_configuration_test.dart, export_screen_preview_test.dart, export_screen_execution_test.dart, export_screen_accessibility_test.dart.
Integration test targeting a physical iOS device via TestFlight should be in integration_test/bufdir_export_flow_test.dart and gated behind a CI environment variable so it does not run on every PR.
The preview panel requires a round-trip to the edge function to compute aggregated counts. If this call takes 5–15 seconds for large scopes, users may assume the app has frozen and navigate away, potentially triggering duplicate preview requests or leaving the export in an undefined state.
Mitigation & Contingency
Mitigation: Show an immediate skeleton loading state on the preview panel as soon as the period and scope are confirmed, with named stage labels (e.g. 'Querying activities…', 'Computing totals…') streamed from the edge function's progress events. Set a clear user-visible timeout with a retry option. Pre-warm the edge function with a lightweight ping when the scope selector is opened.
Contingency: If preview latency consistently exceeds 10 seconds for large scopes, cache the preview payload in local BLoC state and allow the coordinator to proceed to confirmation without re-fetching if scope and period have not changed since the last preview.
The export preview panel contains dynamic content (warning badges, loading skeletons, aggregated counts) that must be announced correctly by VoiceOver and TalkBack. Incorrect semantics annotations could make the preview unreadable for blind coordinators, violating the project's WCAG 2.2 AA accessibility mandate.
Mitigation & Contingency
Mitigation: Implement semantic annotations using Flutter's Semantics widget with explicit labels for all dynamic content. Use live region announcements for loading state transitions. Schedule a dedicated accessibility review session with a screen reader user (Blindeforbundet has relevant expertise) before marking the epic complete.
Contingency: If the preview panel cannot be made fully screen-reader-accessible in time for launch, ship a simplified text-only summary mode activated by a toggle at the top of the preview panel, which renders all data as a single readable paragraph with no interactive badges.