high priority medium complexity testing pending testing specialist Tier 2

Acceptance Criteria

An integration test file exists (e.g., test/integration/receipt_threshold_e2e_test.dart) that sets up two Supabase test org fixtures with different receipt_threshold values (100 and 500)
Test scenario A: Log in as a user belonging to the 100 NOK org, open the expense form, enter 99 NOK — indicator shows 'optional'; enter 100 NOK — indicator shows 'required'
Test scenario B: Log in as a user belonging to the 500 NOK org, enter 499 NOK — indicator shows 'optional'; enter 500 NOK — indicator shows 'required'
Test scenario C: Cross-org isolation — user from org A cannot read org B's threshold config (assert Supabase RLS blocks the query)
A grep/static analysis step asserts that no numeric literal matching known threshold values (100, 500) appears in the ReceiptAttachmentIndicator widget source file or any UI layer file it imports
All three test scenarios pass in CI without manual intervention or environment-specific secrets embedded in test code
Test fixtures are torn down after each test run, leaving no residual test data in the Supabase test project
Test execution time is under 30 seconds per scenario on CI hardware

Technical Requirements

frameworks
Flutter
Riverpod
flutter_test
apis
Supabase PostgreSQL 15 (test fixture setup and teardown)
Supabase Auth (test user provisioning)
data models
activity
activity_type
performance requirements
Each test scenario must complete within 30 seconds
Test fixture setup (org + user creation) must complete within 5 seconds via Supabase service role API
security requirements
Supabase service role key used only in test setup helper, never hardcoded in test files — read from environment variable
Test user credentials must be synthetic and not reuse any production account patterns
RLS cross-org isolation explicitly asserted as part of the test suite

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Structure the test using a TestOrgFixture helper class that encapsulates Supabase service role calls for creating and deleting test organizations and users. This helper is reusable across the test suite. Use flutter_test's integration_test package for widget-level integration testing rather than a full e2e driver test to keep execution time low. The test should import the Riverpod ProviderContainer and override the org config provider with real Supabase data (not mocked) to validate the full data path.

For the static analysis assertion, implement as a Dart test that reads the source file as a string and asserts no threshold literals — this is a simple but effective guard against regression. Document the test environment requirements (Supabase test project URL and anon key) in the project's .env.example file.

Testing Requirements

Integration tests (flutter_test integration_test package): use Supabase service role client in test setup to insert org rows with different receipt_threshold values and create test user accounts. Use pumpWidget with the full app routing or a scoped widget tree that includes the expense form and indicator. Simulate amount input via tester.enterText(). Assert indicator semantic label or widget state after each amount change.

Teardown: delete test orgs and users via service role client in tearDown(). Static analysis step: run grep in CI over the indicator widget file and its transitive UI imports to confirm absence of threshold literals.

Component
Receipt Attachment Indicator
ui low
Epic Risks (2)
medium impact medium prob technical

Flutter's accessibility live region support (SemanticsProperties.liveRegion) has known inconsistencies between iOS VoiceOver and Android TalkBack, and between Flutter versions. Threshold-crossing announcements may fail to fire or double-fire, breaking the accessibility contract for Blindeforbundet users.

Mitigation & Contingency

Mitigation: Test live region announcements on physical devices with VoiceOver and TalkBack enabled from the first iteration. Use the AccessibilityLiveRegionAnnouncer component pattern already established in the project. Verify announcement timing relative to Bloc state emissions to avoid double-fires.

Contingency: If Flutter live regions prove unreliable, implement a platform-channel fallback that calls UIAccessibility.post(notification:) on iOS and AccessibilityManager.sendAccessibilityEvent() on Android directly, bypassing Flutter's abstraction.

medium impact low prob integration

The org-configurable threshold must be available at form-render time. If the threshold configuration is fetched asynchronously and not cached, the indicator may briefly show the wrong state (e.g., 'optional' before the threshold loads), confusing users and potentially allowing invalid submissions.

Mitigation & Contingency

Mitigation: Ensure the receipt threshold validator loads and caches the org configuration at app startup or organization selection time, not lazily on form open. Use a loading state in the indicator widget rather than defaulting to 'optional' while configuration is pending.

Contingency: If startup caching is not feasible, treat an unknown threshold as 'receipt required' (fail safe) and surface a clear loading indicator until the configuration resolves, preventing invalid submissions while the config loads.