Widget and integration tests for accounting export UI
epic-accounting-system-export-ui-task-010 — Write flutter_test widget tests for ExportDateRangePicker (preset selection, invalid range validation), ExportConfirmationDialog (normal and duplicate-warning states, skipped claims display), and ExportHistoryPanel (populated list, empty state, download tap). Write BLoC unit tests covering all state transitions including DuplicateWarning flow. Write an integration test that drives the full coordinator happy path: select date range → review confirmation dialog → confirm export → receive success → tap download. Use mocktail to stub the edge function client and repository.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 6 - 158 tasks
Can start after Tier 5 completes
Implementation Notes
Use dependency injection consistently across all components so stubs can be passed in tests — avoid ServiceLocator/GetIt calls inside widget build methods. For BLoC tests, use the bloc_test package's blocTest function rather than manually adding events and asserting state streams — it produces clearer failure messages. For the integration test, create a test entry point (integration_test/app_test.dart) that calls IntegrationTestWidgetsFlutterBinding.ensureInitialized() and overrides the Supabase client with a mocktail fake before calling runApp. Stub FileDownloadHandler as well to prevent url_launcher from attempting real platform calls during tests — assert that download() was called with the correct URL using verify().
When testing DuplicateWarning flow in the widget test, pump the dialog with the BLoC already in DuplicateWarning state using BlocProvider.value — do not re-drive the entire event chain. Use Key widgets on critical interactive elements (ConfirmButton, DownloadSnackBarAction, HistoryRowItem) to make finders stable and test-readable.
Testing Requirements
Three test layers required: (1) Widget tests using WidgetTester — pump each component in isolation, interact via tester.tap/tester.enterText, assert rendered output with find.text/find.byType/find.byKey. (2) BLoC unit tests using bloc_test's blocTest helper — provide stubbed repository/edge function client via constructor injection, assert exact state sequences with emitsInOrder. (3) Integration test using integration_test package — register mocked dependencies in main_test.dart before runApp, drive full user flow with finder interactions, assert final SnackBar text visible. Use setUp/tearDown for stub registration.
Aim for ≥90% line coverage across all four components combined.
Export operations may take several seconds, and the UI must handle all intermediate states (loading, partial success, failure, duplicate warning) without leaving the coordinator on a blank or unresponsive screen. Missing state handling causes confusion and potentially double-submissions.
Mitigation & Contingency
Mitigation: Design the BLoC state machine with explicit states for each transition before writing any widget code: ExportIdle, ExportDuplicateWarning, ExportInProgress, ExportSuccess, ExportPartialSuccess, ExportFailed. Each state maps to a distinct UI. Widget tests cover all states.
Contingency: If a loading state is missed in production, surface a generic error state with a retry action rather than leaving the UI stuck. Add a timeout on the Edge Function call (default 30 seconds) that transitions to ExportFailed with a user-readable message.
The custom Export Date Range Picker may not be fully navigable with VoiceOver if the underlying Flutter date widgets do not expose the correct semantic tree. This is a critical accessibility failure for Blindeforbundet users who rely on screen readers.
Mitigation & Contingency
Mitigation: Use Flutter's built-in DateRangePicker as the base and wrap with explicit Semantics nodes for start and end labels. Test with VoiceOver on a physical iOS device as part of the definition of done for this component. Reference the existing AccessibilityTestHarness pattern used elsewhere in the app.
Contingency: If the custom picker fails accessibility audit, replace it with two independent DatePicker fields (start and end) using Flutter's standard accessible date input, which has broader VoiceOver support than range variants.