From 0d4476847c405ac7056e07cb822cad41ff4838be Mon Sep 17 00:00:00 2001 From: Ayush Barnwal Date: Tue, 9 Dec 2025 21:10:08 +0530 Subject: [PATCH 1/4] Add Semantics labels to loading indicators and update related tests --- lib/widgets/action_sheet.dart | 10 +- lib/widgets/home.dart | 7 +- lib/widgets/message_list.dart | 23 ++- lib/widgets/topic_list.dart | 12 +- test/widgets/home_test.dart | 225 +++++++++++++++++----------- test/widgets/message_list_test.dart | 38 +++-- test/widgets/topic_list_test.dart | 28 +++- 7 files changed, 229 insertions(+), 114 deletions(-) diff --git a/lib/widgets/action_sheet.dart b/lib/widgets/action_sheet.dart index c56e41d5c2..eeb136f489 100644 --- a/lib/widgets/action_sheet.dart +++ b/lib/widgets/action_sheet.dart @@ -255,8 +255,14 @@ class BottomSheetEmptyContentPlaceholder extends StatelessWidget { final designVariables = DesignVariables.of(context); final child = loading - ? CircularProgressIndicator() - : Text( + ? Semantics( + label: 'Loading…', + textDirection: Directionality.of(context), + liveRegion: true, + focusable: true, + child: CircularProgressIndicator(), + ) + : Text( textAlign: TextAlign.center, style: TextStyle( color: designVariables.labelSearchPrompt, diff --git a/lib/widgets/home.dart b/lib/widgets/home.dart index c74da27a86..4f4c54f70f 100644 --- a/lib/widgets/home.dart +++ b/lib/widgets/home.dart @@ -246,7 +246,12 @@ class _LoadingPlaceholderPageState extends State<_LoadingPlaceholderPage> { child: Column( mainAxisSize: MainAxisSize.min, children: [ - const CircularProgressIndicator(), + Semantics( + label: 'Loading…', + textDirection: Directionality.of(context), + liveRegion: true, + child:const CircularProgressIndicator(), + ), Visibility( visible: showTryAnotherAccount, maintainSize: true, diff --git a/lib/widgets/message_list.dart b/lib/widgets/message_list.dart index 7ff3d395b8..8318b425b4 100644 --- a/lib/widgets/message_list.dart +++ b/lib/widgets/message_list.dart @@ -1029,7 +1029,19 @@ class _MessageListState extends State with PerAccountStoreAwareStat Widget build(BuildContext context) { final zulipLocalizations = ZulipLocalizations.of(context); - if (!model.fetched) return const Center(child: CircularProgressIndicator()); + if (!model.fetched) { + return Center( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 16.0), + child: Semantics( + label: 'Loading more messages', + textDirection: Directionality.of(context), + liveRegion: true, + child: const CircularProgressIndicator(), + ), + ), + ); + } if (model.items.isEmpty && model.haveNewest && model.haveOldest) { final String header; @@ -1283,10 +1295,15 @@ class _MessageListLoadingMore extends StatelessWidget { @override Widget build(BuildContext context) { - return const Center( + return Center( child: Padding( padding: EdgeInsets.symmetric(vertical: 16.0), - child: CircularProgressIndicator())); // TODO perhaps a different indicator + child: Semantics( + textDirection: Directionality.of(context), + label: 'Loading more messages', + liveRegion: true, + child: CircularProgressIndicator(), + ))); // TODO perhaps a different indicator } } diff --git a/lib/widgets/topic_list.dart b/lib/widgets/topic_list.dart index 846f2202f7..6775d5863f 100644 --- a/lib/widgets/topic_list.dart +++ b/lib/widgets/topic_list.dart @@ -166,7 +166,17 @@ class _TopicListState extends State<_TopicList> with PerAccountStoreAwareStateMi @override Widget build(BuildContext context) { if (lastFetchedTopics == null) { - return const Center(child: CircularProgressIndicator()); + return Center( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 16.0), + child: Semantics( + textDirection: Directionality.of(context), + label: 'Loading…', // plain string (not localized) + liveRegion: true, + child: CircularProgressIndicator(), + ), + ), + ); } // TODO(design) handle the rare case when `lastFetchedTopics` is empty diff --git a/test/widgets/home_test.dart b/test/widgets/home_test.dart index 239449c990..d30ac94b82 100644 --- a/test/widgets/home_test.dart +++ b/test/widgets/home_test.dart @@ -397,115 +397,164 @@ void main () { } testWidgets('smoke', (tester) async { - addTearDown(testBinding.reset); - testBinding.globalStore.loadPerAccountDuration = loadPerAccountDuration; - await prepare(tester); - await tester.pump(loadPerAccountDuration); - checkOnHomePage(tester, expectedAccount: eg.selfAccount); + final SemanticsHandle semanticsHandle = tester.ensureSemantics(); + try { + addTearDown(testBinding.reset); + testBinding.globalStore.loadPerAccountDuration = loadPerAccountDuration; + await prepare(tester); + + // While loading, the loading page should be shown and have the semantics label. + checkOnLoadingPage(); + expect(find.bySemanticsLabel('Loading…'), findsOneWidget); + + await tester.pump(loadPerAccountDuration); + checkOnHomePage(tester, expectedAccount: eg.selfAccount); + } finally { + semanticsHandle.dispose(); + } }); testWidgets('"Try another account" button appears after timeout', (tester) async { - addTearDown(testBinding.reset); - testBinding.globalStore.loadPerAccountDuration = loadPerAccountDuration; - await prepare(tester); - checkOnLoadingPage(); - check(find.text('Try another account').hitTestable()).findsNothing(); - - await tester.pump(kTryAnotherAccountWaitPeriod); - checkOnLoadingPage(); - check(find.text('Try another account').hitTestable()).findsOne(); - - await tester.pump(loadPerAccountDuration); - checkOnHomePage(tester, expectedAccount: eg.selfAccount); + final SemanticsHandle semanticsHandle = tester.ensureSemantics(); + try { + addTearDown(testBinding.reset); + testBinding.globalStore.loadPerAccountDuration = loadPerAccountDuration; + await prepare(tester); + checkOnLoadingPage(); + // No try-another button immediately. + check(find.text('Try another account').hitTestable()).findsNothing(); + + // The loading semantics should already be present. + expect(find.bySemanticsLabel('Loading…'), findsOneWidget); + + await tester.pump(kTryAnotherAccountWaitPeriod); + checkOnLoadingPage(); + check(find.text('Try another account').hitTestable()).findsOne(); + + await tester.pump(loadPerAccountDuration); + checkOnHomePage(tester, expectedAccount: eg.selfAccount); + } finally { + semanticsHandle.dispose(); + } }); testWidgets('while loading, go back from ChooseAccountPage', (tester) async { - testBinding.globalStore.loadPerAccountDuration = loadPerAccountDuration; - await prepare(tester); - await tester.pump(kTryAnotherAccountWaitPeriod); - await tapTryAnotherAccount(tester); - - lastPoppedRoute = null; - await tester.tap(find.byType(BackButton)); - await tester.pump(); - check(lastPoppedRoute).isA().page.isA(); - await tester.pump( - (lastPoppedRoute as TransitionRoute).reverseTransitionDuration - // TODO not sure why a 1ms fudge is needed; investigate. - + Duration(milliseconds: 1)); - checkOnLoadingPage(); - - await tester.pump(loadPerAccountDuration); - checkOnHomePage(tester, expectedAccount: eg.selfAccount); + final SemanticsHandle semanticsHandle = tester.ensureSemantics(); + try { + testBinding.globalStore.loadPerAccountDuration = loadPerAccountDuration; + await prepare(tester); + await tester.pump(kTryAnotherAccountWaitPeriod); + await tapTryAnotherAccount(tester); + + lastPoppedRoute = null; + await tester.tap(find.byType(BackButton)); + await tester.pump(); + check(lastPoppedRoute).isA().page.isA(); + await tester.pump( + (lastPoppedRoute as TransitionRoute).reverseTransitionDuration + // TODO not sure why a 1ms fudge is needed; investigate. + + Duration(milliseconds: 1)); + checkOnLoadingPage(); + + // Semantics check: loading label present + expect(find.bySemanticsLabel('Loading…'), findsOneWidget); + + await tester.pump(loadPerAccountDuration); + checkOnHomePage(tester, expectedAccount: eg.selfAccount); + } finally { + semanticsHandle.dispose(); + } }); testWidgets('while loading, choose a different account', (tester) async { - testBinding.globalStore.loadPerAccountDuration = loadPerAccountDuration; - await prepare(tester); - await tester.pump(kTryAnotherAccountWaitPeriod); - await tapTryAnotherAccount(tester); - - testBinding.globalStore.loadPerAccountDuration = loadPerAccountDuration * 2; - await chooseAccountWithEmail(tester, eg.otherAccount.email); - - await tester.pump(loadPerAccountDuration); - // The second loadPerAccount is still pending. - checkOnLoadingPage(); - - await tester.pump(loadPerAccountDuration); - // The second loadPerAccount finished. - checkOnHomePage(tester, expectedAccount: eg.otherAccount); + final SemanticsHandle semanticsHandle = tester.ensureSemantics(); + try { + testBinding.globalStore.loadPerAccountDuration = loadPerAccountDuration; + await prepare(tester); + await tester.pump(kTryAnotherAccountWaitPeriod); + await tapTryAnotherAccount(tester); + + testBinding.globalStore.loadPerAccountDuration = loadPerAccountDuration * 2; + await chooseAccountWithEmail(tester, eg.otherAccount.email); + + // While the second account is still loading, we should be on a loading page. + await tester.pump(loadPerAccountDuration); + checkOnLoadingPage(); + expect(find.bySemanticsLabel('Loading…'), findsOneWidget); + + await tester.pump(loadPerAccountDuration); + // The second load finished. + checkOnHomePage(tester, expectedAccount: eg.otherAccount); + } finally { + semanticsHandle.dispose(); + } }); testWidgets('while loading, choosing an account disallows going back', (tester) async { - testBinding.globalStore.loadPerAccountDuration = loadPerAccountDuration; - await prepare(tester); - await tester.pump(kTryAnotherAccountWaitPeriod); - await tapTryAnotherAccount(tester); - - // While still loading, choose a different account. - await chooseAccountWithEmail(tester, eg.otherAccount.email); - - // User cannot go back because the navigator stack - // was cleared after choosing an account. - check(getRouteOf(tester, find.byType(CircularProgressIndicator))) - .isNotNull().isFirst.isTrue(); - - await tester.pump(loadPerAccountDuration); // wait for loadPerAccount - checkOnHomePage(tester, expectedAccount: eg.otherAccount); + final SemanticsHandle semanticsHandle = tester.ensureSemantics(); + try { + testBinding.globalStore.loadPerAccountDuration = loadPerAccountDuration; + await prepare(tester); + await tester.pump(kTryAnotherAccountWaitPeriod); + await tapTryAnotherAccount(tester); + + // While still loading, choose a different account. + await chooseAccountWithEmail(tester, eg.otherAccount.email); + + // User cannot go back because the navigator stack was cleared after choosing an account. + check(getRouteOf(tester, find.byType(CircularProgressIndicator))) + .isNotNull().isFirst.isTrue(); + + // Semantics check: ensure loading label present and first route is our account route. + expect(find.bySemanticsLabel('Loading…'), findsOneWidget); + + await tester.pump(loadPerAccountDuration); // wait for loadPerAccount + checkOnHomePage(tester, expectedAccount: eg.otherAccount); + } finally { + semanticsHandle.dispose(); + } }); testWidgets('while loading, go to nested levels of ChooseAccountPage', (tester) async { - testBinding.globalStore.loadPerAccountDuration = loadPerAccountDuration; - final thirdAccount = eg.account(user: eg.thirdUser); - await testBinding.globalStore.add(thirdAccount, eg.initialSnapshot( - realmUsers: [eg.thirdUser])); - await prepare(tester); - - await tester.pump(kTryAnotherAccountWaitPeriod); - // While still loading the first account, choose a different account. - await tapTryAnotherAccount(tester); - await chooseAccountWithEmail(tester, eg.otherAccount.email); - // User cannot go back because the navigator stack - // was cleared after choosing an account. - check(getRouteOf(tester, find.byType(CircularProgressIndicator))) - .isA() + final SemanticsHandle semanticsHandle = tester.ensureSemantics(); + try { + testBinding.globalStore.loadPerAccountDuration = loadPerAccountDuration; + final thirdAccount = eg.account(user: eg.thirdUser); + await testBinding.globalStore.add( + thirdAccount, + eg.initialSnapshot(realmUsers: [eg.thirdUser]), + ); + await prepare(tester); + + await tester.pump(kTryAnotherAccountWaitPeriod); + + // While loading the first account, navigate to ChooseAccountPage + // and select a different account. + await tapTryAnotherAccount(tester); + await chooseAccountWithEmail(tester, eg.otherAccount.email); + check(getRouteOf(tester, find.byType(CircularProgressIndicator))) + .isA() ..isFirst.isTrue() ..accountId.equals(eg.otherAccount.id); - await tester.pump(kTryAnotherAccountWaitPeriod); - // While still loading the second account, choose a different account. - await tapTryAnotherAccount(tester); - await chooseAccountWithEmail(tester, thirdAccount.email); - // User cannot go back because the navigator stack - // was cleared after choosing an account. - check(getRouteOf(tester, find.byType(CircularProgressIndicator))) - .isA() + // Semantics: loading label should appear during the account switch. + expect(find.bySemanticsLabel('Loading…'), findsOneWidget); + + await tester.pump(kTryAnotherAccountWaitPeriod); + + // While loading the second account, navigate again and select a third account. + await tapTryAnotherAccount(tester); + await chooseAccountWithEmail(tester, thirdAccount.email); + check(getRouteOf(tester, find.byType(CircularProgressIndicator))) + .isA() ..isFirst.isTrue() ..accountId.equals(thirdAccount.id); - await tester.pump(loadPerAccountDuration); // wait for loadPerAccount - checkOnHomePage(tester, expectedAccount: thirdAccount); + await tester.pump(loadPerAccountDuration); // Finish loading the third account. + checkOnHomePage(tester, expectedAccount: thirdAccount); + } finally { + semanticsHandle.dispose(); + } }); testWidgets('after finishing loading, go back from ChooseAccountPage', (tester) async { diff --git a/test/widgets/message_list_test.dart b/test/widgets/message_list_test.dart index 89b2a31bd8..2f20edb944 100644 --- a/test/widgets/message_list_test.dart +++ b/test/widgets/message_list_test.dart @@ -838,19 +838,29 @@ void main() { // The last message is spaced above the bottom of the viewport. check(tester.getRect(find.text('message 9'))) .bottom..isGreaterThan(400)..isLessThan(570); + + // Semantics check: ensure loading semantics label is not present + final SemanticsHandle semanticsHandle = tester.ensureSemantics(); + try { + expect(find.bySemanticsLabel('Loading more messages'), findsNothing); + } finally { + semanticsHandle.dispose(); + } }); testWidgets('loading indicator displaces spacer etc.', (tester) async { - await setupMessageListPage(tester, narrow: CombinedFeedNarrow(), - skipPumpAndSettle: true, - // TODO(#1569) fix realism of this data: foundNewest false should mean - // some messages found after anchor (and then we might need to scroll - // to cause fetching newer messages). - fetchResult: eg.nearGetMessagesResult(anchor: 1000, - foundOldest: true, foundNewest: false, - messages: List.generate(10, - (i) => eg.streamMessage(id: 100 + i, content: '

message $i

')))); - await tester.pump(); + final SemanticsHandle semanticsHandle = tester.ensureSemantics(); + try { + await setupMessageListPage(tester, narrow: CombinedFeedNarrow(), + skipPumpAndSettle: true, + // TODO(#1569) fix realism of this data: foundNewest false should mean + // some messages found after anchor (and then we might need to scroll + // to cause fetching newer messages). + fetchResult: eg.nearGetMessagesResult(anchor: 1000, + foundOldest: true, foundNewest: false, + messages: List.generate(10, + (i) => eg.streamMessage(id: 100 + i, content: '

message $i

')))); + await tester.pump(); // The message list will immediately start fetching newer messages. connection.prepare(json: eg.newerGetMessagesResult( @@ -869,7 +879,13 @@ void main() { // The last message is shortly above it; no spacer or anything else. check(tester.getRect(find.text('message 9'))) .bottom.isGreaterThan(loadingIndicatorRect.top - 36); // TODO(#1569) where's this space going? - await tester.pumpAndSettle(); + //Semantics check: ensure the loading indicator exposes the expected label + expect(find.bySemanticsLabel('Loading more messages'), findsOneWidget); + + await tester.pumpAndSettle(); + } finally { + semanticsHandle.dispose(); + } }); // TODO(#1569) test no typing status or mark-read button when not haveNewest diff --git a/test/widgets/topic_list_test.dart b/test/widgets/topic_list_test.dart index 1cd9c4bb26..b1fcf15077 100644 --- a/test/widgets/topic_list_test.dart +++ b/test/widgets/topic_list_test.dart @@ -114,14 +114,26 @@ void main() { json: GetStreamTopicsResult(topics: []).toJson(), delay: Duration(seconds: 1), ); - await tester.pumpWidget(TestZulipApp( - accountId: eg.selfAccount.id, - child: TopicListPage(streamId: channel.streamId))); - await tester.pump(); - check(find.byType(CircularProgressIndicator)).findsOne(); - - await tester.pump(Duration(seconds: 1)); - check(find.byType(CircularProgressIndicator)).findsNothing(); + // Enable semantics for this test so we can assert the accessibility label. + final SemanticsHandle semanticsHandle = tester.ensureSemantics(); + try { + await tester.pumpWidget(TestZulipApp( + accountId: eg.selfAccount.id, + child: TopicListPage(streamId: channel.streamId))); + await tester.pump(); + // Visual indicator present + check(find.byType(CircularProgressIndicator)).findsOne(); + // Semantics label should be discoverable while loading. + expect(find.bySemanticsLabel('Loading…'), findsOneWidget); + + await tester.pump(Duration(seconds: 1)); + // Visual indicator gone after load completes + check(find.byType(CircularProgressIndicator)).findsNothing(); + // Semantics label should also be gone. + expect(find.bySemanticsLabel('Loading…'), findsNothing); + } finally { + semanticsHandle.dispose(); + } }); testWidgets('fetch again when navigating away and back', (tester) async { From 03f9ba95ae2a5f0c050ef1a09eacfbf70a057a47 Mon Sep 17 00:00:00 2001 From: Ayush Barnwal Date: Wed, 10 Dec 2025 23:57:32 +0530 Subject: [PATCH 2/4] l10n: Add localized loading labels and update tests Add a localized 'loading' label in multiple widgets and apply it consistently across the UI. Update ARB files and regenerate localization outputs. Update widget tests to use the localized semantics label instead of hardcoded strings. --- assets/l10n/app_en.arb | 6 +++++- lib/generated/l10n/zulip_localizations.dart | 6 ++++++ .../l10n/zulip_localizations_ar.dart | 3 +++ .../l10n/zulip_localizations_de.dart | 3 +++ .../l10n/zulip_localizations_el.dart | 3 +++ .../l10n/zulip_localizations_en.dart | 3 +++ .../l10n/zulip_localizations_es.dart | 3 +++ .../l10n/zulip_localizations_fr.dart | 3 +++ .../l10n/zulip_localizations_he.dart | 3 +++ .../l10n/zulip_localizations_hu.dart | 3 +++ .../l10n/zulip_localizations_it.dart | 3 +++ .../l10n/zulip_localizations_ja.dart | 3 +++ .../l10n/zulip_localizations_nb.dart | 3 +++ .../l10n/zulip_localizations_pl.dart | 3 +++ .../l10n/zulip_localizations_ru.dart | 3 +++ .../l10n/zulip_localizations_sk.dart | 3 +++ .../l10n/zulip_localizations_sl.dart | 3 +++ .../l10n/zulip_localizations_uk.dart | 3 +++ .../l10n/zulip_localizations_zh.dart | 3 +++ lib/widgets/action_sheet.dart | 4 ++-- lib/widgets/home.dart | 2 +- lib/widgets/message_list.dart | 13 +++++-------- lib/widgets/topic_list.dart | 10 ++++------ test/widgets/home_test.dart | 19 +++++++++++++------ test/widgets/message_list_test.dart | 8 +++++--- test/widgets/topic_list_test.dart | 6 ++++-- 26 files changed, 96 insertions(+), 29 deletions(-) diff --git a/assets/l10n/app_en.arb b/assets/l10n/app_en.arb index 38b49c2edd..45fbf8cb53 100644 --- a/assets/l10n/app_en.arb +++ b/assets/l10n/app_en.arb @@ -1419,5 +1419,9 @@ "zulipAppTitle": "Zulip", "@zulipAppTitle": { "description": "The name of Zulip. This should be either 'Zulip' or a transliteration." - } + }, + "loading": "Loading…", + "@loading": { + "description": "Label shown for loading indicators" + } } diff --git a/lib/generated/l10n/zulip_localizations.dart b/lib/generated/l10n/zulip_localizations.dart index fe79921c07..3a574d775c 100644 --- a/lib/generated/l10n/zulip_localizations.dart +++ b/lib/generated/l10n/zulip_localizations.dart @@ -2066,6 +2066,12 @@ abstract class ZulipLocalizations { /// In en, this message translates to: /// **'Zulip'** String get zulipAppTitle; + + /// Label shown for loading indicators + /// + /// In en, this message translates to: + /// **'Loading…'** + String get loading; } class _ZulipLocalizationsDelegate diff --git a/lib/generated/l10n/zulip_localizations_ar.dart b/lib/generated/l10n/zulip_localizations_ar.dart index 5e69a181da..c985c3fead 100644 --- a/lib/generated/l10n/zulip_localizations_ar.dart +++ b/lib/generated/l10n/zulip_localizations_ar.dart @@ -1180,4 +1180,7 @@ class ZulipLocalizationsAr extends ZulipLocalizations { @override String get zulipAppTitle => 'Zulip'; + + @override + String get loading => 'Loading…'; } diff --git a/lib/generated/l10n/zulip_localizations_de.dart b/lib/generated/l10n/zulip_localizations_de.dart index 241e886e55..d5c4fafc7d 100644 --- a/lib/generated/l10n/zulip_localizations_de.dart +++ b/lib/generated/l10n/zulip_localizations_de.dart @@ -1206,4 +1206,7 @@ class ZulipLocalizationsDe extends ZulipLocalizations { @override String get zulipAppTitle => 'Zulip'; + + @override + String get loading => 'Loading…'; } diff --git a/lib/generated/l10n/zulip_localizations_el.dart b/lib/generated/l10n/zulip_localizations_el.dart index ce72dc77bf..bace5f478e 100644 --- a/lib/generated/l10n/zulip_localizations_el.dart +++ b/lib/generated/l10n/zulip_localizations_el.dart @@ -1180,4 +1180,7 @@ class ZulipLocalizationsEl extends ZulipLocalizations { @override String get zulipAppTitle => 'Zulip'; + + @override + String get loading => 'Loading…'; } diff --git a/lib/generated/l10n/zulip_localizations_en.dart b/lib/generated/l10n/zulip_localizations_en.dart index 6d10362555..4fef18eb91 100644 --- a/lib/generated/l10n/zulip_localizations_en.dart +++ b/lib/generated/l10n/zulip_localizations_en.dart @@ -1180,6 +1180,9 @@ class ZulipLocalizationsEn extends ZulipLocalizations { @override String get zulipAppTitle => 'Zulip'; + + @override + String get loading => 'Loading…'; } /// The translations for English, as used in the United Kingdom (`en_GB`). diff --git a/lib/generated/l10n/zulip_localizations_es.dart b/lib/generated/l10n/zulip_localizations_es.dart index 80098d557f..636fc966a3 100644 --- a/lib/generated/l10n/zulip_localizations_es.dart +++ b/lib/generated/l10n/zulip_localizations_es.dart @@ -1180,4 +1180,7 @@ class ZulipLocalizationsEs extends ZulipLocalizations { @override String get zulipAppTitle => 'Zulip'; + + @override + String get loading => 'Loading…'; } diff --git a/lib/generated/l10n/zulip_localizations_fr.dart b/lib/generated/l10n/zulip_localizations_fr.dart index adb41177b2..ad612e10f3 100644 --- a/lib/generated/l10n/zulip_localizations_fr.dart +++ b/lib/generated/l10n/zulip_localizations_fr.dart @@ -1196,4 +1196,7 @@ class ZulipLocalizationsFr extends ZulipLocalizations { @override String get zulipAppTitle => 'Zulip'; + + @override + String get loading => 'Loading…'; } diff --git a/lib/generated/l10n/zulip_localizations_he.dart b/lib/generated/l10n/zulip_localizations_he.dart index e59309be3e..434009df67 100644 --- a/lib/generated/l10n/zulip_localizations_he.dart +++ b/lib/generated/l10n/zulip_localizations_he.dart @@ -1180,4 +1180,7 @@ class ZulipLocalizationsHe extends ZulipLocalizations { @override String get zulipAppTitle => 'Zulip'; + + @override + String get loading => 'Loading…'; } diff --git a/lib/generated/l10n/zulip_localizations_hu.dart b/lib/generated/l10n/zulip_localizations_hu.dart index 6a3c11465c..1aab0129b3 100644 --- a/lib/generated/l10n/zulip_localizations_hu.dart +++ b/lib/generated/l10n/zulip_localizations_hu.dart @@ -1180,4 +1180,7 @@ class ZulipLocalizationsHu extends ZulipLocalizations { @override String get zulipAppTitle => 'Zulip'; + + @override + String get loading => 'Loading…'; } diff --git a/lib/generated/l10n/zulip_localizations_it.dart b/lib/generated/l10n/zulip_localizations_it.dart index cd27a1daa7..c189ef8ea2 100644 --- a/lib/generated/l10n/zulip_localizations_it.dart +++ b/lib/generated/l10n/zulip_localizations_it.dart @@ -1200,4 +1200,7 @@ class ZulipLocalizationsIt extends ZulipLocalizations { @override String get zulipAppTitle => 'Zulip'; + + @override + String get loading => 'Loading…'; } diff --git a/lib/generated/l10n/zulip_localizations_ja.dart b/lib/generated/l10n/zulip_localizations_ja.dart index d11aedf19f..02f44dc8dd 100644 --- a/lib/generated/l10n/zulip_localizations_ja.dart +++ b/lib/generated/l10n/zulip_localizations_ja.dart @@ -1154,4 +1154,7 @@ class ZulipLocalizationsJa extends ZulipLocalizations { @override String get zulipAppTitle => 'Zulip'; + + @override + String get loading => 'Loading…'; } diff --git a/lib/generated/l10n/zulip_localizations_nb.dart b/lib/generated/l10n/zulip_localizations_nb.dart index 162ca9e677..808f774bcb 100644 --- a/lib/generated/l10n/zulip_localizations_nb.dart +++ b/lib/generated/l10n/zulip_localizations_nb.dart @@ -1180,4 +1180,7 @@ class ZulipLocalizationsNb extends ZulipLocalizations { @override String get zulipAppTitle => 'Zulip'; + + @override + String get loading => 'Loading…'; } diff --git a/lib/generated/l10n/zulip_localizations_pl.dart b/lib/generated/l10n/zulip_localizations_pl.dart index 40682171b2..9319689444 100644 --- a/lib/generated/l10n/zulip_localizations_pl.dart +++ b/lib/generated/l10n/zulip_localizations_pl.dart @@ -1199,4 +1199,7 @@ class ZulipLocalizationsPl extends ZulipLocalizations { @override String get zulipAppTitle => 'Zulip'; + + @override + String get loading => 'Loading…'; } diff --git a/lib/generated/l10n/zulip_localizations_ru.dart b/lib/generated/l10n/zulip_localizations_ru.dart index 04f8903b91..43da124b26 100644 --- a/lib/generated/l10n/zulip_localizations_ru.dart +++ b/lib/generated/l10n/zulip_localizations_ru.dart @@ -1212,4 +1212,7 @@ class ZulipLocalizationsRu extends ZulipLocalizations { @override String get zulipAppTitle => 'Zulip'; + + @override + String get loading => 'Loading…'; } diff --git a/lib/generated/l10n/zulip_localizations_sk.dart b/lib/generated/l10n/zulip_localizations_sk.dart index e2dc542fc5..58bac381c2 100644 --- a/lib/generated/l10n/zulip_localizations_sk.dart +++ b/lib/generated/l10n/zulip_localizations_sk.dart @@ -1182,4 +1182,7 @@ class ZulipLocalizationsSk extends ZulipLocalizations { @override String get zulipAppTitle => 'Zulip'; + + @override + String get loading => 'Loading…'; } diff --git a/lib/generated/l10n/zulip_localizations_sl.dart b/lib/generated/l10n/zulip_localizations_sl.dart index 6f9d8343ca..713d481c78 100644 --- a/lib/generated/l10n/zulip_localizations_sl.dart +++ b/lib/generated/l10n/zulip_localizations_sl.dart @@ -1221,4 +1221,7 @@ class ZulipLocalizationsSl extends ZulipLocalizations { @override String get zulipAppTitle => 'Zulip'; + + @override + String get loading => 'Loading…'; } diff --git a/lib/generated/l10n/zulip_localizations_uk.dart b/lib/generated/l10n/zulip_localizations_uk.dart index 84df26a42e..f87868789a 100644 --- a/lib/generated/l10n/zulip_localizations_uk.dart +++ b/lib/generated/l10n/zulip_localizations_uk.dart @@ -1201,4 +1201,7 @@ class ZulipLocalizationsUk extends ZulipLocalizations { @override String get zulipAppTitle => 'Zulip'; + + @override + String get loading => 'Loading…'; } diff --git a/lib/generated/l10n/zulip_localizations_zh.dart b/lib/generated/l10n/zulip_localizations_zh.dart index b08c25004e..0f671c4730 100644 --- a/lib/generated/l10n/zulip_localizations_zh.dart +++ b/lib/generated/l10n/zulip_localizations_zh.dart @@ -1180,6 +1180,9 @@ class ZulipLocalizationsZh extends ZulipLocalizations { @override String get zulipAppTitle => 'Zulip'; + + @override + String get loading => 'Loading…'; } /// The translations for Chinese, as used in China, using the Han script (`zh_Hans_CN`). diff --git a/lib/widgets/action_sheet.dart b/lib/widgets/action_sheet.dart index eeb136f489..c2740a7dad 100644 --- a/lib/widgets/action_sheet.dart +++ b/lib/widgets/action_sheet.dart @@ -253,10 +253,10 @@ class BottomSheetEmptyContentPlaceholder extends StatelessWidget { @override Widget build(BuildContext context) { final designVariables = DesignVariables.of(context); - + final loc = ZulipLocalizations.of(context); final child = loading ? Semantics( - label: 'Loading…', + label:loc.loading , textDirection: Directionality.of(context), liveRegion: true, focusable: true, diff --git a/lib/widgets/home.dart b/lib/widgets/home.dart index 4f4c54f70f..4778fa6c11 100644 --- a/lib/widgets/home.dart +++ b/lib/widgets/home.dart @@ -247,7 +247,7 @@ class _LoadingPlaceholderPageState extends State<_LoadingPlaceholderPage> { mainAxisSize: MainAxisSize.min, children: [ Semantics( - label: 'Loading…', + label: zulipLocalizations.loading, textDirection: Directionality.of(context), liveRegion: true, child:const CircularProgressIndicator(), diff --git a/lib/widgets/message_list.dart b/lib/widgets/message_list.dart index 8318b425b4..91755eba8e 100644 --- a/lib/widgets/message_list.dart +++ b/lib/widgets/message_list.dart @@ -1034,13 +1034,10 @@ class _MessageListState extends State with PerAccountStoreAwareStat child: Padding( padding: const EdgeInsets.symmetric(vertical: 16.0), child: Semantics( - label: 'Loading more messages', + label: zulipLocalizations.loading, textDirection: Directionality.of(context), liveRegion: true, - child: const CircularProgressIndicator(), - ), - ), - ); + child: const CircularProgressIndicator()))); } if (model.items.isEmpty && model.haveNewest && model.haveOldest) { @@ -1295,15 +1292,15 @@ class _MessageListLoadingMore extends StatelessWidget { @override Widget build(BuildContext context) { + final loc = ZulipLocalizations.of(context); return Center( child: Padding( padding: EdgeInsets.symmetric(vertical: 16.0), child: Semantics( textDirection: Directionality.of(context), - label: 'Loading more messages', + label: loc.loading, liveRegion: true, - child: CircularProgressIndicator(), - ))); // TODO perhaps a different indicator + child: CircularProgressIndicator()))); // TODO perhaps a different indicator } } diff --git a/lib/widgets/topic_list.dart b/lib/widgets/topic_list.dart index 6775d5863f..3a18a77d7e 100644 --- a/lib/widgets/topic_list.dart +++ b/lib/widgets/topic_list.dart @@ -166,18 +166,16 @@ class _TopicListState extends State<_TopicList> with PerAccountStoreAwareStateMi @override Widget build(BuildContext context) { if (lastFetchedTopics == null) { + final loc = ZulipLocalizations.of(context); + return Center( child: Padding( padding: const EdgeInsets.symmetric(vertical: 16.0), child: Semantics( textDirection: Directionality.of(context), - label: 'Loading…', // plain string (not localized) + label: loc.loading, liveRegion: true, - child: CircularProgressIndicator(), - ), - ), - ); - } + child: CircularProgressIndicator(),),),);} // TODO(design) handle the rare case when `lastFetchedTopics` is empty diff --git a/test/widgets/home_test.dart b/test/widgets/home_test.dart index d30ac94b82..a9008025c6 100644 --- a/test/widgets/home_test.dart +++ b/test/widgets/home_test.dart @@ -18,6 +18,7 @@ import 'package:zulip/widgets/page.dart'; import 'package:zulip/widgets/profile.dart'; import 'package:zulip/widgets/subscription_list.dart'; import 'package:zulip/widgets/theme.dart'; +import 'package:zulip/generated/l10n/zulip_localizations.dart'; import '../api/fake_api.dart'; import '../example_data.dart' as eg; @@ -398,6 +399,7 @@ void main () { testWidgets('smoke', (tester) async { final SemanticsHandle semanticsHandle = tester.ensureSemantics(); + final loc = await ZulipLocalizations.delegate.load(const Locale('en')); try { addTearDown(testBinding.reset); testBinding.globalStore.loadPerAccountDuration = loadPerAccountDuration; @@ -405,7 +407,7 @@ void main () { // While loading, the loading page should be shown and have the semantics label. checkOnLoadingPage(); - expect(find.bySemanticsLabel('Loading…'), findsOneWidget); + expect(find.bySemanticsLabel(loc.loading), findsOneWidget); await tester.pump(loadPerAccountDuration); checkOnHomePage(tester, expectedAccount: eg.selfAccount); @@ -416,6 +418,7 @@ void main () { testWidgets('"Try another account" button appears after timeout', (tester) async { final SemanticsHandle semanticsHandle = tester.ensureSemantics(); + final loc = await ZulipLocalizations.delegate.load(const Locale('en')); try { addTearDown(testBinding.reset); testBinding.globalStore.loadPerAccountDuration = loadPerAccountDuration; @@ -425,7 +428,7 @@ void main () { check(find.text('Try another account').hitTestable()).findsNothing(); // The loading semantics should already be present. - expect(find.bySemanticsLabel('Loading…'), findsOneWidget); + expect(find.bySemanticsLabel(loc.loading), findsOneWidget); await tester.pump(kTryAnotherAccountWaitPeriod); checkOnLoadingPage(); @@ -440,6 +443,7 @@ void main () { testWidgets('while loading, go back from ChooseAccountPage', (tester) async { final SemanticsHandle semanticsHandle = tester.ensureSemantics(); + final loc = await ZulipLocalizations.delegate.load(const Locale('en')); try { testBinding.globalStore.loadPerAccountDuration = loadPerAccountDuration; await prepare(tester); @@ -457,7 +461,7 @@ void main () { checkOnLoadingPage(); // Semantics check: loading label present - expect(find.bySemanticsLabel('Loading…'), findsOneWidget); + expect(find.bySemanticsLabel(loc.loading), findsOneWidget); await tester.pump(loadPerAccountDuration); checkOnHomePage(tester, expectedAccount: eg.selfAccount); @@ -468,6 +472,7 @@ void main () { testWidgets('while loading, choose a different account', (tester) async { final SemanticsHandle semanticsHandle = tester.ensureSemantics(); + final loc = await ZulipLocalizations.delegate.load(const Locale('en')); try { testBinding.globalStore.loadPerAccountDuration = loadPerAccountDuration; await prepare(tester); @@ -480,7 +485,7 @@ void main () { // While the second account is still loading, we should be on a loading page. await tester.pump(loadPerAccountDuration); checkOnLoadingPage(); - expect(find.bySemanticsLabel('Loading…'), findsOneWidget); + expect(find.bySemanticsLabel(loc.loading), findsOneWidget); await tester.pump(loadPerAccountDuration); // The second load finished. @@ -492,6 +497,7 @@ void main () { testWidgets('while loading, choosing an account disallows going back', (tester) async { final SemanticsHandle semanticsHandle = tester.ensureSemantics(); + final loc = await ZulipLocalizations.delegate.load(const Locale('en')); try { testBinding.globalStore.loadPerAccountDuration = loadPerAccountDuration; await prepare(tester); @@ -506,7 +512,7 @@ void main () { .isNotNull().isFirst.isTrue(); // Semantics check: ensure loading label present and first route is our account route. - expect(find.bySemanticsLabel('Loading…'), findsOneWidget); + expect(find.bySemanticsLabel(loc.loading), findsOneWidget); await tester.pump(loadPerAccountDuration); // wait for loadPerAccount checkOnHomePage(tester, expectedAccount: eg.otherAccount); @@ -517,6 +523,7 @@ void main () { testWidgets('while loading, go to nested levels of ChooseAccountPage', (tester) async { final SemanticsHandle semanticsHandle = tester.ensureSemantics(); + final loc = await ZulipLocalizations.delegate.load(const Locale('en')); try { testBinding.globalStore.loadPerAccountDuration = loadPerAccountDuration; final thirdAccount = eg.account(user: eg.thirdUser); @@ -538,7 +545,7 @@ void main () { ..accountId.equals(eg.otherAccount.id); // Semantics: loading label should appear during the account switch. - expect(find.bySemanticsLabel('Loading…'), findsOneWidget); + expect(find.bySemanticsLabel(loc.loading), findsOneWidget); await tester.pump(kTryAnotherAccountWaitPeriod); diff --git a/test/widgets/message_list_test.dart b/test/widgets/message_list_test.dart index 2f20edb944..809e108109 100644 --- a/test/widgets/message_list_test.dart +++ b/test/widgets/message_list_test.dart @@ -39,6 +39,7 @@ import 'package:zulip/widgets/channel_colors.dart'; import 'package:zulip/widgets/theme.dart'; import 'package:zulip/widgets/topic_list.dart'; import 'package:zulip/widgets/user.dart'; +import 'package:zulip/generated/l10n/zulip_localizations.dart'; import '../api/fake_api.dart'; import '../example_data.dart' as eg; @@ -841,8 +842,9 @@ void main() { // Semantics check: ensure loading semantics label is not present final SemanticsHandle semanticsHandle = tester.ensureSemantics(); + final loc = await ZulipLocalizations.delegate.load(const Locale('en')); try { - expect(find.bySemanticsLabel('Loading more messages'), findsNothing); + expect(find.bySemanticsLabel(loc.loading), findsNothing); } finally { semanticsHandle.dispose(); } @@ -880,8 +882,8 @@ void main() { check(tester.getRect(find.text('message 9'))) .bottom.isGreaterThan(loadingIndicatorRect.top - 36); // TODO(#1569) where's this space going? //Semantics check: ensure the loading indicator exposes the expected label - expect(find.bySemanticsLabel('Loading more messages'), findsOneWidget); - + final loc = await ZulipLocalizations.delegate.load(const Locale('en')); + expect(find.bySemanticsLabel(loc.loading), findsOneWidget); await tester.pumpAndSettle(); } finally { semanticsHandle.dispose(); diff --git a/test/widgets/topic_list_test.dart b/test/widgets/topic_list_test.dart index b1fcf15077..34046bc443 100644 --- a/test/widgets/topic_list_test.dart +++ b/test/widgets/topic_list_test.dart @@ -12,6 +12,7 @@ import 'package:zulip/widgets/app_bar.dart'; import 'package:zulip/widgets/icons.dart'; import 'package:zulip/widgets/message_list.dart'; import 'package:zulip/widgets/topic_list.dart'; +import 'package:zulip/generated/l10n/zulip_localizations.dart'; import '../api/fake_api.dart'; import '../example_data.dart' as eg; @@ -116,6 +117,7 @@ void main() { ); // Enable semantics for this test so we can assert the accessibility label. final SemanticsHandle semanticsHandle = tester.ensureSemantics(); + final loc = await ZulipLocalizations.delegate.load(const Locale('en')); try { await tester.pumpWidget(TestZulipApp( accountId: eg.selfAccount.id, @@ -124,13 +126,13 @@ void main() { // Visual indicator present check(find.byType(CircularProgressIndicator)).findsOne(); // Semantics label should be discoverable while loading. - expect(find.bySemanticsLabel('Loading…'), findsOneWidget); + expect(find.bySemanticsLabel(loc.loading), findsOneWidget); await tester.pump(Duration(seconds: 1)); // Visual indicator gone after load completes check(find.byType(CircularProgressIndicator)).findsNothing(); // Semantics label should also be gone. - expect(find.bySemanticsLabel('Loading…'), findsNothing); + expect(find.bySemanticsLabel(loc.loading), findsNothing); } finally { semanticsHandle.dispose(); } From 09207725707d923ed7b9ebb639c6ce0368f9085f Mon Sep 17 00:00:00 2001 From: Ayush Barnwal Date: Thu, 11 Dec 2025 12:14:24 +0530 Subject: [PATCH 3/4] all requested review resolved --- assets/l10n/app_en.arb | 6 ++--- lib/generated/l10n/zulip_localizations.dart | 2 +- lib/widgets/action_sheet.dart | 11 +++----- lib/widgets/home.dart | 1 - lib/widgets/message_list.dart | 14 +++-------- lib/widgets/topic_list.dart | 10 +++----- test/widgets/home_test.dart | 28 ++++++++++----------- test/widgets/message_list_test.dart | 8 +++--- test/widgets/topic_list_test.dart | 6 ++--- 9 files changed, 37 insertions(+), 49 deletions(-) diff --git a/assets/l10n/app_en.arb b/assets/l10n/app_en.arb index 45fbf8cb53..54c82a71a3 100644 --- a/assets/l10n/app_en.arb +++ b/assets/l10n/app_en.arb @@ -1420,8 +1420,8 @@ "@zulipAppTitle": { "description": "The name of Zulip. This should be either 'Zulip' or a transliteration." }, - "loading": "Loading…", - "@loading": { - "description": "Label shown for loading indicators" + "loading": "Loading…", + "@loading": { + "description": "Semantic label for a loading indicator" } } diff --git a/lib/generated/l10n/zulip_localizations.dart b/lib/generated/l10n/zulip_localizations.dart index 3a574d775c..0693164f48 100644 --- a/lib/generated/l10n/zulip_localizations.dart +++ b/lib/generated/l10n/zulip_localizations.dart @@ -2067,7 +2067,7 @@ abstract class ZulipLocalizations { /// **'Zulip'** String get zulipAppTitle; - /// Label shown for loading indicators + /// Semantic label for a loading indicator /// /// In en, this message translates to: /// **'Loading…'** diff --git a/lib/widgets/action_sheet.dart b/lib/widgets/action_sheet.dart index c2740a7dad..b13eca493c 100644 --- a/lib/widgets/action_sheet.dart +++ b/lib/widgets/action_sheet.dart @@ -253,15 +253,12 @@ class BottomSheetEmptyContentPlaceholder extends StatelessWidget { @override Widget build(BuildContext context) { final designVariables = DesignVariables.of(context); - final loc = ZulipLocalizations.of(context); - final child = loading - ? Semantics( - label:loc.loading , - textDirection: Directionality.of(context), + final zulipLocalizations = ZulipLocalizations.of(context); + final child = loading ? Semantics( + label:zulipLocalizations.loading , liveRegion: true, focusable: true, - child: CircularProgressIndicator(), - ) + child: CircularProgressIndicator()) : Text( textAlign: TextAlign.center, style: TextStyle( diff --git a/lib/widgets/home.dart b/lib/widgets/home.dart index 4778fa6c11..7cc74e27b2 100644 --- a/lib/widgets/home.dart +++ b/lib/widgets/home.dart @@ -248,7 +248,6 @@ class _LoadingPlaceholderPageState extends State<_LoadingPlaceholderPage> { children: [ Semantics( label: zulipLocalizations.loading, - textDirection: Directionality.of(context), liveRegion: true, child:const CircularProgressIndicator(), ), diff --git a/lib/widgets/message_list.dart b/lib/widgets/message_list.dart index 91755eba8e..63ffd47f71 100644 --- a/lib/widgets/message_list.dart +++ b/lib/widgets/message_list.dart @@ -1031,13 +1031,10 @@ class _MessageListState extends State with PerAccountStoreAwareStat if (!model.fetched) { return Center( - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 16.0), child: Semantics( label: zulipLocalizations.loading, - textDirection: Directionality.of(context), liveRegion: true, - child: const CircularProgressIndicator()))); + child: const CircularProgressIndicator())); } if (model.items.isEmpty && model.haveNewest && model.haveOldest) { @@ -1292,15 +1289,12 @@ class _MessageListLoadingMore extends StatelessWidget { @override Widget build(BuildContext context) { - final loc = ZulipLocalizations.of(context); + final zulipLocalizations = ZulipLocalizations.of(context); return Center( - child: Padding( - padding: EdgeInsets.symmetric(vertical: 16.0), child: Semantics( - textDirection: Directionality.of(context), - label: loc.loading, + label: zulipLocalizations.loading, liveRegion: true, - child: CircularProgressIndicator()))); // TODO perhaps a different indicator + child: CircularProgressIndicator())); // TODO perhaps a different indicator } } diff --git a/lib/widgets/topic_list.dart b/lib/widgets/topic_list.dart index 3a18a77d7e..861cb43e98 100644 --- a/lib/widgets/topic_list.dart +++ b/lib/widgets/topic_list.dart @@ -166,16 +166,14 @@ class _TopicListState extends State<_TopicList> with PerAccountStoreAwareStateMi @override Widget build(BuildContext context) { if (lastFetchedTopics == null) { - final loc = ZulipLocalizations.of(context); + final zulipLocalizations = ZulipLocalizations.of(context); return Center( - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 16.0), child: Semantics( - textDirection: Directionality.of(context), - label: loc.loading, + label: zulipLocalizations.loading, liveRegion: true, - child: CircularProgressIndicator(),),),);} + child: CircularProgressIndicator())); + } // TODO(design) handle the rare case when `lastFetchedTopics` is empty diff --git a/test/widgets/home_test.dart b/test/widgets/home_test.dart index a9008025c6..138f051ddb 100644 --- a/test/widgets/home_test.dart +++ b/test/widgets/home_test.dart @@ -399,7 +399,7 @@ void main () { testWidgets('smoke', (tester) async { final SemanticsHandle semanticsHandle = tester.ensureSemantics(); - final loc = await ZulipLocalizations.delegate.load(const Locale('en')); + final zulipLocalizations= await ZulipLocalizations.delegate.load(const Locale('en')); try { addTearDown(testBinding.reset); testBinding.globalStore.loadPerAccountDuration = loadPerAccountDuration; @@ -407,7 +407,7 @@ void main () { // While loading, the loading page should be shown and have the semantics label. checkOnLoadingPage(); - expect(find.bySemanticsLabel(loc.loading), findsOneWidget); + expect(find.bySemanticsLabel(zulipLocalizations.loading), findsOneWidget); await tester.pump(loadPerAccountDuration); checkOnHomePage(tester, expectedAccount: eg.selfAccount); @@ -418,7 +418,7 @@ void main () { testWidgets('"Try another account" button appears after timeout', (tester) async { final SemanticsHandle semanticsHandle = tester.ensureSemantics(); - final loc = await ZulipLocalizations.delegate.load(const Locale('en')); + final zulipLocalizations = await ZulipLocalizations.delegate.load(const Locale('en')); try { addTearDown(testBinding.reset); testBinding.globalStore.loadPerAccountDuration = loadPerAccountDuration; @@ -428,7 +428,7 @@ void main () { check(find.text('Try another account').hitTestable()).findsNothing(); // The loading semantics should already be present. - expect(find.bySemanticsLabel(loc.loading), findsOneWidget); + expect(find.bySemanticsLabel(zulipLocalizations.loading), findsOneWidget); await tester.pump(kTryAnotherAccountWaitPeriod); checkOnLoadingPage(); @@ -443,7 +443,7 @@ void main () { testWidgets('while loading, go back from ChooseAccountPage', (tester) async { final SemanticsHandle semanticsHandle = tester.ensureSemantics(); - final loc = await ZulipLocalizations.delegate.load(const Locale('en')); + final zulipLocalizations = await ZulipLocalizations.delegate.load(const Locale('en')); try { testBinding.globalStore.loadPerAccountDuration = loadPerAccountDuration; await prepare(tester); @@ -456,12 +456,12 @@ void main () { check(lastPoppedRoute).isA().page.isA(); await tester.pump( (lastPoppedRoute as TransitionRoute).reverseTransitionDuration - // TODO not sure why a 1ms fudge is needed; investigate. - + Duration(milliseconds: 1)); + // TODO not sure why a 1ms fudge is needed; investigate. + + Duration(milliseconds: 1)); checkOnLoadingPage(); // Semantics check: loading label present - expect(find.bySemanticsLabel(loc.loading), findsOneWidget); + expect(find.bySemanticsLabel(zulipLocalizations.loading), findsOneWidget); await tester.pump(loadPerAccountDuration); checkOnHomePage(tester, expectedAccount: eg.selfAccount); @@ -472,7 +472,7 @@ void main () { testWidgets('while loading, choose a different account', (tester) async { final SemanticsHandle semanticsHandle = tester.ensureSemantics(); - final loc = await ZulipLocalizations.delegate.load(const Locale('en')); + final zulipLocalizations = await ZulipLocalizations.delegate.load(const Locale('en')); try { testBinding.globalStore.loadPerAccountDuration = loadPerAccountDuration; await prepare(tester); @@ -485,7 +485,7 @@ void main () { // While the second account is still loading, we should be on a loading page. await tester.pump(loadPerAccountDuration); checkOnLoadingPage(); - expect(find.bySemanticsLabel(loc.loading), findsOneWidget); + expect(find.bySemanticsLabel(zulipLocalizations.loading), findsOneWidget); await tester.pump(loadPerAccountDuration); // The second load finished. @@ -497,7 +497,7 @@ void main () { testWidgets('while loading, choosing an account disallows going back', (tester) async { final SemanticsHandle semanticsHandle = tester.ensureSemantics(); - final loc = await ZulipLocalizations.delegate.load(const Locale('en')); + final zulipLocalizations = await ZulipLocalizations.delegate.load(const Locale('en')); try { testBinding.globalStore.loadPerAccountDuration = loadPerAccountDuration; await prepare(tester); @@ -512,7 +512,7 @@ void main () { .isNotNull().isFirst.isTrue(); // Semantics check: ensure loading label present and first route is our account route. - expect(find.bySemanticsLabel(loc.loading), findsOneWidget); + expect(find.bySemanticsLabel(zulipLocalizations.loading), findsOneWidget); await tester.pump(loadPerAccountDuration); // wait for loadPerAccount checkOnHomePage(tester, expectedAccount: eg.otherAccount); @@ -523,7 +523,7 @@ void main () { testWidgets('while loading, go to nested levels of ChooseAccountPage', (tester) async { final SemanticsHandle semanticsHandle = tester.ensureSemantics(); - final loc = await ZulipLocalizations.delegate.load(const Locale('en')); + final zulipLocalizations = await ZulipLocalizations.delegate.load(const Locale('en')); try { testBinding.globalStore.loadPerAccountDuration = loadPerAccountDuration; final thirdAccount = eg.account(user: eg.thirdUser); @@ -545,7 +545,7 @@ void main () { ..accountId.equals(eg.otherAccount.id); // Semantics: loading label should appear during the account switch. - expect(find.bySemanticsLabel(loc.loading), findsOneWidget); + expect(find.bySemanticsLabel(zulipLocalizations.loading), findsOneWidget); await tester.pump(kTryAnotherAccountWaitPeriod); diff --git a/test/widgets/message_list_test.dart b/test/widgets/message_list_test.dart index 809e108109..2cb9fdd6fc 100644 --- a/test/widgets/message_list_test.dart +++ b/test/widgets/message_list_test.dart @@ -842,9 +842,9 @@ void main() { // Semantics check: ensure loading semantics label is not present final SemanticsHandle semanticsHandle = tester.ensureSemantics(); - final loc = await ZulipLocalizations.delegate.load(const Locale('en')); + final zulipLocalizations = await ZulipLocalizations.delegate.load(const Locale('en')); try { - expect(find.bySemanticsLabel(loc.loading), findsNothing); + expect(find.bySemanticsLabel(zulipLocalizations.loading), findsNothing); } finally { semanticsHandle.dispose(); } @@ -882,8 +882,8 @@ void main() { check(tester.getRect(find.text('message 9'))) .bottom.isGreaterThan(loadingIndicatorRect.top - 36); // TODO(#1569) where's this space going? //Semantics check: ensure the loading indicator exposes the expected label - final loc = await ZulipLocalizations.delegate.load(const Locale('en')); - expect(find.bySemanticsLabel(loc.loading), findsOneWidget); + final zulipLocalizations = await ZulipLocalizations.delegate.load(const Locale('en')); + expect(find.bySemanticsLabel(zulipLocalizations.loading), findsOneWidget); await tester.pumpAndSettle(); } finally { semanticsHandle.dispose(); diff --git a/test/widgets/topic_list_test.dart b/test/widgets/topic_list_test.dart index 34046bc443..ac1d6e0759 100644 --- a/test/widgets/topic_list_test.dart +++ b/test/widgets/topic_list_test.dart @@ -117,7 +117,7 @@ void main() { ); // Enable semantics for this test so we can assert the accessibility label. final SemanticsHandle semanticsHandle = tester.ensureSemantics(); - final loc = await ZulipLocalizations.delegate.load(const Locale('en')); + final zulipLocalizations = await ZulipLocalizations.delegate.load(const Locale('en')); try { await tester.pumpWidget(TestZulipApp( accountId: eg.selfAccount.id, @@ -126,13 +126,13 @@ void main() { // Visual indicator present check(find.byType(CircularProgressIndicator)).findsOne(); // Semantics label should be discoverable while loading. - expect(find.bySemanticsLabel(loc.loading), findsOneWidget); + expect(find.bySemanticsLabel(zulipLocalizations.loading), findsOneWidget); await tester.pump(Duration(seconds: 1)); // Visual indicator gone after load completes check(find.byType(CircularProgressIndicator)).findsNothing(); // Semantics label should also be gone. - expect(find.bySemanticsLabel(loc.loading), findsNothing); + expect(find.bySemanticsLabel(zulipLocalizations.loading), findsNothing); } finally { semanticsHandle.dispose(); } From 0360488a78c4aee2b2e4dd019bdac0b943e130b9 Mon Sep 17 00:00:00 2001 From: Ayush Barnwal Date: Fri, 12 Dec 2025 13:37:49 +0530 Subject: [PATCH 4/4] Resolve merge conflict with main