high priority medium complexity testing pending testing specialist Tier 2

Acceptance Criteria

Integration test suite tagged 'privacy_guard' covers all four SensitiveFieldPrivacyGuard scenarios: first-access dialog, dismissal, session suppression, and post-logout suppression reset
Test 1 (first access): navigating TalkBack/VoiceOver focus to a sensitive field triggers the privacy warning dialog within one focus event; dialog title and body text are verified against design copy
Test 2 (dismissal): tapping the 'Dismiss' button on the dialog closes it; the sensitive field value becomes readable by the screen reader immediately after dismissal
Test 3 (session suppression): after dismissing the dialog once, navigating focus to a second sensitive field within the same session does NOT trigger the dialog again
Test 4 (session suppression across field types): suppression applies across all three field types (assignment details, phone number, peer mentor name) — not just the originally accessed field type
Test 5 (logout reset): after a simulated logout (session cleared), navigating to a sensitive field triggers the privacy warning dialog again as if it were first access
Test 6 (no screen reader): when the device accessibility services report no active screen reader, no privacy dialog is shown regardless of navigation method
Each test uses flutter_test's WidgetTester to simulate focus events rather than actual TalkBack/VoiceOver, ensuring headless CI execution
All tests are deterministic and do not depend on device state or real Supabase sessions

Technical Requirements

frameworks
Flutter
flutter_test
apis
Flutter SemanticsController
Flutter AccessibilityFeatures (to mock screen reader active state)
SensitiveFieldPrivacyGuard internal API
Session management service (internal)
data models
Session
SensitiveFieldType enum
performance requirements
Each individual test must complete in under 5 seconds on a standard CI runner
security requirements
Tests must use mock/stub sensitive field values — no real PII (names, phone numbers, assignment data) in test fixtures
Test session state must be isolated per test — no shared mutable state between test cases
ui components
SensitiveFieldPrivacyGuard widget
PrivacyWarningDialog widget
Sensitive field display widgets

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

The key challenge is simulating screen reader focus events headlessly. Use tester.sendSemanticsAction(semanticsId, SemanticsAction.focus) to trigger focus on a specific semantics node rather than relying on real assistive technology. To mock AccessibilityFeatures (which reports whether a screen reader is active), inject a fake MediaQueryData with accessibilityFeatures overridden — wrap the widget under test in MediaQuery(data: MediaQueryData(accessibilityFeatures: FakeAccessibilityFeatures(accessibleNavigation: true)), child: ...). Session suppression state in SensitiveFieldPrivacyGuard is likely stored in a ChangeNotifier or Riverpod provider — ensure the test harness resets this state between tests using addTearDown.

Structure test helpers as a PrivacyGuardTestHelper class with methods: pumpWithScreenReader(), pumpWithoutScreenReader(), simulateLogout(), navigateToSensitiveField(SensitiveFieldType). This avoids boilerplate duplication across the six test cases.

Testing Requirements

All tests are Flutter integration tests using flutter_test's WidgetTester (not flutter_driver). Use a mock AccessibilityService that can be toggled to simulate screen reader active/inactive states. Use a mock SessionService that can be reset between tests to simulate logout. Tag all tests with 'privacy_guard' and 'accessibility' for selective CI execution.

Arrange each test with: (1) pump the widget under test with injected mock services, (2) simulate focus event via tester.sendSemanticsAction or tester.tap on the focusable node, (3) assert dialog presence/absence via findsOneWidget/findsNothing. Verify dialog copy text exactly — do not use partial string matches — to catch unintended copy changes. Run the full suite in both ltr and rtl layout directions to ensure the dialog renders correctly for both.

Component
Sensitive Field Privacy Guard
service high
Epic Risks (3)
high impact high prob integration

Flutter does not natively enforce a focus trap within a bottom sheet or modal dialog in the semantic tree — VoiceOver and TalkBack can navigate outside the sheet to background content. Implementing a reliable focus trap requires overriding the semantic tree, which may conflict with the existing modal helper infrastructure in the app and require changes to shared components beyond this feature's scope.

Mitigation & Contingency

Mitigation: Prototype the focus trap on the first modal sheet implementation before building the remaining sheets. Evaluate Flutter's ExcludeSemantics and BlockSemantics widgets as the trap mechanism, and coordinate with the team owning the shared modal helpers to agree on a non-breaking integration point before writing production code.

Contingency: If a complete semantic focus trap cannot be implemented without breaking existing modal patterns, implement a partial solution using FocusScope with autofocus on the modal's first element and a prominent 'Return to main content' semantic action, documenting the deviation from WCAG 2.4.3 with a scheduled remediation item.

high impact medium prob technical

The activity wizard uses BLoC state management and the UI rebuilds the entire step widget subtree on transition. If the semantic tree is traversed by VoiceOver before the build cycle settles, focus may land on a stale or partially rendered step, causing the wrong step label or progress value to be announced. This is particularly problematic for blind users who cannot visually verify the announcement against the screen.

Mitigation & Contingency

Mitigation: Coordinate ActivityWizardStepSemantics with FocusManagementService (from the core services epic) to delay focus placement until the post-build callback confirms the new step's semantic tree is complete. Write integration tests using the AccessibilityTestHarness that assert the full announcement sequence across all five wizard steps.

Contingency: If post-build focus delay is insufficient due to async BLoC emission timing, add an explicit semantic notification barrier in the wizard cubit that emits a 'step ready' event only after the new widget tree has been marked as built, decoupling the announcement trigger from the raw state transition.

medium impact medium prob scope

Automated WCAG contrast ratio checking on widget tree snapshots may produce false positives for gradient backgrounds, dark-mode overrides, or design token overrides that are resolved at runtime but appear as unresolvable colours at static analysis time. Excessive false positives would erode team trust in the CI gate, leading to suppression rules that also mask real violations.

Mitigation & Contingency

Mitigation: Scope the WCAGComplianceChecker to check only solid-colour backgrounds in the first iteration, explicitly excluding gradients from contrast checks with documented rationale. Design the check output to distinguish 'undetermined' (gradient/unknown) from 'fail' (solid colour below threshold) so the team can take targeted action on genuine failures only.

Contingency: If false positive rates exceed 20% of reported violations during initial CI runs, switch the CI gate from a hard build failure to a warning annotation on the pull request, combined with a mandatory manual review step, until the checker's rule set has been tuned to match actual design token values.