high priority medium complexity testing pending testing specialist Tier 4

Acceptance Criteria

A dedicated test file exists (e.g. test/integration/accessibility_provider_wiring_test.dart) covering all four widgets
StatCardWidget test: SemanticsController finds at least one Semantics node with non-empty label when SummaryAccessibilityProvider is in scope
MilestoneBadgeWidget test: locked and unlocked states both produce distinct, non-empty Semantics labels
ActivityTypeBreakdownWidget test: each segment has a Semantics label including segment name and percentage
SummaryShareOverlay test: share button, close button, and preview image all have non-empty Semantics labels
Reduced-motion ON: all four widgets' AnimationController durations are zero (assert via tester.binding.transientCallbackCount == 0 after pumpAndSettle, or check controller.duration == Duration.zero)
Reduced-motion OFF: animations run (assert AnimationController.duration > Duration.zero)
All announcement strings returned by SummaryAccessibilityProvider for each widget are non-empty and do not equal placeholder/fallback text like 'TODO' or empty string
Tests run cleanly with flutter test — no flaky async gaps (all awaited pumpAndSettle calls have explicit timeouts)
CI-safe: tests do not rely on real network, Supabase, or device locale — all external dependencies are mocked

Technical Requirements

frameworks
Flutter
flutter_test
BLoC test utilities (bloc_test package) for providing SummaryBloc stub
Mockito or mocktail for mocking SummaryAccessibilityProvider dependencies
apis
WidgetTester (flutter_test) — pump, pumpAndSettle, pumpWidget
SemanticsController (tester.semantics) — find by label, assert tree structure
SummaryAccessibilityProvider (inject via InheritedWidget or BLoC provider wrapper)
AnimationController introspection for reduced-motion assertions
data models
SummaryStatCardData (stubbed test fixtures)
MilestoneBadgeData (locked and unlocked fixtures)
ActivityTypeBreakdownData (3-segment and 6-segment fixtures)
AccessibilityLabels (from SummaryAccessibilityProvider)
performance requirements
Full test suite for this file must complete in under 30 seconds on CI (no real timers — use FakeAsync where needed)
security requirements
No real credentials or Supabase URLs in test fixtures — use placeholder strings
ui components
StatCardWidget
MilestoneBadgeWidget
ActivityTypeBreakdownWidget
SummaryShareOverlay
SummaryAccessibilityProvider (test double / real implementation with controlled input)

Execution Context

Execution Tier
Tier 4

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%.

Component
Summary Accessibility Provider
infrastructure medium
Epic Risks (2)
medium impact medium prob technical

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.

medium impact low prob integration

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.