high priority medium complexity testing pending testing specialist Tier 4

Acceptance Criteria

Test: empty state — submit button finder shows widget with enabled: false (or onPressed: null)
Test: valid email + valid password entered — submit button is enabled (onPressed is non-null)
Test: invalid email format entered — inline error text 'Enter a valid email address' (localised) is visible below email field
Test: password too short entered — inline error text for password validation is visible below password field
Test: BLoC emits loading state — both email and password TextFields have enabled: false
Test: BLoC emits auth error state — ErrorBanner widget is visible with the correct error message text
Test: BLoC emits auth error state — Semantics node with liveRegion:true exists in the tree
Test: dismiss error banner — tapping dismiss causes ErrorBanner to be absent from tree
All tests run in isolation with no real network calls or Supabase dependencies
Test coverage for LoginForm widget reaches ≥90% line coverage
All test descriptions are in English and clearly describe the scenario being tested
Tests pass on both CI (headless) and local development environments

Technical Requirements

frameworks
Flutter
flutter_test
bloc_test
mocktail or mockito
data models
LoginFormState (all variants: initial, validating, loading, success, failure)
LoginFormEvent (emailChanged, passwordChanged, submitted, errorDismissed)
performance requirements
Each test completes within 2 seconds
No real timers or async gaps — use tester.pumpAndSettle() appropriately
security requirements
No real credentials used in tests — use clearly fake values like 'test@example.com' / 'TestPass123!'
ui components
LoginForm widget under test
MockLoginFormBLoC (via bloc_test MockBloc)
BlocProvider wrapping LoginForm in test harness

Execution Context

Execution Tier
Tier 4

Tier 4 - 323 tasks

Can start after Tier 3 completes

Implementation Notes

Structure tests as: setUp creates MockBloc and pumps LoginForm wrapped in BlocProvider and MaterialApp (for localisation). Each test case seeds the desired state using when(mockBloc.state).thenReturn(desiredState) or whenListen. For the loading state test, verify inputs are disabled by checking TextFormField's enabled property via tester.widget. For the error banner test, pump after BLoC state emission using tester.pump() (not pumpAndSettle, as no animations should be required for the test assertion).

Include a pumpWidget helper in a setUp to reduce boilerplate. The bloc_test MockBloc approach avoids real BLoC logic — test only the UI reaction to states, not BLoC business logic (that belongs in separate BLoC unit tests). Keep test file under 250 lines by extracting widget pump helpers into a local testHelpers function.

Testing Requirements

All tests are widget tests in test/widgets/login_form_test.dart. Each state scenario is a separate test case within a group('LoginForm', ...) block. Use MockBloc from bloc_test. Seed the mock with whenListen to emit specific states.

Use find.text() for error messages, find.byType(AppButton) with tester.widget().onPressed for enabled state, find.byType(ErrorBanner) for banner visibility. Use tester.ensureSemantics() for semantics assertions. Do NOT use integration_test package here — pure widget tests only. Run flutter test test/widgets/login_form_test.dart --coverage and attach coverage report.

Component
Login Form
ui medium
Epic Risks (3)
high impact medium prob scope

Automated accessibility checks (e.g., flutter_accessibility_service) may pass while manual VoiceOver/TalkBack testing reveals focus-order issues, missing semantic roles, or live region announcements that fire too early or not at all. Discovering these late risks delaying the MVP release.

Mitigation & Contingency

Mitigation: Conduct manual VoiceOver and TalkBack testing on physical devices at the end of every sprint, not only at release. Define accessibility acceptance criteria per component and include them in the DoD. Use Semantics widgets explicitly rather than relying on implicit semantics from Flutter's default widgets for all interactive elements.

Contingency: Maintain a prioritized accessibility bug backlog separate from the main backlog. If critical VoiceOver issues are found close to release, create an explicit accessibility hotfix sprint before TestFlight distribution to Blindeforbundet testers.

medium impact medium prob technical

Keyboard height varies significantly between iOS and Android, between device sizes (iPhone SE vs iPad), and with third-party keyboards. The KeyboardAwareLayout may not correctly adjust scroll offset in all combinations, causing input fields to remain hidden behind the keyboard on certain device/keyboard configurations.

Mitigation & Contingency

Mitigation: Test on a matrix of devices including iPhone SE (small viewport), a mid-size Android phone, and a tablet. Implement the layout using MediaQuery.viewInsets.bottom rather than a fixed padding value to correctly respond to any keyboard height. Include edge cases for floating keyboards on iPads.

Contingency: If device-specific issues are found after release, implement a bottom-padding fallback using BottomPadding inset and allow users to manually scroll. Log affected device/OS combinations for targeted fixes.

high impact low prob dependency

If the design token system's colour palette is updated without re-running contrast validation, form field labels, error messages, or placeholder text could fall below the WCAG 2.2 AA 4.5:1 ratio, causing a compliance regression.

Mitigation & Contingency

Mitigation: Integrate a contrast ratio validator (e.g., a CI lint step using the contrast-ratio-validator component) that checks all colour pairs used in the login form on every pull request. Document which token pairs are used for labels, errors, and backgrounds in the login form.

Contingency: If a contrast regression is detected post-merge, hot-patch the affected design token value. Do not ship a TestFlight build with known WCAG AA failures to Blindeforbundet testers.