Integration tests — full expense registration flow
epic-travel-expense-registration-ui-task-013 — Write widget and integration tests covering the end-to-end expense registration flow: type selection with mutual exclusion, amount entry crossing the receipt threshold, receipt attachment, summary confirmation, and successful submission. Cover the coordinator attestation flow: claim appears in queue, approve action updates status, realtime update reflected in UI. Use flutter_test with a mocked Supabase client and verify all BLoC state transitions.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 7 - 84 tasks
Can start after Tier 6 completes
Implementation Notes
Start by creating a shared test fixture file (expense_test_fixtures.dart) that defines reusable mock Supabase responses and sample ExpenseRegistration models — this prevents duplication across test files. For the mutual-exclusion test, directly call the BLoC's add(SelectExpenseTypeEvent) and use bloc_test's expect to assert the conflict state rather than relying on widget tap simulation, which is more fragile. For realtime tests, use a StreamController>> to emit mock Supabase events and inject it into the BLoC constructor via the test setup. The coordinator attestation test should use a FakeAsync zone to control time if any debounce/throttle logic exists in the BLoC.
Avoid testing UI pixel positions or exact colors — only assert widget presence/absence, semantic labels, and BLoC state correctness. All BLoC instances must be explicitly closed in tearDownAll to prevent test suite leaks that cause false failures on CI.
Testing Requirements
Use flutter_test for widget tests and bloc_test for isolated BLoC state transition tests. Mock the Supabase client using mocktail — define a MockSupabaseClient that stubs insert(), stream(), and from() calls. Use tester.pumpAndSettle() after async operations and tester.pump(Duration.zero) for immediate frame advances. Structure tests in three groups: (1) peer mentor expense registration flow, (2) coordinator attestation flow, (3) realtime update reflection.
Each group should have a happy path, at least one error path, and one edge case (e.g., threshold boundary at exactly 100 NOK). Run flutter test --coverage and assert ≥ 90% line coverage for all files under the expense registration feature directory.
The image_picker Flutter plugin requires platform-specific permissions (NSPhotoLibraryUsageDescription, camera permission) and behaves differently across iOS and Android versions. Permission denial or plugin misconfiguration can silently prevent receipt attachment.
Mitigation & Contingency
Mitigation: Configure all required permission strings in Info.plist and AndroidManifest.xml during initial plugin setup. Use the permission_handler package to check and request permissions before launching the picker, with clear user-facing explanations. Test on both platforms across at least two OS versions.
Contingency: If image_picker proves unreliable on a specific platform version, fall back to file_picker as an alternative that uses the OS document picker interface, which requires fewer permissions on some Android versions.
The expense form BLoC manages interconnected state across expense type selection, field visibility, receipt requirement, threshold evaluation, and submission flow. Incorrect state transitions can cause UI inconsistencies such as required receipt indicator not updating after amount change, or form appearing valid when mutual exclusion is violated.
Mitigation & Contingency
Mitigation: Model BLoC states as sealed classes with exhaustive pattern matching. Write state transition unit tests covering every combination of: type selection change, amount field change above/below threshold, receipt attachment/removal, and offline mode toggle. Use bloc_test for comprehensive state sequence assertions.
Contingency: If BLoC complexity becomes unmanageable, split into two BLoCs — one for type selection/exclusion state and one for field values/submission — coordinating via a parent provider, accepting the small overhead of inter-BLoC communication.
The expense type selector must enforce mutual exclusion visually by disabling options and showing conflict tooltips, while remaining fully accessible to screen reader users who cannot perceive visual disable states. Incorrect semantics labelling will fail WCAG 2.2 AA requirements critical for Blindeforbundet and HLF users.
Mitigation & Contingency
Mitigation: Use Flutter Semantics widgets to explicitly set disabled state and provide conflict explanations as semanticLabel strings on disabled options. Run accessibility audits with TalkBack and VoiceOver during widget development, not post-completion. Reference the project's accessibility test harness for required test coverage.
Contingency: If custom widget accessibility is difficult to certify, implement the selector as a standard Flutter Radio/Checkbox group with built-in accessibility semantics and an explanatory Text widget below each conflicting option, sacrificing visual elegance for guaranteed WCAG compliance.