Write Widget Tests for Persistent Back Button
epic-navigation-and-gesture-accessibility-ui-components-task-016 — Write flutter_test widget tests for PersistentBackButton covering: button visible on non-root routes, hidden on root routes, tap triggers GoRouter pop, Semantics label is 'Go back', touch target ≥ 44×44dp, and tooltip displays on long press. Run AccessibilityAuditRunner assertions on all test cases.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 4 - 323 tasks
Can start after Tier 3 completes
Implementation Notes
GoRouter integration in tests requires wrapping the widget in a MaterialApp.router with a controlled GoRouter instance. Create a helper that builds a two-route GoRouter (root and a child) so tests can push/pop routes predictably. For the hidden-at-root test, start at the root route and assert the button is not rendered or is Offstage. For visibility tests, push a second route and assert the button becomes visible.
Tooltip test: pump two frames after longPress to allow the tooltip overlay to appear before asserting. Avoid testing GoRouter's internal pop behavior — only assert that the correct method was called (use a spy or check the route stack depth after tap).
Testing Requirements
Widget tests using WidgetTester. Set up a GoRouter test harness with at least two routes to simulate root and non-root states. Use tester.longPress() to trigger the tooltip and pump frames before asserting tooltip visibility. Use tester.getSemantics() for label assertions.
Use a GoRouter mock or a real GoRouter with a test-controlled route stack. Run AccessibilityAuditRunner.audit(tester) after each distinct UI state (visible and hidden). Minimum 6 test cases as described in acceptance criteria.
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.
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.
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.