high priority low complexity testing pending testing specialist Tier 2

Acceptance Criteria

Test simulates a horizontal drag gesture (left or right) and asserts that the current route does not change
Test simulates a vertical drag and asserts the scroll position changes (scrollOffset increases or decreases)
Test verifies that SemanticsAction.scrollUp and SemanticsAction.scrollDown are present in the Semantics tree
Test verifies that overscroll beyond content bounds does not produce negative scroll positions (clamped at 0 and maxScrollExtent)
Test verifies VerticalScrollContainer does not respond to horizontal scroll physics
AccessibilityAuditRunner passes on the rendered widget with zero violations
Tests are deterministic and do not depend on frame timing beyond explicit pump calls

Technical Requirements

frameworks
Flutter
flutter_test
performance requirements
Tests complete in under 10 seconds per case
ui components
VerticalScrollContainer (component under test)
AccessibilityAuditRunner

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Wrap VerticalScrollContainer in a constrained SizedBox to ensure it has a measurable viewport. Attach a ScrollController to the container for test assertions on scroll position. Use a NavigatorObserver with a pop/push counter to verify no navigation occurs during horizontal drag. The horizontal gesture test is the most important: simulate a full drag (press, move dx=200, release) and assert the navigator pop count remains 0 and the route name is unchanged.

For scroll semantics, ensure the widget has semanticsIdentifier or use find.bySemanticsLabel. Note: overscroll clamp behavior depends on ScrollPhysics — ensure VerticalScrollContainer uses ClampingScrollPhysics or equivalent, not BouncingScrollPhysics, unless the design spec requires bounce.

Testing Requirements

Widget tests using WidgetTester. Use tester.drag() with an Offset(dx, 0) to simulate horizontal drag and assert no navigation occurs using a NavigatorObserver. Use tester.drag() with Offset(0, -dy) to simulate vertical scroll and assert scrollController.offset changes. Use tester.getSemantics() to find the scroll node and assert scrollUp/scrollDown actions are present.

For the overscroll clamp test, drag beyond bounds and assert scrollController.position.pixels stays within [0, maxScrollExtent]. Pump sufficient frames after each gesture. Run AccessibilityAuditRunner.audit(tester) at the end.

Component
Vertical Scroll Container
ui low
Epic Risks (3)
high impact high prob technical

Flutter's ModalBottomSheet and showDialog do not automatically confine VoiceOver or TalkBack focus to the modal's subtree on all platform versions. Background content may remain reachable by screen readers, confusing users and violating WCAG 2.2 criterion 1.3.1.

Mitigation & Contingency

Mitigation: Wrap modal content in an ExcludeSemantics or BlockSemantics widget for background content. Use a Semantics node with liveRegion on the modal container and manually request focus via FocusScope after the modal animation completes. Test on both iOS (VoiceOver) and Android (TalkBack) during widget development.

Contingency: If platform-level focus trapping is unreliable, implement a custom modal wrapper widget that uses a FocusTrap widget (available in Flutter's internal tooling) and an Overlay entry with semantics blocking on the dimmed background layer.

medium impact medium prob technical

On iOS, the system-level swipe-back gesture (UINavigationController) can bypass PopScope and GoRouter's gesture suppression, meaning users can still accidentally dismiss screens via swipe even after the component is implemented. This breaks the gesture-free contract for motor-impaired users.

Mitigation & Contingency

Mitigation: Set popGestureEnabled: false in GoRouter route configurations where swipe-back is suppressed. Test specifically against Flutter's CupertinoPageRoute, which respects this flag, and verify that GoRouter generates Cupertino routes on iOS rather than Material routes with gesture enabled.

Contingency: If go_router's popGestureEnabled flag does not propagate correctly, wrap affected routes in a WillPopScope replacement (PopScope with canPop: false) and file a bug with the go_router maintainers. Document the workaround in the navigation-route-config component for future maintainers.

medium impact medium prob scope

The feature description implies migrating all existing ModalBottomSheet and dialog call sites across the app to use the new accessible helpers, which is a cross-cutting change. Scope underestimation could mean the epic finishes the new components but leaves many call sites un-migrated, leaving the accessibility promise partially broken.

Mitigation & Contingency

Mitigation: Audit all existing modal call sites at the start of the epic (grep for showModalBottomSheet, showDialog, showCupertinoDialog) and add the count to the task list. Treat migration as explicit tasks, not an implied post-step.

Contingency: If migration scope grows beyond the epic's estimate, create a follow-up tech-debt epic scoped only to call-site migration, and gate the release on at minimum all flows used by the accessibility user-story acceptance criteria being migrated.