Manual screen-reader and golden-file QA pass
epic-no-access-screen-ui-task-012 — Perform a manual QA pass of the no-access-screen-widget with TalkBack (Android) and VoiceOver (iOS): confirm the live region fires on entry, the heading is announced correctly, portal link and logout button are reachable in logical order, and all labels are descriptive. Generate a golden-file test screenshot at 1.0 and 2.0 text scale for regression. Fix any issues found.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 5 - 253 tasks
Can start after Tier 4 completes
Implementation Notes
For golden file tests, pin the test to a fixed device size using tester.binding.setSurfaceSize(Size(390, 844)) and reset after the test to avoid contaminating other tests. Use a fixed ThemeData with no system font substitution to ensure deterministic rendering across machines — apply fontFamily: 'Roboto' or a custom test font via FontLoader. TalkBack QA: enable TalkBack in Android Settings > Accessibility, navigate to the no-access screen, and swipe right to move through focus. Use a single-finger double-tap to activate interactive elements.
VoiceOver QA: enable VoiceOver in iOS Settings > Accessibility, use swipe right to navigate and double-tap to activate. The logical focus order should be: (1) live region announcement, (2) page heading, (3) logo (if labelled), (4) denial message body, (5) portal link, (6) logout button. If any element is announced out of order, adjust the Semantics sortKey or widget order in the tree. Document every finding — even passing checks — in the QA markdown file so it serves as a living accessibility specification for this screen.
Testing Requirements
Golden file tests are added to the existing test file or a new test/goldens/no_access_screen_golden_test.dart. Use testWidgets with pumpWidget wrapping the widget in MaterialApp with a fixed screen size (e.g., Size(390, 844) for iPhone 14) and a fixed textScaler. Call await expectLater(find.byType(NoAccessScreenWidget), matchesGoldenFile('goldens/no_access_screen_1x.png')). Repeat for 2.0 scale.
Golden images must be committed to the repository. CI must run flutter test without --update-goldens and fail if rendered output diverges from baseline. Manual QA checklist is considered a deliverable alongside code changes.
Flutter's live region (SemanticsProperties.liveRegion) announcement may be delayed or swallowed by the OS accessibility engine if the Semantics tree is not fully built when the screen mounts, causing screen-reader users to miss the denial announcement.
Mitigation & Contingency
Mitigation: Trigger the live region announcement from a post-frame callback (WidgetsBinding.addPostFrameCallback) to ensure the Semantics tree is committed before the announcement fires. Test on both VoiceOver (iOS) and TalkBack (Android) physical devices.
Contingency: If live region timing is unreliable, fall back to using SemanticsService.announce() directly in the initState post-frame callback, which provides more deterministic announcement timing.
The organisation logo may fail to load (network error, missing asset) leaving a broken image in an otherwise functional screen, degrading the professional appearance and potentially confusing users.
Mitigation & Contingency
Mitigation: Wrap the logo widget in an error builder that renders a styled fallback (organisation name text or a generic icon) when the logo asset or network image fails to load.
Contingency: If logo loading is persistently unreliable across organisations, remove the logo from the no-access screen entirely in favour of a text-only header using the organisation's display name from the design token system.