End-to-end integration test: full Wrapped flow
epic-annual-impact-summary-orchestration-task-015 — Write integration tests (flutter_test with integration_test package) covering the complete Wrapped flow: push notification deep-link opens WrappedSummaryScreen → slides load → navigation between slides → share action succeeds → period change reloads data → offline cache displayed when network unavailable. Use a seeded Supabase test environment for deterministic data.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 7 - 84 tasks
Can start after Tier 6 completes
Implementation Notes
Structure the test file as a single testWidgets suite with setUp/tearDown for Supabase test data seeding and cleanup. Use a dedicated test_helpers/wrapped_test_seed.dart file to encapsulate all data seeding logic (annual_summary rows, assignments, activities) so seeds can be reused by future tests. For the deep-link scenario, directly call the notification handler function with a constructed RemoteMessage object rather than relying on real FCM — this makes the test deterministic and CI-safe. For slide navigation, use WidgetTester.drag() on the PageView or tap on arrow/dot controls depending on the slide navigation implementation.
For the offline scenario, override the repository's HTTP client or Supabase client with a mock that returns cached data (from the previously loaded state) and throws a connectivity exception on the subsequent load — assert the offline banner widget key is present. Ensure tests do not share state between scenarios: use separate testWidgets blocks rather than one monolithic test to allow individual failures to be diagnosed.
Testing Requirements
This task IS the testing requirement. Integration tests must use the integration_test package with flutter_test. Use testWidgets with a real app pump (pumpWidget(MyApp())) backed by ProviderScope overrides for the network layer and feature flags. Seed the Supabase test environment using a setup script or Supabase migrations applied to a dedicated test project.
Network interception for the offline scenario can be achieved by overriding the Supabase client provider with a mock that throws a SocketException after initial data load. Use share_plus's platform channel mock to assert share invocation without triggering the real share sheet on CI. All integration tests must run on both iOS Simulator and Android Emulator in CI. TestFlight distribution is used for physical device validation but is not required for CI pass.
If the device transitions between online and offline states while the user is mid-session in the wrapped screen, the BLoC may emit conflicting state transitions (loaded → error → offline) that cause visual flickering or an inconsistent UI state such as showing the offline banner over an already-loaded summary.
Mitigation & Contingency
Mitigation: Implement a connectivity stream listener in the BLoC that only triggers a state re-evaluation when transitioning from online to offline, not on every connectivity event. Once a summary is in the Loaded state, the BLoC should not transition to error/offline unless the user explicitly requests a refresh. Store the last-loaded data in BLoC state so it survives connectivity changes.
Contingency: If state flickering is observed in testing, add a minimum 3-second debounce on connectivity state changes before the BLoC reacts, and display a non-blocking top banner rather than replacing the entire screen state.
The push notification deep-link to the wrapped-summary-screen must work correctly whether the app is in the foreground, background, or terminated state. Handling all three app launch states on both iOS and Android is a common source of edge-case bugs, particularly when authentication state must be restored before the deep link can be resolved.
Mitigation & Contingency
Mitigation: Implement deep-link handling through the existing notification-deep-link-handler component which already manages app-state-aware routing. Define the wrapped-summary route in the navigation config early in the epic so the router is ready before notification dispatch is wired. Test all three app states (foreground, background, terminated) explicitly in the QA checklist.
Contingency: If terminated-state deep-linking fails on specific platforms, fall back to launching the app to the home screen with an in-app notification banner prompting the user to open their summary, rather than direct deep-link navigation.
The wrapped-summary-screen manages a large number of AnimationController instances (one or more per slide) via the wrapped-animation-controller. If disposal is not triggered correctly when the user exits mid-flow (e.g., via system back gesture or deep-link away), memory leaks will accumulate across session navigation.
Mitigation & Contingency
Mitigation: Implement screen disposal via Flutter's dispose() lifecycle method calling a single wrapped-animation-controller.disposeAll() method that iterates the named controller registry. Write a test that navigates to the screen, starts animations, then navigates away and verifies no active AnimationController listeners remain using Flutter's test binding.
Contingency: If disposal bugs are detected in production via memory profiling, patch by converting all AnimationControllers to use AutomaticKeepAliveClientMixin false and wrap each slide in a widget that disposes its own controller when removed from the widget tree.