From eda9609b8b271662ebc2dac3cecd5e09a03c84c0 Mon Sep 17 00:00:00 2001 From: "Vaish, Ishan" Date: Fri, 19 Jun 2026 03:13:49 -0400 Subject: [PATCH 1/3] Spell out TWOW as separate letters for screen readers on homepage banner --- lib/ui/notices/notices_card.dart | 8 ++- test/ui/notices/notices_card_test.dart | 68 ++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 test/ui/notices/notices_card_test.dart diff --git a/lib/ui/notices/notices_card.dart b/lib/ui/notices/notices_card.dart index 585ac6e4b..2a7f02a96 100644 --- a/lib/ui/notices/notices_card.dart +++ b/lib/ui/notices/notices_card.dart @@ -13,6 +13,12 @@ class NoticesCard extends StatelessWidget { /// MODELS final NoticesModel notice; + // VoiceOver/TalkBack read "TWOW" as a made-up word instead of spelling it + // out, so it's spaced into individual letters for the accessibility label only. + static final RegExp _twowPattern = RegExp(r'\bTWOW\b', caseSensitive: false); + + static String _spellOutForScreenReaders(String text) => text.replaceAll(_twowPattern, 'T W O W'); + @override Widget build(BuildContext context) { return Card( @@ -32,7 +38,7 @@ class NoticesCard extends StatelessWidget { Widget buildBannerView(NoticesModel notice) { // The screen reader will read - "image - (text on the image)" return Semantics( - label: notice.title, + label: _spellOutForScreenReaders(notice.title), image: true, button: true, child: GestureDetector( diff --git a/test/ui/notices/notices_card_test.dart b/test/ui/notices/notices_card_test.dart new file mode 100644 index 000000000..dc67f4985 --- /dev/null +++ b/test/ui/notices/notices_card_test.dart @@ -0,0 +1,68 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:campus_mobile_experimental/core/models/notices.dart'; +import 'package:campus_mobile_experimental/ui/notices/notices_card.dart'; +import 'package:flutter/material.dart'; + +Finder findBannerSemantics() => + find.byWidgetPredicate((widget) => widget is Semantics && widget.properties.image == true); + +void main() { + group('NoticesCard Tests', () { + testWidgets('spells out TWOW as separate letters in the accessibility label', (WidgetTester tester) async { + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: NoticesCard( + notice: NoticesModel( + title: 'UC San Diego TWOW', + imageUrl: '', + link: '', + ), + ), + ), + ), + ); + + final semantics = tester.getSemantics(findBannerSemantics()); + expect(semantics.label, 'UC San Diego T W O W'); + }); + + testWidgets('leaves titles without TWOW unchanged', (WidgetTester tester) async { + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: NoticesCard( + notice: NoticesModel( + title: 'Welcome Week Schedule', + imageUrl: '', + link: '', + ), + ), + ), + ), + ); + + final semantics = tester.getSemantics(findBannerSemantics()); + expect(semantics.label, 'Welcome Week Schedule'); + }); + + testWidgets('does not alter words that merely contain twow as a substring', (WidgetTester tester) async { + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: NoticesCard( + notice: NoticesModel( + title: 'TWOWeek Kickoff', + imageUrl: '', + link: '', + ), + ), + ), + ), + ); + + final semantics = tester.getSemantics(findBannerSemantics()); + expect(semantics.label, 'TWOWeek Kickoff'); + }); + }); +} From a3e6b827594770e004b8b85a68fe69f08173bb7b Mon Sep 17 00:00:00 2001 From: "Vaish, Ishan" Date: Mon, 22 Jun 2026 01:25:45 -0500 Subject: [PATCH 2/3] Fix semantics setup in banner tests --- test/ui/notices/notices_card_test.dart | 34 +++++++------------------- 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/test/ui/notices/notices_card_test.dart b/test/ui/notices/notices_card_test.dart index dc67f4985..83f3134a5 100644 --- a/test/ui/notices/notices_card_test.dart +++ b/test/ui/notices/notices_card_test.dart @@ -9,17 +9,11 @@ Finder findBannerSemantics() => void main() { group('NoticesCard Tests', () { testWidgets('spells out TWOW as separate letters in the accessibility label', (WidgetTester tester) async { + final semanticsHandle = tester.ensureSemantics(); + addTearDown(semanticsHandle.dispose); await tester.pumpWidget( MaterialApp( - home: Scaffold( - body: NoticesCard( - notice: NoticesModel( - title: 'UC San Diego TWOW', - imageUrl: '', - link: '', - ), - ), - ), + home: Scaffold(body: NoticesCard(notice: NoticesModel(title: 'UC San Diego TWOW', imageUrl: '', link: ''))), ), ); @@ -28,16 +22,12 @@ void main() { }); testWidgets('leaves titles without TWOW unchanged', (WidgetTester tester) async { + final semanticsHandle = tester.ensureSemantics(); + addTearDown(semanticsHandle.dispose); await tester.pumpWidget( MaterialApp( home: Scaffold( - body: NoticesCard( - notice: NoticesModel( - title: 'Welcome Week Schedule', - imageUrl: '', - link: '', - ), - ), + body: NoticesCard(notice: NoticesModel(title: 'Welcome Week Schedule', imageUrl: '', link: '')), ), ), ); @@ -47,17 +37,11 @@ void main() { }); testWidgets('does not alter words that merely contain twow as a substring', (WidgetTester tester) async { + final semanticsHandle = tester.ensureSemantics(); + addTearDown(semanticsHandle.dispose); await tester.pumpWidget( MaterialApp( - home: Scaffold( - body: NoticesCard( - notice: NoticesModel( - title: 'TWOWeek Kickoff', - imageUrl: '', - link: '', - ), - ), - ), + home: Scaffold(body: NoticesCard(notice: NoticesModel(title: 'TWOWeek Kickoff', imageUrl: '', link: ''))), ), ); From 76f2c6934bf707f47ea1e53fcea538cfceb4c8f4 Mon Sep 17 00:00:00 2001 From: "Vaish, Ishan" Date: Mon, 22 Jun 2026 01:31:15 -0500 Subject: [PATCH 3/3] Remove banner widget test --- test/ui/notices/notices_card_test.dart | 52 -------------------------- 1 file changed, 52 deletions(-) delete mode 100644 test/ui/notices/notices_card_test.dart diff --git a/test/ui/notices/notices_card_test.dart b/test/ui/notices/notices_card_test.dart deleted file mode 100644 index 83f3134a5..000000000 --- a/test/ui/notices/notices_card_test.dart +++ /dev/null @@ -1,52 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:campus_mobile_experimental/core/models/notices.dart'; -import 'package:campus_mobile_experimental/ui/notices/notices_card.dart'; -import 'package:flutter/material.dart'; - -Finder findBannerSemantics() => - find.byWidgetPredicate((widget) => widget is Semantics && widget.properties.image == true); - -void main() { - group('NoticesCard Tests', () { - testWidgets('spells out TWOW as separate letters in the accessibility label', (WidgetTester tester) async { - final semanticsHandle = tester.ensureSemantics(); - addTearDown(semanticsHandle.dispose); - await tester.pumpWidget( - MaterialApp( - home: Scaffold(body: NoticesCard(notice: NoticesModel(title: 'UC San Diego TWOW', imageUrl: '', link: ''))), - ), - ); - - final semantics = tester.getSemantics(findBannerSemantics()); - expect(semantics.label, 'UC San Diego T W O W'); - }); - - testWidgets('leaves titles without TWOW unchanged', (WidgetTester tester) async { - final semanticsHandle = tester.ensureSemantics(); - addTearDown(semanticsHandle.dispose); - await tester.pumpWidget( - MaterialApp( - home: Scaffold( - body: NoticesCard(notice: NoticesModel(title: 'Welcome Week Schedule', imageUrl: '', link: '')), - ), - ), - ); - - final semantics = tester.getSemantics(findBannerSemantics()); - expect(semantics.label, 'Welcome Week Schedule'); - }); - - testWidgets('does not alter words that merely contain twow as a substring', (WidgetTester tester) async { - final semanticsHandle = tester.ensureSemantics(); - addTearDown(semanticsHandle.dispose); - await tester.pumpWidget( - MaterialApp( - home: Scaffold(body: NoticesCard(notice: NoticesModel(title: 'TWOWeek Kickoff', imageUrl: '', link: ''))), - ), - ); - - final semantics = tester.getSemantics(findBannerSemantics()); - expect(semantics.label, 'TWOWeek Kickoff'); - }); - }); -}