high priority low complexity testing pending testing specialist Tier 4

Acceptance Criteria

All widget tests pass in CI with `flutter test` — zero failures, zero skips
PauseConfirmationDialog: consequence text is rendered and visible in the default state
PauseConfirmationDialog: tapping the confirm button triggers the onConfirmed callback
PauseConfirmationDialog: tapping the cancel button triggers the onCancelled callback
PauseConfirmationDialog: reason field is present in the widget tree but not required for confirm to be enabled
PauseStatusBanner: renders the formatted pause timestamp when provided via BLoC state
PauseStatusBanner: renders the pause reason string when provided via BLoC state
PauseStatusBanner: tapping the reactivate shortcut calls context.read<MentorStatusBLoC>().add(ReactivateMentorEvent) exactly once
PauseReactivateToggle: shows a loading indicator when BLoC emits MentorStatusLoading
PauseReactivateToggle: reflects active/inactive toggle state correctly on MentorStatusSuccess
PauseReactivateToggle: renders error text when BLoC emits MentorStatusError
All BLoC-dependent widgets use MockMentorStatusBLoC — no real BLoC or service instantiated in widget tests
Test files follow project naming: pause_confirmation_dialog_test.dart, pause_status_banner_test.dart, pause_reactivate_toggle_test.dart

Technical Requirements

frameworks
Flutter
flutter_test
BLoC
bloc_test
Mocktail
data models
MentorStatusState
MentorStatusLoading
MentorStatusSuccess
MentorStatusError
ReactivateMentorEvent
performance requirements
Full widget test suite for the three components completes in under 10 seconds
ui components
PauseConfirmationDialog
PauseStatusBanner
PauseReactivateToggle
MockMentorStatusBLoC

Execution Context

Execution Tier
Tier 4

Tier 4 - 323 tasks

Can start after Tier 3 completes

Implementation Notes

Create a shared test helper (e.g., pumpWithBloc()) that wraps a widget in MaterialApp + Theme + BlocProvider to avoid boilerplate repetition across the three test files. For MockMentorStatusBLoC, override stream and state getters to return the values set by whenListen / when(). For the PauseConfirmationDialog, since it takes callbacks rather than BLoC directly, tests are straightforward — no mock BLoC needed there. Use tester.tap() + tester.pump() (not pumpAndSettle if there are ongoing animations that should not settle).

Avoid golden file tests at this stage to keep CI fast; screenshot regression can be added later.

Testing Requirements

Use flutter_test's WidgetTester and pump/pumpAndSettle. Wrap widgets under test in MaterialApp + BlocProvider(create: (_) => mockBloc) so that context.read and BlocBuilder work correctly. For PauseConfirmationDialog, pass callbacks as parameters and verify they are called via a boolean flag or verify() on a mock. For BLoC-dependent widgets (banner and toggle), use MockMentorStatusBLoC extending Mock and implement MentorStatusBLoC, then use whenListen to emit state sequences.

Use find.byType, find.text, and find.byKey to assert widget tree. Test the error text visibility using expect(find.text('...'), findsOneWidget).

Component
Pause / Reactivate Toggle
ui low
Epic Risks (3)
medium impact medium prob technical

PauseConfirmationDialog must meet WCAG 2.2 AA focus trap requirements: when the dialog opens, focus must move to the first interactive element; when it closes, focus must return to the triggering toggle. Flutter's default showDialog does not always guarantee correct focus restoration on Android, which could trap screen-reader users.

Mitigation & Contingency

Mitigation: Use a custom modal implementation wrapping Flutter's Dialog with explicit FocusScope management and test with TalkBack on Android and VoiceOver on iOS during development, not just in final QA. Reference the accessible-modal-sheet pattern already used elsewhere in the codebase.

Contingency: If full WCAG focus management cannot be achieved within the sprint, ship the feature with a documented accessibility defect and schedule a dedicated accessibility remediation task. Communicate the known gap to the accessibility stakeholder at HLF/NHF.

low impact medium prob technical

If the user taps the PauseReactivateToggle rapidly before the BLoC emits a loading state, multiple PauseMentorEvents could be added to the BLoC stream, resulting in duplicate service calls and inconsistent UI state (toggle flickering between states).

Mitigation & Contingency

Mitigation: Disable the toggle widget immediately on first tap by emitting MentorStatusLoading synchronously before the async service call begins. Use BLoC's event transformer with droppable() to discard subsequent events while a transition is in progress.

Contingency: If BLoC event deduplication is not sufficient, add a debounce at the widget level (300 ms) and a server-side idempotency check in MentorStatusService that no-ops if the requested transition is already in progress.

low impact low prob security

PauseStatusBanner must conditionally render the reactivate shortcut only for users with coordinator permission. If the permission check relies on a stale role state in the Riverpod provider, a peer mentor could briefly see a reactivate action they are not authorised to use, which could cause a confusing permission-denied error if tapped.

Mitigation & Contingency

Mitigation: Source the permission check directly from the role-resolution Riverpod provider (which is kept in sync with Supabase auth) rather than from local component state. Add a widget test asserting the shortcut is absent when the user role is peer_mentor.

Contingency: If the shortcut is accidentally shown to an unauthorised user, the underlying MentorStatusService enforces role validation server-side, so the worst outcome is a visible error message rather than an actual unauthorised state change.