Integration test: accessibility provider wired to all widgets
epic-annual-impact-summary-ui-components-task-016 — Write Flutter widget integration tests verifying that SummaryAccessibilityProvider is correctly wired into all four widgets (StatCardWidget, MilestoneBadgeWidget, ActivityTypeBreakdownWidget, SummaryShareOverlay). Tests must confirm: announcement strings are non-empty, reduced-motion flag suppresses animations, and Semantics trees contain required labels.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 4 - 323 tasks
Can start after Tier 3 completes
Implementation Notes
Enable semantics in the test harness with tester.ensureSemantics() (disposes automatically) or SemanticsHandle. When asserting label presence, use find.bySemanticsLabel(RegExp(r'.+')) with findsWidgets to detect any non-empty label, then add specific string matchers per widget. For animation duration assertions, expose a @visibleForTesting getter on each widget's State that returns the AnimationController, or use a test-only factory constructor. Alternatively, use FakeAsync and verify no pending timers after reduced-motion pump.
Avoid using integration_test package (device-based) — pure flutter_test is sufficient and faster. Organise fixtures in test/fixtures/summary_accessibility_fixtures.dart to keep test data DRY across test files.
Testing Requirements
All tests are Flutter widget tests (flutter_test), not integration tests requiring a device. Use tester.semantics.enabled = true before pumping widgets. For reduced-motion assertions, inject a SummaryAccessibilityProvider configured with reducedMotionEnabled: true / false respectively and assert animation durations or absence of in-progress animations after pump. Structure test file with a group() per widget.
Each group must have: a 'semantics labels present' test, a 'reduced motion suppresses animations' test, and an 'announcement string non-empty' test. Minimum 12 test cases total across the four widgets. Measure and report line coverage for the four widget files — target ≥ 75%.
Simultaneous count-up animations across multiple stat cards and chart draw-in animations on lower-end Android devices may cause frame drops below 60fps, degrading the premium Wrapped experience and making the feature feel unpolished.
Mitigation & Contingency
Mitigation: Stagger animation starts using AnimationController with staggered intervals rather than starting all animations simultaneously. Use RepaintBoundary around each animated widget to isolate rasterisation. Profile on a mid-range Android device (e.g., equivalent to Pixel 4a) during development, not just at QA.
Contingency: If frame rate targets cannot be met on low-end devices, implement a device-capability check at startup and substitute simpler fade-in animations for the count-up and chart draw-in on devices below a CPU performance threshold.
The activity-type-breakdown-widget must render organisation-specific activity type labels sourced from the terminology system. If the terminology provider is not yet integrated at the time this widget is built, the widget will display hardcoded system labels, which is a regression risk for multi-org support.
Mitigation & Contingency
Mitigation: Accept activity type labels as a typed parameter in the widget constructor rather than reading from the terminology provider directly inside the widget. The BLoC or repository layer resolves labels before passing them to the widget, maintaining clean separation and testability.
Contingency: If terminology resolution is unavailable at widget integration time, display internal activity type keys as a temporary fallback with a localised suffix '(label pending)' visible only in non-production builds so QA can identify unresolved labels.