Write widget and integration tests for history UI
epic-bufdir-report-history-ui-task-009 — Write flutter_test widget tests for ReportHistoryScreen, ReportHistoryListItem, and ReportSummaryMetricsWidget covering: correct rendering of all data fields, loading/empty/error state transitions, filter control interactions and state persistence, re-export and re-download button tap callbacks, pagination trigger on scroll, and accessibility semantics tree validation. Use mock state providers to isolate UI tests from service dependencies.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 5 - 253 tasks
Can start after Tier 4 completes
Implementation Notes
Create a test fixture factory (e.g., ReportHistoryTestFixtures) that produces typed fake data models for bufdir_export_audit_log and annual_summary — this avoids repetitive setup code across test files. For BLoC-based state, use the blocTest utility from bloc_test package if available, or directly emit states via a FakeBloc. For Riverpod-based state, use ProviderContainer with overrides to inject fakes. When testing scroll-to-bottom pagination, use tester.drag() on the ListView to simulate scroll, then verify the mock was called.
Accessibility tests: after pumping the widget, call tester.getSemantics() and traverse the tree asserting each interactive Finder has a non-empty label — this catches regressions from future refactors. Group tests into separate files per component to keep test files focused and maintainable.
Testing Requirements
Write all tests using flutter_test. Use ProviderScope/BlocProvider with mock implementations to inject fake state. Structure tests in describe/group blocks per component. Cover: (1) rendering correctness for all states (loading, loaded, empty, error), (2) interaction correctness (tap callbacks, filter changes, scroll-to-load), (3) accessibility semantics tree validation using tester.getSemantics().
Use FakeAsync or tester.pump() to control async state transitions without real timers. Do not use real Supabase client — mock all data sources at the repository or service layer.
The ReportSummaryMetricsWidget must display metrics as they were at time of submission, not recalculated from current data. If the metrics are not stored as a JSON snapshot in the history record at export time, the widget will either show wrong data or require a full re-aggregation on every list load.
Mitigation & Contingency
Mitigation: Ensure the Bufdir export pipeline (bufdir-report-export feature) writes a summary_metrics JSONB column in the history record at export time, containing total_activities, total_hours, and participants_reached. The UI widget reads only from this snapshot field — never from live aggregation queries.
Contingency: If snapshot data is missing for historical records (e.g., older exports before the column existed), display a 'Metrics not available for this report' placeholder in the widget rather than showing zeros or triggering a live aggregation that could return different figures.
Re-export can take several seconds (it runs the full generation pipeline). Without adequate progress feedback, coordinators may tap the button multiple times, triggering duplicate exports and duplicate history records.
Mitigation & Contingency
Mitigation: Disable the re-export button immediately on first tap and show an inline progress indicator on the list item. Guard against duplicate invocations at the service layer using an in-progress flag keyed by report ID. Display a loading state on the list item throughout the operation.
Contingency: If duplicate re-export records are created (e.g., due to a race condition), the history table will show multiple entries for the same original report — which is harmless but confusing. Add a deduplication UI hint ('Re-exported N times') and a backend guard that prevents more than one in-flight re-export for the same source record ID simultaneously.