Write integration and widget tests for Notification Centre UI
epic-in-app-notification-centre-ui-task-012 — Write a comprehensive test suite using flutter_test covering: (1) NotificationFilterBar emits correct BLoC events on chip tap and reflects active filter state, (2) NotificationTabBadge renders correct count, shows 99+ for overflow, and hides at zero, (3) NotificationDeepLinkHandler resolves valid payloads to correct routes and handles deleted entities with snackbar + mark-read, (4) NotificationCentreScreen renders skeletons in loading state, empty state when list is empty, and list items when loaded, (5) pull-to-refresh triggers BLoC refresh event, (6) infinite scroll triggers LoadNextPage near bottom. Target 80%+ branch coverage for all four components.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 7 - 84 tasks
Can start after Tier 6 completes
Implementation Notes
For BLoC widget tests, wrap the widget under test in a `BlocProvider
For route assertions in deep link tests, use a GoRouter with a NavigatorObserver mock or check the current location via a test GoRouter instance. Group happy-path tests first, then edge cases, then error cases within each file for readability.
Testing Requirements
This task IS the test implementation. Organize tests into four files: `notification_filter_bar_test.dart`, `notification_tab_badge_test.dart`, `notification_deep_link_handler_test.dart`, `notification_centre_screen_test.dart`. Use `group()` to separate test scenarios within each file. Run with `flutter test --coverage` and verify the lcov report shows >= 80% branch coverage.
Include a GitHub Actions step (or equivalent CI config) that fails the build if coverage drops below threshold. Use `mocktail` for dependency mocking. Avoid testing implementation details — test observable widget output and BLoC event emissions only.
If a referenced entity (contact, certification, activity) has been deleted or its RLS policy now excludes the current user, the deep link handler may navigate to a screen that renders in an error state or throws an unhandled exception.
Mitigation & Contingency
Mitigation: The deep link handler must perform a lightweight existence check (HEAD request or minimal SELECT) before pushing the route. Define a contract with each destination screen for how to handle a not-found entity ID passed as a route parameter.
Contingency: If the existence check itself fails (network error), navigate to the destination screen anyway and let it handle the error gracefully with its own error state; do not block navigation for network timeouts.
If the tab badge widget triggers a full rebuild of the bottom navigation bar on every unread count change, it will cause visible jank on devices with many active Realtime events (e.g., org admins receiving org-wide alerts).
Mitigation & Contingency
Mitigation: Scope the badge widget to a dedicated BlocSelector that rebuilds only when the unread count value changes, not on any BLoC state emission. Use RepaintBoundary to isolate the badge from the rest of the nav bar.
Contingency: If performance issues persist, debounce badge updates to a maximum of one rebuild per 500ms and display the last known count during the debounce window.
Complex swipe-to-mark-read gestures and dynamic list updates may conflict with VoiceOver/TalkBack navigation patterns, particularly for Blindeforbundet users who rely exclusively on screen readers.
Mitigation & Contingency
Mitigation: Provide a dedicated accessibility action (Semantics.onTap / CustomSemanticsAction) for mark-as-read on each list item so screen reader users do not need the swipe gesture. Test with VoiceOver on iOS and TalkBack on Android before each release.
Contingency: If the swipe gesture proves incompatible with assistive technologies, disable it when a screen reader is detected (via ScreenReaderDetectionService) and rely solely on the tap-to-read and accessible action pathways.