From df834f4c8b5209db02003209f26638bc36808df1 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Wed, 21 Jul 2021 21:11:08 +0530 Subject: [PATCH] test(localization): add unit tests Signed-off-by: xsahil03x --- .../test/basics_test.dart | 109 +++++++ .../test/override_test.dart | 276 ++++++++++++++++++ .../test/stream_chat_localization_test.dart | 20 -- .../test/translations_test.dart | 164 +++++++++++ 4 files changed, 549 insertions(+), 20 deletions(-) create mode 100644 packages/stream_chat_localizations/test/basics_test.dart create mode 100644 packages/stream_chat_localizations/test/override_test.dart delete mode 100644 packages/stream_chat_localizations/test/stream_chat_localization_test.dart create mode 100644 packages/stream_chat_localizations/test/translations_test.dart diff --git a/packages/stream_chat_localizations/test/basics_test.dart b/packages/stream_chat_localizations/test/basics_test.dart new file mode 100644 index 00000000..c1315a9a --- /dev/null +++ b/packages/stream_chat_localizations/test/basics_test.dart @@ -0,0 +1,109 @@ +// ignore_for_file: omit_local_variable_types + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:stream_chat_localizations/src/stream_chat_localizations.dart'; + +void main() { + testWidgets('Nested Localizations', (WidgetTester tester) async { + await tester.pumpWidget(MaterialApp( + // Creates the outer Localizations widget. + home: ListView( + children: [ + const LocalizationTracker(key: ValueKey('outer')), + Localizations( + locale: const Locale('hi'), + delegates: GlobalStreamChatLocalizations.delegates, + child: const LocalizationTracker(key: ValueKey('inner')), + ), + ], + ), + )); + + final LocalizationTrackerState outerTracker = tester.state( + find.byKey(const ValueKey('outer'), skipOffstage: false)); + expect(outerTracker.captionFontSize, 12.0); + final LocalizationTrackerState innerTracker = tester.state( + find.byKey(const ValueKey('inner'), skipOffstage: false)); + expect(innerTracker.captionFontSize, 13.0); + }); + + testWidgets( + 'Localizations is compatible with ChangeNotifier.dispose() called ' + 'during didChangeDependencies', + (WidgetTester tester) async { + // PageView calls ScrollPosition.dispose() during didChangeDependencies. + await tester.pumpWidget(MaterialApp( + supportedLocales: const [ + Locale('en', 'US'), + Locale('hi', 'IN'), + ], + localizationsDelegates: const [ + DummyLocalizations.delegate, + GlobalStreamChatLocalizations.delegate, + ], + home: PageView(), + )); + + await tester.binding.setLocale('hi', 'IN'); + await tester.pump(); + await tester.pumpWidget(Container()); + }, + ); + + testWidgets('Locale without countryCode', (WidgetTester tester) async { + // Regression test for https://github.com/flutter/flutter/pull/16782 + await tester.pumpWidget(MaterialApp( + localizationsDelegates: const >[ + GlobalStreamChatLocalizations.delegate, + ], + supportedLocales: const [ + Locale('en', 'US'), + Locale('hi'), + ], + home: Container(), + )); + + await tester.binding.setLocale('hi', ''); + await tester.pump(); + await tester.binding.setLocale('en', 'US'); + await tester.pump(); + }); +} + +/// A localizations delegate that does not contain any useful data, and is only +/// used to trigger didChangeDependencies upon locale change. +class _DummyLocalizationsDelegate + extends LocalizationsDelegate { + const _DummyLocalizationsDelegate(); + + @override + Future load(Locale locale) async => DummyLocalizations(); + + @override + bool isSupported(Locale locale) => true; + + @override + bool shouldReload(_DummyLocalizationsDelegate old) => true; +} + +class DummyLocalizations { + static const delegate = _DummyLocalizationsDelegate(); +} + +class LocalizationTracker extends StatefulWidget { + const LocalizationTracker({Key? key}) : super(key: key); + + @override + State createState() => LocalizationTrackerState(); +} + +class LocalizationTrackerState extends State { + late double captionFontSize; + + @override + Widget build(BuildContext context) { + captionFontSize = Theme.of(context).textTheme.caption!.fontSize!; + return Container(); + } +} diff --git a/packages/stream_chat_localizations/test/override_test.dart b/packages/stream_chat_localizations/test/override_test.dart new file mode 100644 index 00000000..3e134b6f --- /dev/null +++ b/packages/stream_chat_localizations/test/override_test.dart @@ -0,0 +1,276 @@ +// ignore_for_file: prefer_expression_function_bodies + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:stream_chat_flutter/stream_chat_flutter.dart'; +import 'package:stream_chat_localizations/src/stream_chat_localizations.dart'; +import 'package:flutter_test/flutter_test.dart'; + +class FooStreamChatLocalizations extends StreamChatLocalizationsEn { + FooStreamChatLocalizations( + Locale localeName, + this.launchUrlError, + ) : super(localeName: localeName.toString()); + + @override + final String launchUrlError; +} + +class FooStreamChatLocalizationsDelegate + extends LocalizationsDelegate { + const FooStreamChatLocalizationsDelegate({ + this.supportedLanguage = 'en', + this.launchUrlError = 'foo', + }); + + final String supportedLanguage; + final String launchUrlError; + + @override + bool isSupported(Locale locale) => + supportedLanguage == 'allLanguages' || + locale.languageCode == supportedLanguage; + + @override + Future load(Locale locale) => + SynchronousFuture( + FooStreamChatLocalizations(locale, launchUrlError), + ); + + @override + bool shouldReload(FooStreamChatLocalizationsDelegate old) => false; +} + +Widget buildFrame({ + Locale? locale, + Iterable delegates = + GlobalStreamChatLocalizations.delegates, + required WidgetBuilder buildContent, + LocaleResolutionCallback? localeResolutionCallback, + Iterable supportedLocales = const [ + Locale('en', 'US'), + Locale('hi', 'IN'), + ], +}) => + MaterialApp( + color: const Color(0xFFFFFFFF), + locale: locale, + supportedLocales: supportedLocales, + localizationsDelegates: delegates, + localeResolutionCallback: localeResolutionCallback, + onGenerateRoute: (RouteSettings settings) => MaterialPageRoute( + builder: (BuildContext context) => buildContent(context)), + ); + +void main() { + testWidgets( + 'Locale fallbacks', + (WidgetTester tester) async { + final Key textKey = UniqueKey(); + + await tester.pumpWidget( + buildFrame( + buildContent: (BuildContext context) => Text( + StreamChatLocalizations.of(context)!.launchUrlError, + key: textKey, + ), + ), + ); + + expect( + tester.widget(find.byKey(textKey)).data, + 'Cannot launch the url', + ); + + // Unrecognized locale falls back to 'en' + await tester.binding.setLocale('foo', 'BAR'); + await tester.pump(); + expect( + tester.widget(find.byKey(textKey)).data, + 'Cannot launch the url', + ); + + // Indian hindi locale, falls back to just 'hi' + await tester.binding.setLocale('hi', 'IN'); + await tester.pump(); + expect( + tester.widget(find.byKey(textKey)).data, + 'यूआरएल लॉन्च नहीं कर सकते', + ); + }, + ); + + testWidgets( + "Localizations.override widget tracks parent's locale", + (WidgetTester tester) async { + Widget buildLocaleFrame(Locale locale) => buildFrame( + locale: locale, + supportedLocales: [locale], + buildContent: (BuildContext context) => Localizations.override( + context: context, + child: Builder( + builder: (BuildContext context) { + // No StreamChatLocalizations are defined for the first + // Localizations ancestor, so we should get the values from + // the default one, i.e. the one created by WidgetsApp via + // the LocalizationsDelegate provided by MaterialApp. + return Text( + StreamChatLocalizations.of(context)!.launchUrlError, + ); + }, + ), + ), + ); + + await tester.pumpWidget(buildLocaleFrame(const Locale('en', 'US'))); + expect(find.text('Cannot launch the url'), findsOneWidget); + + await tester.pumpWidget(buildLocaleFrame(const Locale('hi', 'IN'))); + expect(find.text('यूआरएल लॉन्च नहीं कर सकते'), findsOneWidget); + }, + ); + + testWidgets('Localizations.override widget with hardwired locale', + (WidgetTester tester) async { + Widget buildLocaleFrame(Locale locale) => buildFrame( + locale: locale, + buildContent: (BuildContext context) { + return Localizations.override( + context: context, + locale: const Locale('en', 'US'), + child: Builder( + builder: (BuildContext context) { + // No StreamChatLocalizations are defined for the first + // Localizations ancestor, so we should get the values from + // the default one, i.e. the one created by WidgetsApp via + // the LocalizationsDelegate provided by MaterialApp. + return Text( + StreamChatLocalizations.of(context)!.launchUrlError, + ); + }, + ), + ); + }, + ); + + await tester.pumpWidget(buildLocaleFrame(const Locale('en', 'US'))); + expect(find.text('Cannot launch the url'), findsOneWidget); + + await tester.pumpWidget(buildLocaleFrame(const Locale('hi', 'IN'))); + expect(find.text('Cannot launch the url'), findsOneWidget); + }); + + testWidgets( + 'MaterialApp adds StreamChatLocalizations for additional languages', + (WidgetTester tester) async { + final Key textKey = UniqueKey(); + + await tester.pumpWidget(buildFrame( + delegates: >[ + GlobalStreamChatLocalizations.delegate, + const FooStreamChatLocalizationsDelegate( + supportedLanguage: 'fr', + launchUrlError: "Impossible de lancer l'url", + ), + const FooStreamChatLocalizationsDelegate( + supportedLanguage: 'de', + launchUrlError: 'Kann die URL nicht starten', + ), + ], + supportedLocales: const [ + Locale('en'), + Locale('hi'), + Locale('fr'), + Locale('de'), + ], + buildContent: (BuildContext context) => Text( + StreamChatLocalizations.of(context)!.launchUrlError, + key: textKey, + ), + )); + + expect( + tester.widget(find.byKey(textKey)).data, + 'Cannot launch the url', + ); + + await tester.binding.setLocale('hi', 'IN'); + await tester.pump(); + expect(find.text('यूआरएल लॉन्च नहीं कर सकते'), findsOneWidget); + + await tester.binding.setLocale('fr', 'CA'); + await tester.pump(); + expect(find.text("Impossible de lancer l'url"), findsOneWidget); + + await tester.binding.setLocale('de', 'DE'); + await tester.pump(); + expect(find.text('Kann die URL nicht starten'), findsOneWidget); + }, + ); + + testWidgets( + 'MaterialApp overrides MaterialLocalizations for all locales', + (WidgetTester tester) async { + final Key textKey = UniqueKey(); + + await tester.pumpWidget(buildFrame( + // Accept whatever locale we're given + localeResolutionCallback: + (Locale? locale, Iterable supportedLocales) => locale, + delegates: [ + const FooStreamChatLocalizationsDelegate( + supportedLanguage: 'allLanguages', + ), + ], + buildContent: (BuildContext context) { + // Should always be 'foo', no matter what the locale is + return Text( + StreamChatLocalizations.of(context)!.launchUrlError, + key: textKey, + ); + }, + )); + + expect(tester.widget(find.byKey(textKey)).data, 'foo'); + + await tester.binding.setLocale('zh', 'CN'); + await tester.pump(); + expect(find.text('foo'), findsOneWidget); + + await tester.binding.setLocale('de', 'DE'); + await tester.pump(); + expect(find.text('foo'), findsOneWidget); + }, + ); + + testWidgets( + 'MaterialApp overrides MaterialLocalizations for default locale', + (WidgetTester tester) async { + final Key textKey = UniqueKey(); + + await tester.pumpWidget(buildFrame( + delegates: [ + const FooStreamChatLocalizationsDelegate(), + ], + // supportedLocales not specified, so all locales resolve to 'en' + buildContent: (BuildContext context) => Text( + StreamChatLocalizations.of(context)!.launchUrlError, + key: textKey, + ), + )); + + // Unsupported locale '_' (the widget tester's default) resolves to 'en'. + expect(tester.widget(find.byKey(textKey)).data, 'foo'); + + // Unsupported locale 'zh' resolves to 'en'. + await tester.binding.setLocale('zh', 'CN'); + await tester.pump(); + expect(find.text('foo'), findsOneWidget); + + // Unsupported locale 'de' resolves to 'en'. + await tester.binding.setLocale('de', 'DE'); + await tester.pump(); + expect(find.text('foo'), findsOneWidget); + }, + ); +} diff --git a/packages/stream_chat_localizations/test/stream_chat_localization_test.dart b/packages/stream_chat_localizations/test/stream_chat_localization_test.dart deleted file mode 100644 index 0324dd17..00000000 --- a/packages/stream_chat_localizations/test/stream_chat_localization_test.dart +++ /dev/null @@ -1,20 +0,0 @@ -import 'dart:ui'; - -import 'package:flutter_test/flutter_test.dart'; - -import 'package:stream_chat_localizations/stream_chat_localizations.dart'; - -void main() { - for (final language in kStreamChatSupportedLanguages) { - test('translations exist for $language', () async { - final locale = Locale(language); - expect( - GlobalStreamChatLocalizations.delegate.isSupported(locale), - isTrue, - ); - final localizations = - await GlobalStreamChatLocalizations.delegate.load(locale); - expect(localizations.launchUrlError, isNotNull); - }); - } -} diff --git a/packages/stream_chat_localizations/test/translations_test.dart b/packages/stream_chat_localizations/test/translations_test.dart new file mode 100644 index 00000000..6e796d08 --- /dev/null +++ b/packages/stream_chat_localizations/test/translations_test.dart @@ -0,0 +1,164 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:stream_chat_flutter/stream_chat_flutter.dart'; + +import 'package:stream_chat_localizations/stream_chat_localizations.dart'; + +void main() { + for (final language in kStreamChatSupportedLanguages) { + test('translations exist for $language', () async { + final locale = Locale(language); + expect( + GlobalStreamChatLocalizations.delegate.isSupported(locale), isTrue); + final localizations = + await GlobalStreamChatLocalizations.delegate.load(locale); + expect(localizations.launchUrlError, isNotNull); + expect(localizations.loadingUsersError, isNotNull); + expect(localizations.noUsersLabel, isNotNull); + expect(localizations.retryLabel, isNotNull); + expect(localizations.userLastOnlineText, isNotNull); + expect(localizations.userOnlineText, isNotNull); + expect(localizations.userOnlineText, isNotNull); + // no users + expect(localizations.userTypingText([]), isNotNull); + // single user + expect(localizations.userTypingText([User(id: 'test-id')]), isNotNull); + // multiple users + expect( + localizations.userTypingText([ + User(id: 'test-id-1'), + User(id: 'test-id-2'), + ]), + isNotNull, + ); + expect(localizations.threadReplyLabel, isNotNull); + expect(localizations.onlyVisibleToYouText, isNotNull); + expect(localizations.threadReplyCountText(3), isNotNull); + expect( + localizations.attachmentsUploadProgressText(remaining: 3, total: 10), + isNotNull, + ); + expect( + localizations.pinnedByUserText( + pinnedBy: User(id: 'pinned-by-user-id'), + currentUser: OwnUser(id: 'current-user-id'), + ), + isNotNull, + ); + expect(localizations.emptyMessagesText, isNotNull); + expect(localizations.genericErrorText, isNotNull); + expect(localizations.loadingMessagesError, isNotNull); + expect(localizations.resultCountText(3), isNotNull); + expect(localizations.messageDeletedText, isNotNull); + expect(localizations.messageDeletedLabel, isNotNull); + expect(localizations.messageReactionsText, isNotNull); + expect(localizations.emptyChatMessagesText, isNotNull); + expect(localizations.threadSeparatorText(3), isNotNull); + expect(localizations.connectedLabel, isNotNull); + expect(localizations.disconnectedLabel, isNotNull); + expect(localizations.reconnectingLabel, isNotNull); + expect(localizations.alsoSendAsDirectMessageLabel, isNotNull); + expect(localizations.addACommentOrSendLabel, isNotNull); + expect(localizations.searchGifLabel, isNotNull); + expect(localizations.writeAMessageLabel, isNotNull); + expect(localizations.instantCommandsLabel, isNotNull); + expect(localizations.fileTooLargeAfterCompressionError, isNotNull); + expect(localizations.fileTooLargeError, isNotNull); + expect(localizations.emojiMatchingQueryText('sahil'), isNotNull); + expect(localizations.addAFileLabel, isNotNull); + expect(localizations.photoFromCameraLabel, isNotNull); + expect(localizations.uploadAFileLabel, isNotNull); + expect(localizations.uploadAPhotoLabel, isNotNull); + expect(localizations.uploadAVideoLabel, isNotNull); + expect(localizations.videoFromCameraLabel, isNotNull); + expect(localizations.okLabel, isNotNull); + expect(localizations.somethingWentWrongLabel, isNotNull); + expect(localizations.addMoreFilesLabel, isNotNull); + expect(localizations.enablePhotoAndVideoAccessMessage, isNotNull); + expect(localizations.allowGalleryAccessMessage, isNotNull); + expect(localizations.flagMessageLabel, isNotNull); + expect(localizations.flagMessageQuestion, isNotNull); + expect(localizations.flagLabel, isNotNull); + expect(localizations.cancelLabel, isNotNull); + expect(localizations.flagMessageSuccessfulLabel, isNotNull); + expect(localizations.flagMessageSuccessfulText, isNotNull); + expect(localizations.deleteLabel, isNotNull); + expect(localizations.deleteMessageLabel, isNotNull); + expect(localizations.deleteMessageQuestion, isNotNull); + expect(localizations.operationCouldNotBeCompletedText, isNotNull); + expect(localizations.replyLabel, isNotNull); + // pinned + expect(localizations.togglePinUnpinText(pinned: true), isNotNull); + // un-pinned + expect(localizations.togglePinUnpinText(pinned: false), isNotNull); + // delete-failed + expect( + localizations.toggleDeleteRetryDeleteMessageText(isDeleteFailed: true), + isNotNull, + ); + // first-delete + expect( + localizations.toggleDeleteRetryDeleteMessageText(isDeleteFailed: false), + isNotNull, + ); + expect(localizations.copyMessageLabel, isNotNull); + expect(localizations.editMessageLabel, isNotNull); + // resend-failed + expect( + localizations.toggleResendOrResendEditedMessage(isUpdateFailed: true), + isNotNull, + ); + // first resend + expect( + localizations.toggleResendOrResendEditedMessage(isUpdateFailed: false), + isNotNull, + ); + expect(localizations.photosLabel, isNotNull); + expect( + localizations.sentAtText( + date: DateTime.now(), + time: DateTime.now(), + ), + isNotNull, + ); + expect(localizations.todayLabel, isNotNull); + expect(localizations.yesterdayLabel, isNotNull); + expect(localizations.channelIsMutedText, isNotNull); + expect(localizations.noTitleText, isNotNull); + expect(localizations.letsStartChattingLabel, isNotNull); + expect(localizations.sendingFirstMessageLabel, isNotNull); + expect(localizations.startAChatLabel, isNotNull); + expect(localizations.loadingChannelsError, isNotNull); + expect(localizations.deleteConversationQuestion, isNotNull); + expect(localizations.streamChatLabel, isNotNull); + expect(localizations.searchingForNetworkLabel, isNotNull); + expect(localizations.offlineLabel, isNotNull); + expect(localizations.tryAgainLabel, isNotNull); + // 1 member + expect(localizations.membersCountText(1), isNotNull); + // 3 members + expect(localizations.membersCountText(3), isNotNull); + // 1 member + expect(localizations.watchersCountText(1), isNotNull); + // 3 members + expect(localizations.watchersCountText(3), isNotNull); + expect(localizations.viewInfoLabel, isNotNull); + expect(localizations.leaveGroupLabel, isNotNull); + expect(localizations.leaveLabel, isNotNull); + expect(localizations.leaveConversationLabel, isNotNull); + expect(localizations.leaveConversationQuestion, isNotNull); + expect(localizations.showInChatLabel, isNotNull); + expect(localizations.saveVideoLabel, isNotNull); + expect(localizations.uploadErrorLabel, isNotNull); + expect(localizations.giphyLabel, isNotNull); + expect(localizations.shuffleLabel, isNotNull); + expect(localizations.sendLabel, isNotNull); + expect(localizations.withText, isNotNull); + expect(localizations.inText, isNotNull); + expect(localizations.youText, isNotNull); + expect(localizations.ofText, isNotNull); + expect(localizations.fileText, isNotNull); + expect(localizations.replyToMessageLabel, isNotNull); + }); + } +}