high priority medium complexity testing pending testing specialist Tier 4

Acceptance Criteria

AttachmentPickerUI tests: camera source button triggers image_picker with ImageSource.camera; gallery source button triggers image_picker with ImageSource.gallery; file source button triggers file_picker with allowed MIME types only
AttachmentPickerUI tests: attempting to add a 6th attachment when 5 are already selected shows an error snackbar and does NOT invoke the picker
AttachmentPickerUI tests: selecting a disallowed MIME type (e.g. .exe) displays a rejection message and the attachment list remains unchanged
AttachmentPickerUI tests: after a successful pick, a thumbnail preview of the selected file appears in the pending-attachment list
AttachmentThumbnailGrid tests: given a list of image attachments, each renders an Image widget with correct asset/network source
AttachmentThumbnailGrid tests: given a PDF attachment, a PDF-type placeholder icon is rendered (not an Image widget)
AttachmentThumbnailGrid tests: tapping any thumbnail calls the onTap callback with the correct attachment index/id
AttachmentThumbnailGrid tests: delete icon is visible when the current user role has delete permission; delete icon is absent for roles without permission
AttachmentThumbnailGrid tests: tapping delete icon invokes onDelete callback with correct attachment id
AttachmentPreviewModal tests: loading state renders a CircularProgressIndicator and no content widget
AttachmentPreviewModal tests: content-loaded state renders the correct widget type (image viewer for images, PDF viewer for PDFs)
AttachmentPreviewModal tests: error state renders an error message widget with retry button
AttachmentPreviewModal tests: share button triggers share_plus with the correct file path or URL
AttachmentPreviewModal tests: close button or back navigation pops the modal route
All test files are co-located in test/widgets/ and follow the project naming convention *_test.dart
All service and repository dependencies are injected via constructor and replaced with mockito mocks in tests — no real network or storage calls occur
Test suite runs to completion with flutter test with zero failures

Technical Requirements

frameworks
Flutter
flutter_test
mockito
BLoC
data models
activity
performance requirements
Full widget test suite for these three components completes in under 60 seconds on CI
No real I/O (network, storage, file system) executed during any test — all mocked
security requirements
Mock objects must not reference real Supabase credentials or API keys
Test fixtures must not contain real user PII — use synthetic data only
ui components
AttachmentPickerUI
AttachmentThumbnailGrid
AttachmentPreviewModal

Execution Context

Execution Tier
Tier 4

Tier 4 - 323 tasks

Can start after Tier 3 completes

Implementation Notes

Use flutter_test's pumpWidget wrapping components inside a MaterialApp with a BlocProvider (or Riverpod ProviderScope) to supply mocked state. For role-gated delete visibility, inject a mock role/auth provider that returns a controllable role enum. For the 5-attachment limit, drive state by pre-populating the BLoC/Riverpod state with 5 existing attachments before pumping the widget. For MIME rejection, mock image_picker/file_picker to return a file with a disallowed extension and assert the UI reacts correctly.

For AttachmentPreviewModal async states, use a Completer-backed mock to manually transition between loading → content and loading → error states. Avoid testing platform channel code directly — focus on the widget layer's response to the service mock's return values.

Testing Requirements

Widget tests only (no unit or integration tests in this task). Use flutter_test WidgetTester with pumpWidget and pump/pumpAndSettle for async states. Mock all service layer dependencies (attachment service, storage adapter, role provider) using mockito @GenerateMocks annotation. Cover: happy path for each source type, all error/edge-case branches (limit exceeded, MIME rejection, network error in modal), and role-conditional visibility.

Aim for 100% branch coverage of the three component files. Run with flutter test test/widgets/attachment_picker_ui_test.dart test/widgets/attachment_thumbnail_grid_test.dart test/widgets/attachment_preview_modal_test.dart.

Component
Attachment Picker UI
ui medium
Epic Risks (4)
medium impact medium prob dependency

Flutter does not include a first-party PDF renderer. Third-party packages (e.g., flutter_pdfview, syncfusion_flutter_pdf) have inconsistent accessibility support, may not honour dynamic type scaling, and can introduce large binary size increases. Choosing the wrong package late in development risks rework or an inaccessible PDF experience for Blindeforbundet users.

Mitigation & Contingency

Mitigation: Evaluate and spike two PDF rendering packages (flutter_pdfview and pdfx) in the first task of this epic before committing to implementation. Criteria: VoiceOver compatibility, dynamic type support, APK/IPA size delta, and licence. Document the decision in an ADR.

Contingency: If no package meets accessibility requirements, fall back to opening PDFs in the system browser via url_launcher, which inherits the OS's accessible PDF viewer. Display a clear 'Opening in external viewer' message to set user expectation.

medium impact medium prob technical

Flutter's Semantics API for live region announcements (analogous to aria-live) has known gaps on Android for dynamic content updates. Upload progress announcements required by the accessibility user story may not fire reliably on Android devices used by HLF and NHF members.

Mitigation & Contingency

Mitigation: Use SemanticsService.announce() for imperative announcements at each upload state transition rather than relying on declarative Semantics widget tree updates. Test on a physical Android device with TalkBack enabled during development, not only on iOS with VoiceOver.

Contingency: If SemanticsService.announce() proves unreliable, implement a persistent accessible status banner at the top of the screen that reflects the current upload state as plain text, satisfying the WCAG success criterion through a visual + programmatic mechanism.

low impact high prob technical

iOS and Android file picker behaviour diverges significantly: MIME type filtering works reliably on Android but is advisory on iOS (users can still navigate to and select non-compliant files via the Files app). This could allow unsupported file types to reach the upload service, causing validation failures with a confusing user experience.

Mitigation & Contingency

Mitigation: Rely on the AttachmentUploadService (Epic 2) as the authoritative MIME validation gate, regardless of platform. In the UI, re-validate the picked file's extension/MIME after selection and show an inline plain-language error ('Only PDF, JPEG, and PNG files are supported') before even calling the service.

Contingency: If users consistently hit the error due to iOS file picker limitations, add a user-facing help tooltip on the picker sheet explaining the supported file types, reducing support volume while the underlying OS limitation persists.

high impact medium prob scope

WCAG 2.2 AA compliance for three new interactive components (picker, grid, modal) with file system integration, role-gated actions, and live regions is a significant accessibility surface area. An incomplete audit before release could result in the feature being unusable for Blindeforbundet screen reader users at launch.

Mitigation & Contingency

Mitigation: Allocate a dedicated accessibility audit task at the end of the epic using the accessibility-test-harness (619) and wcag-compliance-checker (620) components already in the project. Include at least one manual test session on a physical device with VoiceOver enabled. Fail the epic's definition of done if any WCAG 2.2 AA violation remains open.

Contingency: If critical accessibility issues are found late, gate the feature behind the org-level attachments_enabled feature flag and only enable it for NHF (the requesting org) after a targeted fix cycle, rather than delaying the release for all organisations.