high priority medium complexity testing pending testing specialist Tier 5

Acceptance Criteria

MentorInfoPopup renders mentor name, availability badge, and specialisation tags from BLoC state without triggering any Supabase or HTTP call — verified by asserting no network calls via MockSupabase
MentorInfoPopup focus trap is active: tabbing/focus navigation does not move focus outside the popup while it is open — verified via FocusTrap semantics node
MentorInfoPopup close button dismisses the popup and returns focus to the previously focused element (the map marker that opened it)
MentorInfoPopup deep-link button triggers correct GoRouter navigation to '/peer-mentor/{id}' with the correct mentor ID
MentorListFallback renders exactly N list items when the BLoC state contains N filtered mentors (test with N=0, N=1, N=10)
MentorListFallback live region announces 'Showing {count} mentors' when filter state changes — verified via Semantics liveRegion property
Each MentorListFallback item has a semantic label '{mentor name}, {availability state}, {specialisation}' — verified via semantic tree finder
View toggle from list back to map does not reset scroll position in the list — scroll offset snapshot before and after toggle must match
All tests pass with zero unhandled exceptions and zero semantic tree errors

Technical Requirements

frameworks
Flutter
flutter_test
BLoC
bloc_test
go_router
data models
PeerMentor
MapFilterState
MentorListState
performance requirements
Popup render must complete within one pumpAndSettle call
List of 50 mentors must render without frame drops — use ListView.builder, not Column
security requirements
Test fixtures must not include real names or personal identifiers
ui components
MentorInfoPopup
MentorListFallback
MentorListItem

Execution Context

Execution Tier
Tier 5

Tier 5 - 253 tasks

Can start after Tier 4 completes

Implementation Notes

For focus trap tests, use tester.sendKeyEvent(LogicalKeyboardKey.tab) and assert that the focused widget's key is still a descendant of the popup's key. Inject a MockGoRouter via ProviderScope or a constructor parameter so navigation assertions do not require a real routing context. For live region tests, ensure the Semantics widget uses the liveRegion: true property rather than politeAnnouncement — the test must validate the correct flag. The list scroll preservation test should store the ScrollController in the BLoC or a state holder accessible to both the list and the map view so that the controller is not disposed on view toggle.

Testing Requirements

Widget tests using flutter_test, MockBloc, and a MockGoRouter. Test focus trap behaviour by asserting FocusManager.instance.primaryFocus remains within the popup subtree after simulated keyboard navigation. Test live region by asserting SemanticsNode.hasFlag(SemanticsFlag.isLiveRegion) on the count text node. Scroll position preservation: use ScrollController and record controller.offset before and after ViewToggled event.

Achieve full branch coverage for empty list, single item, and multi-item states.

Component
Mentor Info Popup Card
ui low
Epic Risks (3)
high impact high prob technical

Flutter's map canvas (flutter_map) does not natively support semantic focus traversal for screen readers, meaning map markers may be entirely invisible to VoiceOver/TalkBack users. If the accessible list fallback is not treated as a first-class view, screen reader users will have no access to the feature.

Mitigation & Contingency

Mitigation: From sprint 1, treat mentor-list-fallback as a fully featured primary view, not an afterthought. Implement and test it in parallel with the map canvas. Make the view-toggle-button keyboard focusable and announced on every screen state. Conduct VoiceOver testing on device before submitting each PR touching UI components.

Contingency: If map canvas accessibility cannot be achieved for marker focus traversal, make the view-toggle-button the default focus target on screen load for VoiceOver users (detected via screen-reader-detection-service) so they are immediately directed to the list fallback without needing to discover the toggle.

medium impact medium prob technical

The mentor-info-popup must occupy no more than 40% of visible map area on small screens. On devices with screen heights under 667px (iPhone SE), overlapping with the filter panel or obscuring most of the map could severely degrade usability.

Mitigation & Contingency

Mitigation: Implement the popup as a bottom sheet capped at 40% of screen height with a ScrollView for overflow content. Test on iPhone SE (375x667pt) and the smallest commonly used Android form factor in the device lab. Define max-height as a percentage constant in location-privacy-config or design tokens.

Contingency: If the popup cannot fit all required fields within 40% height on smallest targets, truncate assigned contact count and certification badge to icons-only in the compact view, with a 'View Profile' button always visible at the bottom of the popup regardless of scroll position.

medium impact medium prob integration

Filter state must remain perfectly synchronised between the map view and the list fallback. If the filter panel emits state that is not consumed identically by both views, coordinators switching between views will see inconsistent mentor sets, eroding trust in the feature.

Mitigation & Contingency

Mitigation: Store active filter criteria in a single shared Riverpod provider owned by the map-view-screen and consumed by both map-marker-widget (via mentor-location-service) and mentor-list-fallback. Write integration tests that apply a filter, switch views, and assert identical mentor counts in both views.

Contingency: If filter sync proves brittle, simplify to a single filter state object passed explicitly as a constructor argument to both views on each rebuild, eliminating indirect state sharing.