high priority medium complexity testing pending testing specialist Tier 8

Acceptance Criteria

BLoC unit tests cover: SelectPreset event transitions state to correct preset with updated DateTimeRange
BLoC unit tests cover: SelectCustomRange emits LoadingState then RecordCountLoadedState on success
BLoC unit tests cover: SelectCustomRange emits LoadingState then ValidationFailureState when range invalid
BLoC unit tests cover: in-flight count request is cancelled (no state emission from previous request) when a new SelectCustomRange is dispatched before the first completes
BLoC unit tests cover: ConfirmPeriod event emits PeriodConfirmedState only when no validation errors present
BLoC unit tests cover: ResetPeriod event returns BLoC to initial state
Widget tests cover: RecordCountBanner displays formatted count when state is loaded
Widget tests cover: RecordCountBanner renders live region Semantics label matching count text
Widget tests cover: RecordCountBanner gap banner shows correct day count and message
Widget tests cover: PeriodSelectionScreen with role=coordinator shows full screen, not NoAccessScreen
Widget tests cover: PeriodSelectionScreen with role=peer_mentor shows NoAccessScreen
Widget tests cover: confirm button disabled in loading state and enabled in valid state
Widget tests cover: navigation to export route triggered on PeriodConfirmedState emission
All test files follow flutter_test conventions and are placed in test/ mirroring lib/ directory structure
Test coverage for PeriodSelectionBloc >= 90% branch coverage

Technical Requirements

frameworks
Flutter
flutter_test
bloc_test
BLoC
Riverpod
data models
PeriodSelectionBloc
PeriodSelectionState
PeriodSelectionEvent
RecordCountBanner
PeriodSelectionScreen
performance requirements
All unit tests complete in < 5 seconds total
Widget tests use pumpAndSettle with timeout to avoid hanging on animation
security requirements
Tests must not make real network calls — all Supabase/API calls must be mocked
ui components
MockPeriodSelectionBloc using mocktail
ProviderScope with overrides for Riverpod providers in widget tests
MockGoRouter or NavigatorObserver for navigation assertions

Execution Context

Execution Tier
Tier 8

Tier 8 - 48 tasks

Can start after Tier 7 completes

Integration Task

Handles integration between different epics or system components. Requires coordination across multiple development streams.

Implementation Notes

For the in-flight cancellation test, inject a fake record count service that uses a Completer: emit the first SelectCustomRange, do NOT complete the first request, emit a second SelectCustomRange, complete only the second, and assert only the second result state is emitted (bloc_test's `expect` list should have exactly: [LoadingState, LoadingState, RecordCountLoadedState] not two loaded states). For widget tests of PeriodSelectionScreen, use BlocProvider.value to inject a pre-configured MockBloc rather than letting the screen create its own. Use `whenListen` from bloc_test to set up mock state streams. For role-guard tests, override currentUserRoleProvider in ProviderScope: `overrides: [currentUserRoleProvider.overrideWithValue(UserRole.peerMentor)]`.

For navigation tests, use GoRouter's testing utilities or a custom NavigatorObserver that records pushed routes.

Testing Requirements

Use bloc_test's blocTest() for all BLoC unit tests. Use flutter_test's testWidgets() for all widget tests. Use mocktail to mock the record count API service injected into BLoC. For cancellation test, use a Completer to control async resolution order.

Widget tests must wrap under MaterialApp + ProviderScope. Aim for: 6+ BLoC unit test cases, 8+ widget test cases. Run with `flutter test --coverage` and verify >90% line coverage on period_selection_bloc.dart.

Component
Period Selection BLoC
infrastructure low
Epic Risks (3)
medium impact medium prob technical

The record count query is asynchronous and may take up to 1 second on a slow connection. If the BLoC does not manage loading states carefully, the UI may show a stale count or the confirm button may be briefly enabled during the loading transition, allowing premature submission.

Mitigation & Contingency

Mitigation: Define explicit BLoC states: PeriodSelectionLoading, PeriodSelectionCountLoaded, PeriodSelectionValidationError, and PeriodSelectionReady. The confirm button must only be enabled in PeriodSelectionReady. Add a debounce of 300ms on period-change events before triggering the count query to prevent excessive calls.

Contingency: If debouncing is insufficient to prevent UX degradation on slow connections, add an optimistic loading skeleton to the RecordCountBanner that clearly communicates a pending state, and keep the confirm button disabled until the count resolves.

high impact low prob integration

The confirmedReportPeriodProvider Riverpod contract between this feature and the Bufdir Export feature may not be defined yet, causing integration failures when the downstream export screen attempts to read the provider.

Mitigation & Contingency

Mitigation: Define the confirmedReportPeriodProvider as a StateProvider<DateTimeRange?> in a shared providers file at the start of this epic, before any screen code is written. Communicate the provider contract to the Bufdir Export feature team so both sides align on the same provider reference.

Contingency: If the export feature consumes the period through a different mechanism (e.g., navigation arguments), add a thin adapter that writes the confirmed period to both the Riverpod provider and the navigation argument, ensuring backward compatibility.

medium impact medium prob technical

Flutter's live region support for screen readers is inconsistent across platforms (iOS VoiceOver vs Android TalkBack), and the record count banner must be announced on every change — a pattern that has historically required platform-channel workarounds.

Mitigation & Contingency

Mitigation: Test the live region announcement on both iOS (VoiceOver) and Android (TalkBack) early in implementation using the accessibility_test harness. Reference the project's existing live-region-announcer component (664-accessibility-live-region-announcer) for a proven implementation pattern.

Contingency: If native live region support is insufficient, wrap the record count text in a Semantics widget with a programmatically updated label string, and trigger a SemanticsService.announce call on each count update as a platform-agnostic fallback.