high priority medium complexity testing pending testing specialist Tier 6

Acceptance Criteria

BankID happy path test: starts at method selector, selects BankID, screen transitions to BankID auth screen, mock callback fires with personnummer, personnummer bottom sheet appears with masked value, user scrolls disclosure and confirms, app navigates to home screen
Vipps happy path test: same structure as BankID but via Vipps auth screen and Vipps mock callback
Biometric happy path test: starts at method selector, selects biometric, mock biometric succeeds, app navigates directly to home screen (no personnummer widget since identity already established)
Biometric fallback test: mock BiometricService returns BiometricUnsupported, biometric screen shows fallback banner, tapping CTA routes to method selector
BankID error path test: mock callback returns an auth error, BankID screen displays error state, retry button is present and functional
Each test uses a FakeSupabaseClient and FakeBankIdService / FakeVippsService — no real network calls in CI
All tests pass in CI (no platform-specific biometric hardware required) because platform dependencies are injected via interfaces
Test file is organized by flow (describe-style grouping) and each test is independently runnable without relying on prior test state
Total test suite executes in under 60 seconds on standard CI hardware

Technical Requirements

frameworks
Flutter
flutter_test
BLoC test utilities (bloc_test)
apis
FakeBankIdService interface
FakeVippsService interface
FakeBiometricService interface
FakeSupabaseClient
data models
AuthSessionState
UserIdentity
OrganizationProfile
performance requirements
Each individual test must complete within 10 seconds
Fake service responses must be synchronous or use short Futures (Duration.zero) to keep tests fast
security requirements
Test fixtures must not contain real personnummer values — use clearly fake test identifiers (e.g., '012345*****')
Fake service implementations must not make real network calls — assert no-network in CI via Flutter network override if possible
ui components
TestApp wrapper — minimal MaterialApp/GoRouter setup for integration test context
FakeBankIdService
FakeVippsService
FakeBiometricService
FakeSupabaseClient

Execution Context

Execution Tier
Tier 6

Tier 6 - 158 tasks

Can start after Tier 5 completes

Implementation Notes

Create a test/auth_flow_integration_test.dart file. Build a minimal TestApp that sets up GoRouter with the five auth routes and wraps them in the required BLoC providers injected with fake services. The hardest part is simulating the BankID/Vipps OAuth callback — implement this by having the FakeBankIdService expose a Future that the test can complete via a Completer, then call tester.pump() after completing it to trigger the state rebuild. For biometric tests, inject FakeBiometricService via the BiometricAuthBloc constructor.

For scroll-gated GDPR disclosure tests, use tester.drag() on the ScrollView to simulate scrolling to bottom before tapping confirm. Keep fake implementations in test/fakes/ directory for reuse across test files. Document the test structure in a brief inline comment block at the top of the file.

Testing Requirements

All tests are written in flutter_test (not integration_test package) to avoid device dependency and keep CI fast. Use tester.pumpWidget(TestApp(...)) pattern with dependency injection via BLoC providers. Use tester.tap(find.byType(AppButton).first) and tester.pumpAndSettle() for navigation assertions. Use expect(find.byType(HomeScreen), findsOneWidget) for final navigation assertions.

Group tests by flow using group() blocks. Use setUp() to reset fake service state before each test. Add a CI check that the test file exists and the suite passes as a required check on all PRs touching auth UI files.

Epic Risks (3)
high impact medium prob technical

BankID on mobile uses a WebView or external app redirect that has known compatibility issues with Flutter's WebView package on certain Android versions. BankID's JavaScript-heavy broker pages may also trigger CSP or mixed-content errors in a Flutter WebView, preventing the authentication flow from completing.

Mitigation & Contingency

Mitigation: Use the flutter_inappwebview package (more mature than webview_flutter for complex OAuth pages) and validate BankID WebView rendering on the broker's test environment before integrating with the service layer. Prefer external browser redirect where the broker supports it.

Contingency: If WebView approach fails for certain BankID brokers, implement the full external browser redirect + deep link callback pattern as the primary flow and treat WebView as a fallback only.

medium impact medium prob technical

The OAuth redirect flows (both Vipps and BankID) temporarily move the user outside the Flutter app into an external browser or the Vipps/BankID app. Screen reader users may lose focus context during this transition and become disoriented when the app callback returns them to the loading state, failing the WCAG 2.2 AA mandate.

Mitigation & Contingency

Mitigation: Implement explicit accessibility announcements (live region announcements) at each transition point: when launching the external flow ('Opening Vipps'), during the loading wait state ('Waiting for Vipps confirmation'), and on return ('Login successful' or 'Login failed — please try again'). Test with VoiceOver on iOS and TalkBack on Android during development.

Contingency: If OAuth transition accessibility is unresolvable on a specific platform, add an explicit accessibility user guide in the onboarding flow explaining the external app redirect behavior to set user expectations.

low impact high prob technical

Biometric UI varies significantly across devices — Face ID (iPhone), fingerprint sensor (most Android), front-facing camera biometrics (some Android), and devices with no biometrics at all. Flutter's local_auth handles the OS dialog but the surrounding UI must gracefully handle all these cases, and testing coverage for all permutations is difficult.

Mitigation & Contingency

Mitigation: Use local_auth's getAvailableBiometrics() to detect the exact biometric type and render appropriate iconography (Face ID icon vs. fingerprint icon). For devices with no biometrics, skip the biometric screen entirely and route directly to full re-authentication.

Contingency: If a specific device configuration produces unexpected local_auth behavior in production, implement a user-accessible toggle in Settings to disable biometric login entirely, routing those users to the standard BankID/Vipps flow without biometrics.