high priority medium complexity testing pending testing specialist Tier 5

Acceptance Criteria

All tests reside in integration_test/summary_share_service_test.dart and run via `flutter test integration_test/`
Screenshot capture test: SummaryShareService.share() returns a ShareResult where the internal capturedBytes field is non-null and length > 0
iOS gallery — permission granted: mock permission channel returns 'granted'; gallery save method is called exactly once; ShareResult.success is true
iOS gallery — permission denied: mock permission channel returns 'denied'; settings-redirect dialog widget appears in the widget tree; ShareResult.permissionDenied is true
Android share sheet: mock share_plus channel receives an invocation with args containing MIME type 'image/png'; ShareResult.success is true
Clipboard: mock clipboard channel receives a setData call; a SnackBar or Toast with a confirmation message is visible in the widget tree within 500 ms; ShareResult.success is true
Failure propagation: when screenshot capture mock throws an exception, ShareResult.success is false and userMessage is non-empty
All platform channel mocks are restored to their original handlers after each test to prevent cross-test contamination
Tests do not require a physical device — all platform interactions are mocked via TestDefaultBinaryMessengerBinding

Technical Requirements

frameworks
Flutter
flutter_test
integration_test
apis
TestDefaultBinaryMessengerBinding (platform channel mocking)
WidgetTester
data models
ShareResult
ShareDestination
performance requirements
Each integration test completes within 10 seconds
security requirements
No real permission dialogs triggered during CI — all permission channels must be mocked
ui components
SnackBar or Toast (confirmation message)
AlertDialog (settings redirect)

Execution Context

Execution Tier
Tier 5

Tier 5 - 253 tasks

Can start after Tier 4 completes

Implementation Notes

Platform channel names must match exactly what the underlying plugins use (e.g., 'plugins.flutter.io/image_gallery_saver', 'flutter/clipboard'). Discover these by reading the plugin source or running with verbose channel logging. For the iOS permission flow, mock the permission_handler plugin channel, not a custom channel. Use pumpAndSettle() after share() calls to allow async state propagation before asserting widget tree.

To test the settings dialog, assert find.byType(AlertDialog) and optionally find.text('Open Settings'). Avoid testing on a real device in CI — the integration_test package supports headless execution when platform channels are fully mocked.

Testing Requirements

Integration tests using flutter_test integration_test package with WidgetTester for UI assertions and TestDefaultBinaryMessengerBinding.instance.setMockMethodCallHandler for platform channel mocking. Each platform flow is a separate testWidgets() block. Use setUp() and tearDown() to register/restore channel mocks. Assert both the return value (ShareResult) and side effects (widget tree changes, channel invocations).

For channel invocation verification, capture calls in a local list within the mock handler and assert list length and argument values after the share() call completes.

Component
Summary Share Service
service medium
Epic Risks (3)
high impact medium prob integration

Activity records may contain duplicate entries (as evidenced by the duplicate-detection feature dependency) or proxy-registered activities that should be attributed differently. Including duplicates or mis-attributed records would produce inflated stats, undermining trust in the summary.

Mitigation & Contingency

Mitigation: Implement the aggregation query to join against the deduplication-reviewed-flag on activity records and filter out unresolved duplicates. Coordinate with the duplicate-detection feature team to confirm the authoritative flag field before implementing the RPC. Include a data-quality warning in the summary when unresolved duplicates are detected.

Contingency: If deduplication state is unreliable at release time, add a prominent disclaimer in the summary UI noting that figures reflect all registered activities and may include duplicates pending review. Track a follow-up task to re-aggregate after deduplication runs.

medium impact high prob scope

Each organisation wants to define their own milestone thresholds (e.g., NHF's counting model differs from HLF's certification model). Implementing configurable thresholds may expand scope significantly if the configuration UI is expected in this epic.

Mitigation & Contingency

Mitigation: Scope this epic strictly to the evaluation engine and a hardcoded default threshold set. Define the MilestoneDefinition interface with an organisation_id discriminator so per-org configs can be loaded from the database in a later sprint. Build the admin configuration UI as a separate follow-on task outside this epic.

Contingency: If stakeholders require per-org milestone configuration before launch, deliver a JSON-based configuration file per org as an interim solution, loaded from Supabase storage, until a full admin UI is built.

medium impact medium prob technical

Android 13+ restricts access to media collections and requires READ_MEDIA_IMAGES permission for gallery saves, while older Android versions use WRITE_EXTERNAL_STORAGE. Handling both permission models correctly across the device matrix is error-prone.

Mitigation & Contingency

Mitigation: Use the permission_handler Flutter package with version-aware permission requests abstracted behind the summary-share-service interface. Write platform-specific unit tests for both Android API levels in the test harness. Test on a minimum of three Android versions (API 29, 32, 34) in CI.

Contingency: If gallery save is broken on specific Android versions at launch, disable the 'Save to gallery' option on affected API levels and surface only clipboard and system share sheet, which require no media permissions.