Write widget tests for ExpiryStatusIndicator
epic-certificate-expiry-notifications-user-interface-task-004 — Write Flutter widget tests covering all three expiry states in both compact and full display modes. Tests must verify correct color rendering per state, WCAG-compliant contrast using the contrast ratio validator, Semantics node presence with correct label text, and that the widget tree renders without overflow or layout errors.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 3 - 413 tasks
Can start after Tier 2 completes
Implementation Notes
The contrast ratio helper should implement the exact WCAG 2.2 formula: relative luminance L = 0.2126R + 0.7152G + 0.0722B (after gamma correction), then ratio = (L1 + 0.05) / (L2 + 0.05) where L1 is the lighter color. Flutter's Color stores values as integers — extract red, green, blue as doubles in [0,1] range before applying gamma correction. Do not import a third-party contrast library — implement inline. For Semantics testing, ensure the test MaterialApp has debugShowCheckedModeBanner: false and Overlay support so Semantics traversal works correctly.
Use tester.pumpWidget(MaterialApp(home: Scaffold(body: widget))) as the minimal host. To test dark mode colors, wrap with Theme(data: ThemeData.dark(), child: ...). Keep all test assertions focused on the public contract (colors, semantics, layout safety) — do not test private methods or internal widget structure that may change during refactoring.
Testing Requirements
All tests are Flutter widget tests using flutter_test. Structure the file with nested group() blocks: outer group per display mode (compact, full), inner group per expiry state. Use a shared helper pumpIndicator(tester, status, mode, {dateLabel}) to reduce boilerplate. For color assertions, retrieve the BoxDecoration from the badge Container using tester.widget
For contrast ratio, implement a static double _contrastRatio(Color fg, Color bg) function using the WCAG relative luminance formula directly in the test file. For Semantics assertions, use tester.getSemantics(find.byType(ExpiryStatusIndicator)) or SemanticsHandle. For overflow detection, wrap the widget in a SizedBox(width: 320) and use tester.takeException() — any RenderFlex overflow throws an exception in test mode. Target 100% line coverage of ExpiryStatusIndicator build paths.
The persistent banner must remain visible across app sessions and only disappear when a specific backend condition is met (renewal or coordinator acknowledgement). If the BLoC state is not properly sourced from the notification record repository on every app launch, the banner may disappear prematurely or fail to reappear after a session restart.
Mitigation & Contingency
Mitigation: Drive the banner's visibility exclusively from a Supabase real-time subscription on the notification records table filtered by mentor_id and acknowledged_at IS NULL. Never persist banner visibility state locally. Write an integration test that restarts the BLoC and verifies the banner reappears from the database source.
Contingency: If real-time subscriptions introduce latency or connection reliability issues in offline-first scenarios, add a local cache flag that is only cleared when the repository confirms the acknowledgement write succeeded, with a cache TTL of 24 hours as a fallback.
The notification detail view must conditionally render coordinator-specific actions based on the authenticated user's role. Incorrect role resolution could expose the 'Acknowledge Lapse' action to peer mentors or hide it from coordinators, breaking the workflow and potentially allowing unauthorised state changes.
Mitigation & Contingency
Mitigation: Source the role check from the existing role_state_manager BLoC that is already authenticated against Supabase role claims. Do not rely on a local flag. The coordinator acknowledgement service backend also validates role server-side, providing defence in depth. Add widget tests that render the detail view with mentor and coordinator role fixtures and assert the presence or absence of coordinator actions.
Contingency: If a role resolution bug is found in production, immediately disable the acknowledge action via a feature flag and patch the role check in a hotfix release. The server-side validation in the coordinator acknowledgement service ensures no actual state change can occur even if the button is incorrectly rendered.