medium priority medium complexity testing pending testing specialist Tier 5

Acceptance Criteria

Test file exists at test/features/bufdir/widgets/aggregation_progress_indicator_test.dart and aggregation_summary_widget_test.dart
AggregationProgressIndicator: test for each BLoC state (idle, loading, stage1Active, stage2Active, stage3Active, completed, error) — assert correct stage highlight and icon state for each
AggregationProgressIndicator: warning banner test — pumps widget with warningMessage set, asserts banner visible; pumps with warningMessage null, asserts banner absent
AggregationProgressIndicator: retry button test — taps retry button and verifies correct BLoC event dispatched via MockBloc
AggregationSummaryWidget: renders participant count card with value from mock data
AggregationSummaryWidget: renders activity hours table with correct row count matching mock data categories
AggregationSummaryWidget: renders geographic distribution section with correct entry count
AggregationSummaryWidget: unmapped warning section is absent when unmappedActivities is empty
AggregationSummaryWidget: unmapped warning section is present and shows correct activity names when unmappedActivities is non-empty
AggregationSummaryWidget: numeric value '1234' is rendered as '1 234' (Norwegian locale formatting)
flutter test --coverage reports 90%+ branch coverage for both widget files
All tests pass on both iOS (simulator) and Android (emulator) via flutter test

Technical Requirements

frameworks
flutter_test
bloc_test (MockBloc / MockCubit for BLoC state injection)
mocktail or mockito for dependency mocking
data models
annual_summary (mock data for AggregationSummaryData)
activity_type (mock unmapped activity entries)
bufdir_column_schema (mock category mappings)
performance requirements
Full test suite for both widget files must complete in under 30 seconds in CI
security requirements
Test fixtures must not contain real user data or production UUIDs — use generated fake UUIDs

Execution Context

Execution Tier
Tier 5

Tier 5 - 253 tasks

Can start after Tier 4 completes

Implementation Notes

Use bloc_test's MockBloc approach: class MockAggregationBloc extends MockBloc implements AggregationBloc {}. When testing BLoC state rendering, use whenListen(mockBloc, Stream.value(specificState), initialState: AggregationInitial()) so the widget tree rebuilds with the target state. For locale formatting tests, wrap the widget in a Localizations widget providing nb_NO locale: MaterialApp(locale: const Locale('nb', 'NO'), home: AggregationSummaryWidget(data: mockData)). For the retry button event test, use verify(() => mockBloc.add(RetryAggregationEvent())).called(1) after tap.

Key coverage gap to watch: the conditional unmapped warnings section — ensure both the empty and non-empty branches have dedicated tests. Use Key() widgets on critical testable elements (warning section, retry button, stage indicators) to make finders robust.

Testing Requirements

All tests use flutter_test WidgetTester. BLoC states are injected by wrapping the widget under test in BlocProvider(create: (_) => MockAggregationBloc()) and using whenListen / MockBloc patterns. Each test follows Arrange-Act-Assert: (1) arrange: build widget tree with specific state/data; (2) act: pump or tap; (3) assert: find.text / find.byType / find.byKey / expect(tester.getSize()...). Use pumpAndSettle() only where animations are expected; use pump() for instant state changes.

Create a shared test_fixtures.dart file with factory constructors for AggregationSummaryData to avoid test data duplication. Run flutter test --coverage and generate lcov report to verify 90%+ branch coverage. Include a negative test for each conditional render path (both the true and false branch).

Component
Aggregation Summary Widget
ui medium
Epic Risks (2)
medium impact high prob scope

For NHF with 1,400 local chapters, rendering a geographic breakdown as a flat list in the AggregationSummaryWidget would produce an unscrollable wall of data that is unusable on a mobile screen. Coordinators need to verify geographic completeness before export, but the raw chapter list is too long to review.

Mitigation & Contingency

Mitigation: Design the geographic distribution section as a collapsible two-level hierarchy: regions are shown expanded by default with their aggregate count, individual chapters are collapsed under each region and expandable on tap. Show only non-zero regions by default with a 'show empty regions' toggle.

Contingency: If the hierarchical display is too complex to implement before the submission deadline, fall back to a region-only summary view with a total count of active chapters per region and a note that chapter-level detail is available in the full export file.

medium impact medium prob technical

The aggregation BLoC must handle a multi-stage async pipeline with partial success states, warning accumulation across stages, and retry logic for individual failed stages. If the state model is too simplistic (e.g., loading/success/error), warning states and partial results will be lost on retry, confusing coordinators who see warnings disappear and reappear.

Mitigation & Contingency

Mitigation: Model the BLoC state as a rich sealed class hierarchy: AggregationIdle, AggregationRunning(stage, completedStages, accumulatedWarnings), AggregationComplete(result, warnings), AggregationFailed(failedStage, accumulatedWarnings, error). Accumulated warnings persist across retry attempts so coordinators see the full warning history.

Contingency: If state management complexity causes repeated bugs near the deadline, simplify to a single AggregationResult value object that captures success, warnings, and error as nullable fields, and drive both UI components from that single object rather than a live BLoC stream.