high priority medium complexity testing pending testing specialist Tier 6

Acceptance Criteria

ExportDateRangePicker widget tests: selecting each preset chip updates the displayed date range; selecting a custom range where end < start shows an inline validation error and disables the confirm button; selecting a valid custom range enables confirm
ExportConfirmationDialog widget tests: normal state renders claim count, date range, and enabled Confirm button; duplicate-warning state renders warning banner listing conflicting export run IDs; skipped claims section lists each skipped claim ID with reason text
ExportHistoryPanel widget tests: populated list renders correct number of rows with date and status; empty-state widget is shown when list is empty; tapping a row dispatches the expected BLoC event and calls FileDownloadHandler.download with the resolved signed URL
BLoC unit tests achieve 100% state-transition coverage: Idle → Loading → ExportSuccess; Idle → Loading → DuplicateWarning → (Proceed) → Loading → ExportSuccess; Idle → Loading → ExportFailure; DuplicateWarning → (Cancel) → Idle
BLoC unit tests verify that DuplicateWarning state contains the list of conflicting run IDs returned by the stubbed repository
Integration test executes the full coordinator happy path end-to-end using flutter_test integration_test package: app launches, coordinator navigates to accounting export, selects a preset date range, reviews confirmation dialog (no duplicates), confirms, receives ExportSuccess SnackBar, taps Download — all without real network calls (mocktail stubs injected via dependency injection)
All widget and BLoC tests pass in CI without requiring a device or network connection
Test file naming follows existing project conventions (e.g., *_test.dart alongside source files or under test/ mirroring src/ structure)

Technical Requirements

frameworks
Flutter
BLoC
flutter_test
integration_test
apis
mocktail (Mock, when, verify)
bloc_test (blocTest, emitsInOrder)
flutter_test (WidgetTester, pumpWidget, pump, find, expect)
Supabase Edge Function client interface (mocked)
data models
ExportRun
ExportDateRange
ExportConfirmationData
DuplicateWarningData
ExportHistoryEntry
SignedFileUrl
performance requirements
Full widget test suite completes in under 60 seconds on CI
Integration test completes in under 90 seconds with stubbed network
security requirements
No real Supabase credentials or signed URLs used in tests — all network calls stubbed with mocktail
Test fixtures must not contain real user data or real export file URLs

Execution Context

Execution Tier
Tier 6

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.

Component
Accounting Export Screen
ui medium
Epic Risks (2)
medium impact medium prob technical

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.

high impact medium prob technical

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.