diff --git a/docs/state-management.md b/docs/state-management.md index 61a207c..bb0036c 100644 --- a/docs/state-management.md +++ b/docs/state-management.md @@ -108,7 +108,7 @@ ProviderContainer(overrides: [ packageInfoProvider.overrideWithValue(packageInfo), // required if using App4Training fileSystemProvider.overrideWith((ref) => MemoryFileSystem()), // optional httpClientProvider.overrideWith((ref) => mockClient), // optional - languageProvider.overrideWith(() => TestLanguageController(...)), // optional + languageProvider.overrideWith2((languageCode) => TestLanguageController(...)), // optional languageStatusProvider.overrideWith(() => TestLanguageStatus()), // optional backgroundSchedulerProvider.overrideWith(() => TestBackgroundScheduler()), // for /startup tests ]) diff --git a/docs/testing.md b/docs/testing.md index 31ebe2a..6cce865 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -92,7 +92,7 @@ testWidgets('...', (tester) async { overrides: [ sharedPrefsProvider.overrideWithValue(prefs), packageInfoProvider.overrideWithValue(...), - languageProvider.overrideWith(() => TestLanguageController(initReturns: true)), + languageProvider.overrideWith2((languageCode) => TestLanguageController(initReturns: true)), ], child: const MaterialApp( locale: Locale('de'), diff --git a/integration_test/background_interaction_test.dart b/integration_test/background_interaction_test.dart index ff683a9..4ba061c 100644 --- a/integration_test/background_interaction_test.dart +++ b/integration_test/background_interaction_test.dart @@ -31,14 +31,22 @@ void main() async { // Waiting for the background task to finish its work completer.complete(data); }); - await Workmanager().initialize(backgroundTask, isInDebugMode: false); - await Workmanager().registerOneOffTask("task-identifier", "testTask", - initialDelay: const Duration(seconds: 2)); - - await tester.pumpWidget(ProviderScope(overrides: [ - sharedPrefsProvider.overrideWithValue(prefs), - packageInfoProvider.overrideWithValue(packageInfo) - ], child: const App4Training())); + await Workmanager().initialize(backgroundTask); + await Workmanager().registerOneOffTask( + "task-identifier", + "testTask", + initialDelay: const Duration(seconds: 2), + ); + + await tester.pumpWidget( + ProviderScope( + overrides: [ + sharedPrefsProvider.overrideWithValue(prefs), + packageInfoProvider.overrideWithValue(packageInfo), + ], + child: const App4Training(), + ), + ); expect(find.text('Loading'), findsOneWidget); // Wait for the background isolate to finish @@ -62,15 +70,24 @@ void main() async { }); var fileSystem = await createTestFileSystem(); - await tester.pumpWidget(ProviderScope(overrides: [ - sharedPrefsProvider.overrideWithValue(prefs), - packageInfoProvider.overrideWithValue(packageInfo), - fileSystemProvider.overrideWith((ref) => fileSystem), - // We need to mock DownloadAssetsController because we can't use a memory - // file system to test it (it uses dart:io, not the file package) - languageProvider.overrideWith(() => LanguageController( - assetsController: createMockDownloadAssetsController())), - ], child: const App4Training())); + await tester.pumpWidget( + ProviderScope( + overrides: [ + sharedPrefsProvider.overrideWithValue(prefs), + packageInfoProvider.overrideWithValue(packageInfo), + fileSystemProvider.overrideWith((ref) => fileSystem), + // We need to mock DownloadAssetsController because we can't use a memory + // file system to test it (it uses dart:io, not the file package) + languageProvider.overrideWith2( + (languageCode) => LanguageController( + languageCode: languageCode, + assetsController: createMockDownloadAssetsController(), + ), + ), + ], + child: const App4Training(), + ), + ); expect(find.text('Loading'), findsOneWidget); await tester.pumpAndSettle(); @@ -83,9 +100,12 @@ void main() async { Navigator.of(tester.element(find.byType(Scaffold))).pop(); await tester.pumpAndSettle(); - await Workmanager().initialize(backgroundTask, isInDebugMode: false); - await Workmanager().registerOneOffTask("task-identifier", "testTask", - initialDelay: const Duration(seconds: 2)); + await Workmanager().initialize(backgroundTask); + await Workmanager().registerOneOffTask( + "task-identifier", + "testTask", + initialDelay: const Duration(seconds: 2), + ); // Wait for the background isolate to finish final msg = await completer.future.timeout(const Duration(seconds: 10)); diff --git a/lib/background/background_task.dart b/lib/background/background_task.dart index 6276856..187b504 100644 --- a/lib/background/background_task.dart +++ b/lib/background/background_task.dart @@ -19,8 +19,10 @@ Future writeLog(String message) async { debugPrint(message); final path = await getApplicationDocumentsDirectory(); final file = File('${path.path}/background.log'); - await file.writeAsString('${DateTime.now().toIso8601String()}: $message\n', - mode: FileMode.append); + await file.writeAsString( + '${DateTime.now().toIso8601String()}: $message\n', + mode: FileMode.append, + ); } catch (e) { debugPrint('Error writing message $message to file: $e'); } @@ -52,7 +54,8 @@ Future backgroundMain() async { final prefs = await SharedPreferences.getInstance(); final ref = ProviderContainer( - overrides: [sharedPrefsProvider.overrideWithValue(prefs)]); + overrides: [sharedPrefsProvider.overrideWithValue(prefs)], + ); await backgroundCheck(ref); // TODO: check automatic updates setting; if necessary check connectivity @@ -66,12 +69,18 @@ Future backgroundTestMain() async { final prefs = await SharedPreferences.getInstance(); var fileSystem = await createTestFileSystem(); - final ref = ProviderContainer(overrides: [ - sharedPrefsProvider.overrideWithValue(prefs), - fileSystemProvider.overrideWith((ref) => fileSystem), - languageProvider.overrideWith(() => LanguageController( - assetsController: createMockDownloadAssetsController())) - ]); + final ref = ProviderContainer( + overrides: [ + sharedPrefsProvider.overrideWithValue(prefs), + fileSystemProvider.overrideWith((ref) => fileSystem), + languageProvider.overrideWith2( + (languageCode) => LanguageController( + languageCode: languageCode, + assetsController: createMockDownloadAssetsController(), + ), + ), + ], + ); await backgroundCheck(ref); } diff --git a/lib/data/globals.dart b/lib/data/globals.dart index 63e9c9f..c03d966 100644 --- a/lib/data/globals.dart +++ b/lib/data/globals.dart @@ -11,7 +11,7 @@ final packageInfoProvider = MustOverrideProvider(); Provider MustOverrideProvider() { return Provider( (_) => throw ProviderNotOverriddenException(), - retry: (_, __) => null, // Disable v3 automatic retry - must fail fast + retry: (_, _) => null, // Disable v3 automatic retry - must fail fast ); } diff --git a/lib/data/languages.dart b/lib/data/languages.dart index 6adedd2..2946718 100644 --- a/lib/data/languages.dart +++ b/lib/data/languages.dart @@ -25,7 +25,8 @@ final imageContentProvider = Provider.family((ref, res) { final String path = ref.watch(languageProvider(res.langCode)).path; if (path == '') { debugPrint( - "Error: Can't load image ${res.name} in language ${res.langCode}"); + "Error: Can't load image ${res.name} in language ${res.langCode}", + ); return ''; } final fileSystem = ref.watch(fileSystemProvider); @@ -44,8 +45,10 @@ final imageContentProvider = Provider.family((ref, res) { /// throws [PageNotFoundException]: Hm, errorneous link? /// throws [LanguageCorruptedException]: oops, /// hope this goes away by deleting + re-downloading the language -final pageContentProvider = - FutureProvider.family((ref, page) async { +final pageContentProvider = FutureProvider.family(( + ref, + page, +) async { final fileSystem = ref.watch(fileSystemProvider); final lang = ref.watch(languageProvider(page.langCode)); if (!lang.downloaded) { @@ -61,25 +64,32 @@ final pageContentProvider = debugPrint("Fetching content of '${page.name}/${page.langCode}'..."); try { - String content = await fileSystem - .file(join(lang.path, pageDetails.fileName)) - .readAsString(); + String content = + await fileSystem + .file(join(lang.path, pageDetails.fileName)) + .readAsString(); // Load images directly into the HTML: // Replace with - return content.replaceAllMapped(RegExp(r'src="files/([^.]+.png)"'), - (match) { + return content.replaceAllMapped(RegExp(r'src="files/([^.]+.png)"'), ( + match, + ) { if (!lang.images.containsKey(match.group(1))) { debugPrint( - 'Warning: image ${match.group(1)} missing (in ${pageDetails.fileName})'); + 'Warning: image ${match.group(1)} missing (in ${pageDetails.fileName})', + ); return match.group(0)!; } - String imageData = ref.watch(imageContentProvider( - (name: match.group(1)!, langCode: page.langCode))); + String imageData = ref.watch( + imageContentProvider((name: match.group(1)!, langCode: page.langCode)), + ); return 'src="data:image/png;base64,$imageData"'; }); } on FileSystemException catch (e) { throw LanguageCorruptedException( - page.langCode, 'Error while reading from local storage.', e); + page.langCode, + 'Error while reading from local storage.', + e, + ); } }, retry: null); @@ -88,8 +98,8 @@ final pageContentProvider = /// ref.watch(languageProvider('en').notifier) -> get English LanguageController final languageProvider = NotifierProvider.family((arg) { - return LanguageController(languageCode: arg); -}); + return LanguageController(languageCode: arg); + }); /// How many languages do we have available offline? final countDownloadedLanguagesProvider = Provider((ref) { @@ -112,9 +122,10 @@ class LanguageController extends Notifier { /// We use dependency injection (optional parameters [assetsController]) /// so that we can test the class well - LanguageController( - {this.languageCode = '', DownloadAssetsController? assetsController}) - : _controller = assetsController ?? DownloadAssetsController(); + LanguageController({ + this.languageCode = '', + DownloadAssetsController? assetsController, + }) : _controller = assetsController ?? DownloadAssetsController(); /// Make sure that our [_controller] is initialized Future _initController() async { @@ -132,7 +143,14 @@ class LanguageController extends Notifier { // through the constructor. languageCode = ref.$arg as String; return Language( - '', const {}, const [], const {}, '', 0, DateTime.utc(2023)); + '', + const {}, + const [], + const {}, + '', + 0, + DateTime.utc(2023), + ); } /// Download this language and make it available. @@ -160,19 +178,29 @@ class LanguageController extends Notifier { /// Returns true when the language is now available, false if not Future lazyInit() async { await _initController(); - String path = - join(_controller.assetsDir!, Globals.getResourcesDir(languageCode)); + String path = join( + _controller.assetsDir!, + Globals.getResourcesDir(languageCode), + ); final stat = await ref .watch(fileSystemProvider) .stat(join(path, 'structure', 'contents.json')); bool downloaded = (stat.type != FileSystemEntityType.notFound); debugPrint( - "QuickInit trying to load '$languageCode', downloaded: $downloaded"); + "QuickInit trying to load '$languageCode', downloaded: $downloaded", + ); if (downloaded) { DateTime timestamp = stat.modified.toUtc(); // Always store UTC internally state = Language( - languageCode, const {}, const [], const {}, path, 0, timestamp); + languageCode, + const {}, + const [], + const {}, + path, + 0, + timestamp, + ); return true; } return false; @@ -187,8 +215,10 @@ class LanguageController extends Notifier { try { // Now we store the full path to the language - String path = - join(_controller.assetsDir!, Globals.getResourcesDir(languageCode)); + String path = join( + _controller.assetsDir!, + Globals.getResourcesDir(languageCode), + ); debugPrint("Path: $path"); bool downloaded = await _controller.assetsDirAlreadyExists(); @@ -197,18 +227,22 @@ class LanguageController extends Notifier { // Store the size of the downloaded files (HTML + PDF) int sizeInKB = await _calculateMemoryUsage( - fileSystem.directory(_controller.assetsDir!)); + fileSystem.directory(_controller.assetsDir!), + ); // Get the timestamp: When were our contents stored on the device? - FileStat stat = - await fileSystem.stat(join(path, 'structure', 'contents.json')); + FileStat stat = await fileSystem.stat( + join(path, 'structure', 'contents.json'), + ); DateTime timestamp = stat.modified.toUtc(); // Always store UTC internally // Read structure/contents.json as our source of truth: // Which pages are available, what is the order in the menu - var structure = jsonDecode(fileSystem - .file(join(path, 'structure', 'contents.json')) - .readAsStringSync()); + var structure = jsonDecode( + fileSystem + .file(join(path, 'structure', 'contents.json')) + .readAsStringSync(), + ); final Map pages = {}; final List pageIndex = []; @@ -216,12 +250,16 @@ class LanguageController extends Notifier { final Set pdfFiles = {}; // Go through existing PDF files - var pdfPath = - join(_controller.assetsDir!, Globals.getPdfDir(languageCode)); + var pdfPath = join( + _controller.assetsDir!, + Globals.getPdfDir(languageCode), + ); var pdfDir = fileSystem.directory(pdfPath); if (await pdfDir.exists()) { - await for (var file - in pdfDir.list(recursive: false, followLinks: false)) { + await for (var file in pdfDir.list( + recursive: false, + followLinks: false, + )) { if (file is File) { pdfFiles.add(file.basename); } else { @@ -239,8 +277,13 @@ class LanguageController extends Notifier { pdfName = join(pdfPath, element['pdf']); pdfFiles.remove(element['pdf']); } - pages[element['page']] = Page(element['page'], element['title'], - element['filename'], element['version'], pdfName); + pages[element['page']] = Page( + element['page'], + element['title'], + element['filename'], + element['version'], + pdfName, + ); } // Consistency checking... @@ -252,8 +295,10 @@ class LanguageController extends Notifier { // Register available images var filesDir = fileSystem.directory(join(path, 'files')); if (await filesDir.exists()) { - await for (var file - in filesDir.list(recursive: false, followLinks: false)) { + await for (var file in filesDir.list( + recursive: false, + followLinks: false, + )) { if (file is File) { images[file.basename] = Image(file.basename); } else { @@ -262,15 +307,29 @@ class LanguageController extends Notifier { } } state = Language( - languageCode, pages, pageIndex, images, path, sizeInKB, timestamp); + languageCode, + pages, + pageIndex, + images, + path, + sizeInKB, + timestamp, + ); return true; } catch (e) { String msg = 'Error initializing data structure: $e'; debugPrint(msg); // Delete the whole folder await _controller.clearAssets(); - state = - Language('', const {}, const [], const {}, '', 0, DateTime.utc(2023)); + state = Language( + '', + const {}, + const [], + const {}, + '', + 0, + DateTime.utc(2023), + ); return false; } } @@ -279,8 +338,15 @@ class LanguageController extends Notifier { Future deleteResources() async { await _initController(); await _controller.clearAssets(); - state = - Language('', const {}, const [], const {}, '', 0, DateTime.utc(2023)); + state = Language( + '', + const {}, + const [], + const {}, + '', + 0, + DateTime.utc(2023), + ); } /// Download all files for one language via DownloadAssetsController @@ -293,9 +359,11 @@ class LanguageController extends Notifier { // assetUrls takes an array, but we can't specify both URLs in one call: // DownloadAssets throws when both files have the same name (main.zip) :-/ await _controller.startDownload( - assetsUrls: [AssetUrl(url: Globals.getRemoteUrlHtml(languageCode))]); + assetsUrls: [AssetUrl(url: Globals.getRemoteUrlHtml(languageCode))], + ); await _controller.startDownload( - assetsUrls: [AssetUrl(url: Globals.getRemoteUrlPdf(languageCode))]); + assetsUrls: [AssetUrl(url: Globals.getRemoteUrlPdf(languageCode))], + ); } catch (e) { debugPrint("Error while downloading language '$languageCode': $e"); // delete the empty folder left behind by startDownload() @@ -323,7 +391,9 @@ class LanguageController extends Notifier { /// error handling if a page we expect to be there can't be loaded because /// a HTML file is missing... Future _checkConsistency( - Directory dir, final Map pages) async { + Directory dir, + final Map pages, + ) async { Set files = {}; await for (var file in dir.list(recursive: false, followLinks: false)) { if (file is File) { @@ -333,7 +403,8 @@ class LanguageController extends Notifier { pages.forEach((key, page) { if (!files.remove(page.fileName)) { debugPrint( - "Warning: Structure mentions ${page.fileName} but the file is missing"); + "Warning: Structure mentions ${page.fileName} but the file is missing", + ); } }); if (files.isNotEmpty) debugPrint("Warning: Found orphaned files $files"); @@ -396,8 +467,15 @@ class Language { /// When were the files downloaded on our device? file system attribute, UTC final DateTime downloadTimestamp; - const Language(this.languageCode, this.pages, this.pageIndex, this.images, - this.path, this.sizeInKB, this.downloadTimestamp); + const Language( + this.languageCode, + this.pages, + this.pageIndex, + this.images, + this.path, + this.sizeInKB, + this.downloadTimestamp, + ); /// Returns an list with all the worksheet titles in the menu. /// The list is ordered as identifier -> translated title diff --git a/lib/features/share/share_service.dart b/lib/features/share/share_service.dart index ccd5c92..178e8d7 100644 --- a/lib/features/share/share_service.dart +++ b/lib/features/share/share_service.dart @@ -17,7 +17,7 @@ class ShareService { /// Wraps Share.shareXFiles() (package share_plus) /// - /// Not using the same argument List because that would make it + /// Not using the same argument List`` because that would make it /// harder to verify that the function gets called with correct arguments /// with mocktail Future shareFile(String path) { diff --git a/lib/routes/onboarding/download_languages_page.dart b/lib/routes/onboarding/download_languages_page.dart index b5c0591..fe8f5d2 100644 --- a/lib/routes/onboarding/download_languages_page.dart +++ b/lib/routes/onboarding/download_languages_page.dart @@ -28,9 +28,9 @@ class DownloadLanguagesPage extends ConsumerWidget { : ElevatedButton.styleFrom( // https://api.flutter.dev/flutter/material/ElevatedButton/defaultStyleOf.html backgroundColor: - Theme.of(context).colorScheme.onSurface.withOpacity(0.12), + Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.12), foregroundColor: - Theme.of(context).colorScheme.onSurface.withOpacity(0.38), + Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.38), elevation: 0, shape: const StadiumBorder(), ); diff --git a/lib/routes/startup_page.dart b/lib/routes/startup_page.dart index 37ce526..84f22a9 100644 --- a/lib/routes/startup_page.dart +++ b/lib/routes/startup_page.dart @@ -37,7 +37,7 @@ class StartupPage extends ConsumerWidget { return '/onboarding/2'; // Go to DownloadLanguagesPage } -/* TODO for version 0.9 + /* TODO for version 0.9 // Check whether user completed third onboarding step if (ref.read(sharedPrefsProvider).getString('checkFrequency') == null) { return '/onboarding/3'; @@ -64,8 +64,12 @@ class StartupPage extends ConsumerWidget { Future initResult = ((initFunction != null) ? initFunction!() : init(ref)); return FutureBuilder( - future: initResult.then((String navigateTo) => - Navigator.pushReplacementNamed(context, navigateTo)), + future: initResult.then( + (String navigateTo) => { + if (context.mounted) + {Navigator.pushReplacementNamed(context, navigateTo)}, + }, + ), initialData: "Loading", builder: (BuildContext context, AsyncSnapshot snapshot) { debugPrint(snapshot.connectionState.toString()); @@ -77,7 +81,8 @@ class StartupPage extends ConsumerWidget { return loadingAnimation('Loading'); case ConnectionState.done: debugPrint( - 'Done, hasData: ${snapshot.hasData}, Error: ${snapshot.hasError}'); + 'Done, hasData: ${snapshot.hasData}, Error: ${snapshot.hasError}', + ); if (snapshot.hasError) { // TODO do something more helpful for the user ("try again...") return ErrorPage(snapshot.error.toString()); diff --git a/lib/widgets/html_view.dart b/lib/widgets/html_view.dart index 232cf03..b02fd3f 100644 --- a/lib/widgets/html_view.dart +++ b/lib/widgets/html_view.dart @@ -94,7 +94,7 @@ class HtmlView extends StatelessWidget { margin: Margins(top: Margin(0), bottom: Margin(0)), ), }, - onAnchorTap: (url, _, __) { + onAnchorTap: (url, _, _) { debugPrint("Link tapped: $url"); if (url != null) { Navigator.pushNamed(context, '/view$url'); diff --git a/pubspec.lock b/pubspec.lock index b3bdc2e..c7f8131 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -488,10 +488,10 @@ packages: dependency: transitive description: name: matcher - sha256: "12956d0ad8390bbcc63ca2e1469c0619946ccb52809807067a7020d57e647aa6" + sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861 url: "https://pub.dev" source: hosted - version: "0.12.18" + version: "0.12.19" material_color_utilities: dependency: transitive description: @@ -901,26 +901,26 @@ packages: dependency: "direct main" description: name: test - sha256: "54c516bbb7cee2754d327ad4fca637f78abfc3cbcc5ace83b3eda117e42cd71a" + sha256: "280d6d890011ca966ad08df7e8a4ddfab0fb3aa49f96ed6de56e3521347a9ae7" url: "https://pub.dev" source: hosted - version: "1.29.0" + version: "1.30.0" test_api: dependency: transitive description: name: test_api - sha256: "93167629bfc610f71560ab9312acdda4959de4df6fac7492c89ff0d3886f6636" + sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a" url: "https://pub.dev" source: hosted - version: "0.7.9" + version: "0.7.10" test_core: dependency: transitive description: name: test_core - sha256: "394f07d21f0f2255ec9e3989f21e54d3c7dc0e6e9dbce160e5a9c1a6be0e2943" + sha256: "0381bd1585d1a924763c308100f2138205252fb90c9d4eeaf28489ee65ccde51" url: "https://pub.dev" source: hosted - version: "0.6.15" + version: "0.6.16" typed_data: dependency: transitive description: diff --git a/test/background_result_test.dart b/test/background_result_test.dart index dcd150f..ab66858 100644 --- a/test/background_result_test.dart +++ b/test/background_result_test.dart @@ -13,85 +13,123 @@ void main() { SharedPreferences.setMockInitialValues({}); final prefs = await SharedPreferences.getInstance(); - final ref = ProviderContainer(overrides: [ - languageProvider.overrideWith(() => TestLanguageController()), - sharedPrefsProvider.overrideWith((ref) => prefs) - ]); + final ref = ProviderContainer( + overrides: [ + languageProvider.overrideWith2( + (languageCode) => TestLanguageController(), + ), + sharedPrefsProvider.overrideWith((ref) => prefs), + ], + ); expect(ref.read(backgroundResultProvider).foundActivity, false); - expect(await ref.read(backgroundResultProvider.notifier).checkForActivity(), - false); + expect( + await ref.read(backgroundResultProvider.notifier).checkForActivity(), + false, + ); expect(ref.read(backgroundResultProvider).foundActivity, false); }); test('There was some background activity', () async { final oldTime = DateTime(2023, 2, 2).toUtc(); - SharedPreferences.setMockInitialValues( - {'lastChecked-de': oldTime.toIso8601String()}); + SharedPreferences.setMockInitialValues({ + 'lastChecked-de': oldTime.toIso8601String(), + }); final prefs = await SharedPreferences.getInstance(); - final ref = ProviderContainer(overrides: [ - languageProvider.overrideWith(() => TestLanguageController()), - sharedPrefsProvider.overrideWith((ref) => prefs) - ]); + final ref = ProviderContainer( + overrides: [ + languageProvider.overrideWith2( + (languageCode) => TestLanguageController(), + ), + sharedPrefsProvider.overrideWith((ref) => prefs), + ], + ); expect(ref.read(backgroundResultProvider).foundActivity, false); - expect(await ref.read(backgroundResultProvider.notifier).checkForActivity(), - false); + expect( + await ref.read(backgroundResultProvider.notifier).checkForActivity(), + false, + ); expect(ref.read(backgroundResultProvider).foundActivity, false); // Now we change the lastChecked timestamp for German final currentTime = DateTime.now().toUtc(); await prefs.setString('lastChecked-de', currentTime.toIso8601String()); - expect(ref.read(languageStatusProvider('de')).lastCheckedTimestamp, - equals(oldTime)); - - expect(await ref.read(backgroundResultProvider.notifier).checkForActivity(), - true); + expect( + ref.read(languageStatusProvider('de')).lastCheckedTimestamp, + equals(oldTime), + ); + + expect( + await ref.read(backgroundResultProvider.notifier).checkForActivity(), + true, + ); expect(ref.read(backgroundResultProvider).foundActivity, true); // languageStatusProvider should now have our new timestamp - expect(ref.read(languageStatusProvider('de')).lastCheckedTimestamp, - equals(currentTime)); + expect( + ref.read(languageStatusProvider('de')).lastCheckedTimestamp, + equals(currentTime), + ); // There shouldn't be any changes for another language status provider - expect(ref.read(languageStatusProvider('en')).lastCheckedTimestamp, - equals(DateTime.utc(2023))); + expect( + ref.read(languageStatusProvider('en')).lastCheckedTimestamp, + equals(DateTime.utc(2023)), + ); // next check shouldn't find any background activity - expect(await ref.read(backgroundResultProvider.notifier).checkForActivity(), - false); + expect( + await ref.read(backgroundResultProvider.notifier).checkForActivity(), + false, + ); expect(ref.read(backgroundResultProvider).foundActivity, false); }); test('Test edge case: invalid values', () async { final oldTime = DateTime(2023, 2, 2).toUtc(); - SharedPreferences.setMockInitialValues( - {'lastChecked-de': oldTime.toIso8601String()}); + SharedPreferences.setMockInitialValues({ + 'lastChecked-de': oldTime.toIso8601String(), + }); final prefs = await SharedPreferences.getInstance(); - final ref = ProviderContainer(overrides: [ - languageProvider.overrideWith(() => TestLanguageController()), - sharedPrefsProvider.overrideWith((ref) => prefs) - ]); + final ref = ProviderContainer( + overrides: [ + languageProvider.overrideWith2( + (languageCode) => TestLanguageController(), + ), + sharedPrefsProvider.overrideWith((ref) => prefs), + ], + ); - expect(await ref.read(backgroundResultProvider.notifier).checkForActivity(), - false); + expect( + await ref.read(backgroundResultProvider.notifier).checkForActivity(), + false, + ); // Trying an invalid value, a date in the future and a too-old date await prefs.setString('lastChecked-de', 'invalid'); - expect(await ref.read(backgroundResultProvider.notifier).checkForActivity(), - false); + expect( + await ref.read(backgroundResultProvider.notifier).checkForActivity(), + false, + ); DateTime futureDate = DateTime.now().add(const Duration(days: 1)); await prefs.setString('lastChecked-de', futureDate.toIso8601String()); - expect(await ref.read(backgroundResultProvider.notifier).checkForActivity(), - false); + expect( + await ref.read(backgroundResultProvider.notifier).checkForActivity(), + false, + ); await prefs.setString('lastChecked-de', DateTime(2023).toIso8601String()); - expect(await ref.read(backgroundResultProvider.notifier).checkForActivity(), - false); - - expect(ref.read(languageStatusProvider('de')).lastCheckedTimestamp, - equals(oldTime)); + expect( + await ref.read(backgroundResultProvider.notifier).checkForActivity(), + false, + ); + + expect( + ref.read(languageStatusProvider('de')).lastCheckedTimestamp, + equals(oldTime), + ); }); } diff --git a/test/background_task_test.dart b/test/background_task_test.dart index 6c67413..31bac12 100644 --- a/test/background_task_test.dart +++ b/test/background_task_test.dart @@ -19,9 +19,11 @@ void main() { final ref = ProviderContainer(overrides: [ sharedPrefsProvider.overrideWithValue(prefs), - languageProvider.overrideWith( - () => LanguageController(assetsController: fakeController)), - languageStatusProvider.overrideWith(() => TestLanguageStatus()) + languageProvider.overrideWith2( + (languageCode) => LanguageController( + languageCode: languageCode, + assetsController: fakeController,),), + languageStatusProvider.overrideWith2((languageCode) => TestLanguageStatus()) ]); await backgroundCheck(ref); expect(ref.read(updatesAvailableProvider), false); @@ -37,8 +39,9 @@ void main() { sharedPrefsProvider.overrideWithValue(prefs), httpClientProvider.overrideWith((ref) => mockCheckResponse({'de': 2})), fileSystemProvider.overrideWith((ref) => fileSystem), - languageProvider.overrideWith( - () => LanguageController(assetsController: fakeController)), + languageProvider.overrideWith2( + (languageCode) => LanguageController(assetsController: fakeController), + ), ]); expect(ref.read(sharedPrefsProvider).getBool('updatesAvailable-de'), null); diff --git a/test/check_now_button_test.dart b/test/check_now_button_test.dart index 0462cb7..d5f366b 100644 --- a/test/check_now_button_test.dart +++ b/test/check_now_button_test.dart @@ -32,9 +32,9 @@ void main() { (WidgetTester tester) async { final ref = ProviderContainer(overrides: [ appLanguageProvider.overrideWith(() => TestAppLanguage('de')), - languageProvider.overrideWith(() => TestLanguageController()), + languageProvider.overrideWith2((languageCode) => TestLanguageController()), languageStatusProvider - .overrideWith(() => TestLanguageStatus(checkReturnValue: 0)) + .overrideWith2((languageCode) => TestLanguageStatus(checkReturnValue: 0)) ]); await tester.pumpWidget(UncontrolledProviderScope( @@ -55,9 +55,9 @@ void main() { countCheckCalled = 0; final ref = ProviderContainer(overrides: [ appLanguageProvider.overrideWith(() => TestAppLanguage('de')), - languageProvider.overrideWith(() => TestLanguageController()), + languageProvider.overrideWith2((languageCode) => TestLanguageController()), languageStatusProvider - .overrideWith(() => TestLanguageStatus(checkReturnValue: 2)) + .overrideWith2((languageCode) => TestLanguageStatus(checkReturnValue: 2)) ]); await tester.pumpWidget(UncontrolledProviderScope( @@ -82,9 +82,9 @@ void main() { countCheckCalled = 0; final ref = ProviderContainer(overrides: [ appLanguageProvider.overrideWith(() => TestAppLanguage('de')), - languageProvider.overrideWith(() => TestLanguageController()), - languageStatusProvider.overrideWith( - () => TestLanguageStatus(checkReturnValue: apiRateLimitExceeded)) + languageProvider.overrideWith2((languageCode) => TestLanguageController()), + languageStatusProvider.overrideWith2( + (languageCode) => TestLanguageStatus(checkReturnValue: apiRateLimitExceeded)) ]); await tester.pumpWidget(UncontrolledProviderScope( @@ -111,9 +111,9 @@ void main() { countCheckCalled = 0; final ref = ProviderContainer(overrides: [ appLanguageProvider.overrideWith(() => TestAppLanguage('de')), - languageProvider.overrideWith(() => TestLanguageController()), + languageProvider.overrideWith2((languageCode) => TestLanguageController()), languageStatusProvider - .overrideWith(() => TestLanguageStatus(checkReturnValue: -1)) + .overrideWith2((languageCode) => TestLanguageStatus(checkReturnValue: -1)) ]); await tester.pumpWidget(UncontrolledProviderScope( diff --git a/test/delete_language_button_test.dart b/test/delete_language_button_test.dart index d2c6553..10014a9 100644 --- a/test/delete_language_button_test.dart +++ b/test/delete_language_button_test.dart @@ -30,7 +30,7 @@ void main() { final testLanguageController = TestLanguageController(); final ref = ProviderContainer(overrides: [ appLanguageProvider.overrideWith(() => TestAppLanguage('de')), - languageProvider.overrideWith(() => testLanguageController) + languageProvider.overrideWith2((languageCode) => testLanguageController) ]); await tester.pumpWidget(UncontrolledProviderScope( @@ -56,7 +56,7 @@ void main() { (WidgetTester tester) async { final ref = ProviderContainer(overrides: [ appLanguageProvider.overrideWith(() => TestAppLanguage('de')), - languageProvider.overrideWith(() => TestLanguageController()) + languageProvider.overrideWith2((languageCode) => TestLanguageController()) ]); await tester.pumpWidget(UncontrolledProviderScope( @@ -90,7 +90,7 @@ void main() { testWidgets('Test DeleteAllLanguagesButton', (WidgetTester tester) async { final ref = ProviderContainer(overrides: [ appLanguageProvider.overrideWith(() => TestAppLanguage('de')), - languageProvider.overrideWith(() => TestLanguageController()) + languageProvider.overrideWith2((languageCode) => TestLanguageController()) ]); await tester.pumpWidget(UncontrolledProviderScope( diff --git a/test/download_language_button_test.dart b/test/download_language_button_test.dart index bfcf3a9..4f60b3d 100644 --- a/test/download_language_button_test.dart +++ b/test/download_language_button_test.dart @@ -13,31 +13,44 @@ import 'languages_test.dart'; class TestDownloadLanguageButton extends ConsumerWidget { final String languageCode; final bool highlight; - const TestDownloadLanguageButton(this.languageCode, - {this.highlight = false, super.key}); + + const TestDownloadLanguageButton( + this.languageCode, { + this.highlight = false, + super.key, + }); @override Widget build(BuildContext context, WidgetRef ref) { return MaterialApp( - locale: ref.watch(appLanguageProvider).locale, - localizationsDelegates: AppLocalizations.localizationsDelegates, - supportedLocales: AppLocalizations.supportedLocales, - scaffoldMessengerKey: ref.read(scaffoldMessengerKeyProvider), - home: Scaffold( - body: DownloadLanguageButton(languageCode, highlight: highlight))); + locale: ref.watch(appLanguageProvider).locale, + localizationsDelegates: AppLocalizations.localizationsDelegates, + supportedLocales: AppLocalizations.supportedLocales, + scaffoldMessengerKey: ref.read(scaffoldMessengerKeyProvider), + home: Scaffold( + body: DownloadLanguageButton(languageCode, highlight: highlight), + ), + ); } } void main() { testWidgets('Test DownloadLanguageButton', (WidgetTester tester) async { - final ref = ProviderContainer(overrides: [ - appLanguageProvider.overrideWith(() => TestAppLanguage('de')), - languageProvider - .overrideWith(() => TestLanguageController(downloadedLanguages: [])), - ]); - - await tester.pumpWidget(UncontrolledProviderScope( - container: ref, child: const TestDownloadLanguageButton('en'))); + final ref = ProviderContainer( + overrides: [ + appLanguageProvider.overrideWith(() => TestAppLanguage('de')), + languageProvider.overrideWith2( + (languageCode) => TestLanguageController(downloadedLanguages: []), + ), + ], + ); + + await tester.pumpWidget( + UncontrolledProviderScope( + container: ref, + child: const TestDownloadLanguageButton('en'), + ), + ); expect(find.byIcon(Icons.download), findsOneWidget); expect(find.byType(Container), findsNothing); // should not be highlighted @@ -51,20 +64,27 @@ void main() { }); testWidgets('Test DownloadAllLanguagesButton', (WidgetTester tester) async { - final ref = ProviderContainer(overrides: [ - appLanguageProvider.overrideWith(() => TestAppLanguage('de')), - languageProvider - .overrideWith(() => TestLanguageController(downloadedLanguages: [])) - ]); - - await tester.pumpWidget(UncontrolledProviderScope( + final ref = ProviderContainer( + overrides: [ + appLanguageProvider.overrideWith(() => TestAppLanguage('de')), + languageProvider.overrideWith2( + (languageCodeL) => TestLanguageController(downloadedLanguages: []), + ), + ], + ); + + await tester.pumpWidget( + UncontrolledProviderScope( container: ref, child: MaterialApp( - locale: ref.read(appLanguageProvider).locale, - localizationsDelegates: AppLocalizations.localizationsDelegates, - supportedLocales: AppLocalizations.supportedLocales, - scaffoldMessengerKey: ref.read(scaffoldMessengerKeyProvider), - home: const Scaffold(body: DownloadAllLanguagesButton())))); + locale: ref.read(appLanguageProvider).locale, + localizationsDelegates: AppLocalizations.localizationsDelegates, + supportedLocales: AppLocalizations.supportedLocales, + scaffoldMessengerKey: ref.read(scaffoldMessengerKeyProvider), + home: const Scaffold(body: DownloadAllLanguagesButton()), + ), + ), + ); expect(ref.read(languageProvider('ar')).downloaded, false); @@ -79,17 +99,24 @@ void main() { expect(find.text('34 Sprachen heruntergeladen'), findsOneWidget); }); - testWidgets('Test highlighted DownloadLanguageButton', - (WidgetTester tester) async { - final ref = ProviderContainer(overrides: [ - appLanguageProvider.overrideWith(() => TestAppLanguage('de')), - languageProvider - .overrideWith(() => TestLanguageController(downloadedLanguages: [])), - ]); - - await tester.pumpWidget(UncontrolledProviderScope( + testWidgets('Test highlighted DownloadLanguageButton', ( + WidgetTester tester, + ) async { + final ref = ProviderContainer( + overrides: [ + appLanguageProvider.overrideWith(() => TestAppLanguage('de')), + languageProvider.overrideWith2( + (languageCode) => TestLanguageController(downloadedLanguages: []), + ), + ], + ); + + await tester.pumpWidget( + UncontrolledProviderScope( container: ref, - child: const TestDownloadLanguageButton('en', highlight: true))); + child: const TestDownloadLanguageButton('en', highlight: true), + ), + ); expect(find.byIcon(Icons.download), findsOneWidget); // Test the highlighting @@ -105,14 +132,24 @@ void main() { testWidgets('Test failing download', (WidgetTester tester) async { var throwingController = ThrowingDownloadAssetsController(); - final ref = ProviderContainer(overrides: [ - appLanguageProvider.overrideWith(() => TestAppLanguage('de')), - languageProvider.overrideWith( - () => LanguageController(assetsController: throwingController)) - ]); - - await tester.pumpWidget(UncontrolledProviderScope( - container: ref, child: const TestDownloadLanguageButton('en'))); + final ref = ProviderContainer( + overrides: [ + appLanguageProvider.overrideWith(() => TestAppLanguage('de')), + languageProvider.overrideWith2( + (languageCode) => LanguageController( + languageCode: languageCode, + assetsController: throwingController, + ), + ), + ], + ); + + await tester.pumpWidget( + UncontrolledProviderScope( + container: ref, + child: const TestDownloadLanguageButton('en'), + ), + ); await tester.tap(find.byType(DownloadLanguageButton)); await tester.pump(); diff --git a/test/download_languages_page_test.dart b/test/download_languages_page_test.dart index 6db6ba8..c4e379e 100644 --- a/test/download_languages_page_test.dart +++ b/test/download_languages_page_test.dart @@ -22,17 +22,19 @@ import 'updates_test.dart'; /// Test DownloadLanguagesPage class TestDownloadLanguagesPage extends ConsumerWidget { final TestObserver navigatorObserver; + const TestDownloadLanguagesPage(this.navigatorObserver, {super.key}); @override Widget build(BuildContext context, WidgetRef ref) { return MaterialApp( - locale: ref.watch(appLanguageProvider).locale, - localizationsDelegates: AppLocalizations.localizationsDelegates, - supportedLocales: AppLocalizations.supportedLocales, - onGenerateRoute: (settings) => generateRoutes(settings), - navigatorObservers: [navigatorObserver], - home: const DownloadLanguagesPage()); + locale: ref.watch(appLanguageProvider).locale, + localizationsDelegates: AppLocalizations.localizationsDelegates, + supportedLocales: AppLocalizations.supportedLocales, + onGenerateRoute: (settings) => generateRoutes(settings), + navigatorObservers: [navigatorObserver], + home: const DownloadLanguagesPage(), + ); } } @@ -41,31 +43,42 @@ void main() { SharedPreferences.setMockInitialValues({}); final prefs = await SharedPreferences.getInstance(); final testObserver = TestObserver(); - final ref = ProviderContainer(overrides: [ - appLanguageProvider.overrideWith(() => TestAppLanguage('en')), - languageProvider - .overrideWith(() => TestLanguageController(downloadedLanguages: [])), - sharedPrefsProvider.overrideWith((ref) => prefs) - ]); - await tester.pumpWidget(UncontrolledProviderScope( - container: ref, child: TestDownloadLanguagesPage(testObserver))); + final ref = ProviderContainer( + overrides: [ + appLanguageProvider.overrideWith(() => TestAppLanguage('en')), + languageProvider.overrideWith2( + (languageCode) => TestLanguageController(downloadedLanguages: []), + ), + sharedPrefsProvider.overrideWith((ref) => prefs), + ], + ); + await tester.pumpWidget( + UncontrolledProviderScope( + container: ref, + child: TestDownloadLanguagesPage(testObserver), + ), + ); expect(find.text(AppLocalizationsEn().downloadLanguages), findsOneWidget); - expect(find.text(AppLocalizationsEn().downloadLanguagesExplanation), - findsOneWidget); + expect( + find.text(AppLocalizationsEn().downloadLanguagesExplanation), + findsOneWidget, + ); expect(find.byType(LanguagesTable), findsOneWidget); expect(find.byType(ElevatedButton), findsNWidgets(2)); // Press the continue button expect(testObserver.replacedRoutes, isEmpty); await tester.tap( - find.widgetWithText(ElevatedButton, AppLocalizationsEn().continueText)); + find.widgetWithText(ElevatedButton, AppLocalizationsEn().continueText), + ); await tester.pump(); // Now we see the MissingAppLanguageDialog and close it expect(find.byType(MissingAppLanguageDialog), findsOneWidget); - await tester - .tap(find.widgetWithText(TextButton, AppLocalizationsEn().gotit)); + await tester.tap( + find.widgetWithText(TextButton, AppLocalizationsEn().gotit), + ); await tester.pump(); expect(find.byType(MissingAppLanguageDialog), findsNothing); @@ -75,59 +88,79 @@ void main() { // Now we press the continue button again - this time it should continue await tester.tap( - find.widgetWithText(ElevatedButton, AppLocalizationsEn().continueText)); + find.widgetWithText(ElevatedButton, AppLocalizationsEn().continueText), + ); await tester.pump(); expect(listEquals(testObserver.replacedRoutes, ['/home']), isTrue); -/* TODO for version 0.9 + /* TODO for version 0.9 expect(listEquals(testObserver.replacedRoutes, ['/onboarding/3']), isTrue); */ }); - testWidgets('Test DownloadLanguagesPage back button in German', - (WidgetTester tester) async { + testWidgets('Test DownloadLanguagesPage back button in German', ( + WidgetTester tester, + ) async { final testObserver = TestObserver(); - await tester.pumpWidget(ProviderScope(overrides: [ - appLanguageProvider.overrideWith(() => TestAppLanguage('de')), - languageStatusProvider.overrideWith(() => TestLanguageStatus()) - ], child: TestDownloadLanguagesPage(testObserver))); + await tester.pumpWidget( + ProviderScope( + overrides: [ + appLanguageProvider.overrideWith(() => TestAppLanguage('de')), + languageStatusProvider.overrideWith2((languageCode) => TestLanguageStatus()), + ], + child: TestDownloadLanguagesPage(testObserver), + ), + ); // Check that the page is translated expect(find.text(AppLocalizationsDe().downloadLanguages), findsOneWidget); - expect(find.text(AppLocalizationsDe().downloadLanguagesExplanation), - findsOneWidget); + expect( + find.text(AppLocalizationsDe().downloadLanguagesExplanation), + findsOneWidget, + ); // Click the back button expect(testObserver.replacedRoutes, isEmpty); - await tester - .tap(find.widgetWithText(ElevatedButton, AppLocalizationsDe().back)); + await tester.tap( + find.widgetWithText(ElevatedButton, AppLocalizationsDe().back), + ); await tester.pump(); expect(listEquals(testObserver.replacedRoutes, ['/onboarding/1']), isTrue); }); - testWidgets('Test skipping third onboarding step', - (WidgetTester tester) async { + testWidgets('Test skipping third onboarding step', ( + WidgetTester tester, + ) async { SharedPreferences.setMockInitialValues({'checkFrequency': 'weekly'}); final prefs = await SharedPreferences.getInstance(); final testObserver = TestObserver(); - final ref = ProviderContainer(overrides: [ - appLanguageProvider.overrideWith(() => TestAppLanguage('de')), - languageProvider - .overrideWith(() => TestLanguageController(downloadedLanguages: [])), - sharedPrefsProvider.overrideWith((ref) => prefs) - ]); - await tester.pumpWidget(UncontrolledProviderScope( - container: ref, child: TestDownloadLanguagesPage(testObserver))); + final ref = ProviderContainer( + overrides: [ + appLanguageProvider.overrideWith(() => TestAppLanguage('de')), + languageProvider.overrideWith2( + (languageCode) => TestLanguageController(downloadedLanguages: []), + ), + sharedPrefsProvider.overrideWith((ref) => prefs), + ], + ); + await tester.pumpWidget( + UncontrolledProviderScope( + container: ref, + child: TestDownloadLanguagesPage(testObserver), + ), + ); await ref.read(languageProvider('en').notifier).download(); await tester.pump(); await tester.tap( - find.widgetWithText(ElevatedButton, AppLocalizationsDe().continueText)); + find.widgetWithText(ElevatedButton, AppLocalizationsDe().continueText), + ); await tester.pump(); // We see MissingAppLanguageDialog and close it expect(find.byType(MissingAppLanguageDialog), findsOneWidget); - await tester - .tap(find.widgetWithText(TextButton, AppLocalizationsDe().gotit)); + await tester.tap( + find.widgetWithText(TextButton, AppLocalizationsDe().gotit), + ); await tester.pump(); expect(find.byType(MissingAppLanguageDialog), findsNothing); @@ -136,7 +169,8 @@ void main() { await tester.pump(); expect(testObserver.replacedRoutes, isEmpty); await tester.tap( - find.widgetWithText(ElevatedButton, AppLocalizationsDe().continueText)); + find.widgetWithText(ElevatedButton, AppLocalizationsDe().continueText), + ); await tester.pump(); // Testing getNextRoute() diff --git a/test/language_selection_button_test.dart b/test/language_selection_button_test.dart index 4171bbc..cbc45d8 100644 --- a/test/language_selection_button_test.dart +++ b/test/language_selection_button_test.dart @@ -34,7 +34,7 @@ void main() { (WidgetTester tester) async { await tester.pumpWidget(ProviderScope(overrides: [ appLanguageProvider.overrideWith(() => TestAppLanguage('en')), - languageProvider.overrideWith(() => TestLanguageController( + languageProvider.overrideWith2((languageCode) => TestLanguageController( downloadedLanguages: ['de'], pages: {'Healing': const Page('test', 'test', 'test', '1.0', null)})) ], child: const TestLanguagesButton())); @@ -57,7 +57,7 @@ void main() { (WidgetTester tester) async { await tester.pumpWidget(ProviderScope(overrides: [ appLanguageProvider.overrideWith(() => TestAppLanguage('de')), - languageProvider.overrideWith(() => TestLanguageController( + languageProvider.overrideWith2((languageCode) => TestLanguageController( downloadedLanguages: ['de', 'en', 'fr', 'es', 'ar'], pages: {'Healing': const Page('test', 'test', 'test', '1.0', null)})) ], child: const TestLanguagesButton())); @@ -100,7 +100,7 @@ void main() { container: container, child: ProviderScope(overrides: [ appLanguageProvider.overrideWith(() => TestAppLanguage('de')), - languageProvider.overrideWith(() => TestLanguageController( + languageProvider.overrideWith2((languageCode) => TestLanguageController( downloadedLanguages: availableLanguages, pages: { 'Healing': const Page('test', 'test', 'test', '1.0', null) diff --git a/test/languages_table_test.dart b/test/languages_table_test.dart index 47611f9..7acdbb2 100644 --- a/test/languages_table_test.dart +++ b/test/languages_table_test.dart @@ -19,56 +19,82 @@ class TestLanguagesTable extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { return MaterialApp( - locale: ref.watch(appLanguageProvider).locale, - localizationsDelegates: AppLocalizations.localizationsDelegates, - supportedLocales: AppLocalizations.supportedLocales, - home: const Scaffold(body: LanguagesTable())); + locale: ref.watch(appLanguageProvider).locale, + localizationsDelegates: AppLocalizations.localizationsDelegates, + supportedLocales: AppLocalizations.supportedLocales, + home: const Scaffold(body: LanguagesTable()), + ); } } void main() { - testWidgets('Basic test with no language downloaded, English as appLanguage', - (WidgetTester tester) async { - await tester.pumpWidget(ProviderScope(overrides: [ - appLanguageProvider.overrideWith(() => TestAppLanguage('en')), - languageProvider - .overrideWith(() => TestLanguageController(downloadedLanguages: [])), - languageStatusProvider.overrideWith(() => TestLanguageStatus()) - ], child: const TestLanguagesTable())); + testWidgets( + 'Basic test with no language downloaded, English as appLanguage', + (WidgetTester tester) async { + await tester.pumpWidget( + ProviderScope( + overrides: [ + appLanguageProvider.overrideWith(() => TestAppLanguage('en')), + languageProvider.overrideWith2( + (languageCode) => TestLanguageController(downloadedLanguages: []), + ), + languageStatusProvider.overrideWith2( + (languageCode) => TestLanguageStatus(), + ), + ], + child: const TestLanguagesTable(), + ), + ); - expect( - find.text('All languages ($countAvailableLanguages)'), findsOneWidget); - expect(find.text('German (de)'), findsOneWidget); - expect(find.text('English (en)'), findsOneWidget); - expect(find.byIcon(Icons.check), findsNothing); - expect(find.byIcon(Icons.delete), findsOneWidget); - expect(find.byIcon(Icons.download), - findsNWidgets(countAvailableLanguages + 1)); - expect(find.byIcon(Icons.refresh), findsNothing); + expect( + find.text('All languages ($countAvailableLanguages)'), + findsOneWidget, + ); + expect(find.text('German (de)'), findsOneWidget); + expect(find.text('English (en)'), findsOneWidget); + expect(find.byIcon(Icons.check), findsNothing); + expect(find.byIcon(Icons.delete), findsOneWidget); + expect( + find.byIcon(Icons.download), + findsNWidgets(countAvailableLanguages + 1), + ); + expect(find.byIcon(Icons.refresh), findsNothing); - // Check correct sorting - final germanPosition = tester.getTopLeft(find.text('German (de)')); - final englishPosition = tester.getTopLeft(find.text('English (en)')); - final frenchPosition = tester.getTopLeft(find.text('French (fr)')); - expect(englishPosition.dy < germanPosition.dy, isTrue); - expect(englishPosition.dy < frenchPosition.dy, isTrue); - expect(frenchPosition.dy < germanPosition.dy, isTrue); - }); - testWidgets('Basic test with only German downloaded, German as appLanguage', - (WidgetTester tester) async { - final ref = ProviderContainer(overrides: [ - appLanguageProvider.overrideWith(() => TestAppLanguage('de')), - availableLanguagesProvider.overrideWithValue(['de', 'en', 'fr']), - languageProvider.overrideWith( - () => TestLanguageController(downloadedLanguages: ['de'])), - languageStatusProvider - .overrideWith(() => TestLanguageStatus(langWithUpdates: ['de'])) - ]); - await tester.pumpWidget(UncontrolledProviderScope( - container: ref, child: const TestLanguagesTable())); + // Check correct sorting + final germanPosition = tester.getTopLeft(find.text('German (de)')); + final englishPosition = tester.getTopLeft(find.text('English (en)')); + final frenchPosition = tester.getTopLeft(find.text('French (fr)')); + expect(englishPosition.dy < germanPosition.dy, isTrue); + expect(englishPosition.dy < frenchPosition.dy, isTrue); + expect(frenchPosition.dy < germanPosition.dy, isTrue); + }, + ); + testWidgets('Basic test with only German downloaded, German as appLanguage', ( + WidgetTester tester, + ) async { + final ref = ProviderContainer( + overrides: [ + appLanguageProvider.overrideWith(() => TestAppLanguage('de')), + availableLanguagesProvider.overrideWithValue(['de', 'en', 'fr']), + languageProvider.overrideWith2( + (languageCode) => TestLanguageController(downloadedLanguages: ['de']), + ), + languageStatusProvider.overrideWith2( + (languageCode) => TestLanguageStatus(langWithUpdates: ['de']), + ), + ], + ); + await tester.pumpWidget( + UncontrolledProviderScope( + container: ref, + child: const TestLanguagesTable(), + ), + ); expect( - find.text('Alle Sprachen ($countAvailableLanguages)'), findsOneWidget); + find.text('Alle Sprachen ($countAvailableLanguages)'), + findsOneWidget, + ); expect(find.text('Deutsch (de)'), findsOneWidget); expect(find.text('Englisch (en)'), findsOneWidget); expect(find.byIcon(Icons.check), findsNWidgets(1)); diff --git a/test/languages_test.dart b/test/languages_test.dart index d28fd0e..2a3cdf4 100644 --- a/test/languages_test.dart +++ b/test/languages_test.dart @@ -14,6 +14,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:app4training/data/languages.dart'; import 'package:mocktail/mocktail.dart'; import 'package:path/path.dart' as path; + // ignore: implementation_imports, invalid_use_of_internal_member import 'package:riverpod/src/framework.dart' show $RefArg; @@ -29,8 +30,10 @@ class FakeDownloadAssetsController extends Fake // TODO use this class to test the startDownload() functionality @override - Future init( - {String assetDir = 'assets', bool useFullDirectoryPath = false}) async { + Future init({ + String assetDir = 'assets', + bool useFullDirectoryPath = false, + }) async { _assetDir = assetDir; initCalled = true; return; @@ -50,16 +53,17 @@ class FakeDownloadAssetsController extends Fake } @override - Future startDownload( - {required List assetsUrls, - bool? checkSize, - List uncompressDelegates = const [UnzipDelegate()], - Function(double p1)? onProgress, - Function()? onStartUnzipping, - Function()? onCancel, - Function()? onDone, - Map? requestQueryParams, - Map requestExtraHeaders = const {}}) async { + Future startDownload({ + required List assetsUrls, + bool? checkSize, + List uncompressDelegates = const [UnzipDelegate()], + Function(double p1)? onProgress, + Function()? onStartUnziping, + Function()? onCancel, + Function()? onDone, + Map? requestQueryParams, + Map requestExtraHeaders = const {}, + }) async { // TODO: implement startDownload startDownloadCalls += 1; return; @@ -68,16 +72,17 @@ class FakeDownloadAssetsController extends Fake class ThrowingDownloadAssetsController extends FakeDownloadAssetsController { @override - Future startDownload( - {required List assetsUrls, - bool? checkSize, - List uncompressDelegates = const [UnzipDelegate()], - Function(double p1)? onProgress, - Function()? onStartUnzipping, - Function()? onCancel, - Function()? onDone, - Map? requestQueryParams, - Map requestExtraHeaders = const {}}) async { + Future startDownload({ + required List assetsUrls, + bool? checkSize, + List uncompressDelegates = const [UnzipDelegate()], + Function(double p1)? onProgress, + Function()? onStartUnziping, + Function()? onCancel, + Function()? onDone, + Map? requestQueryParams, + Map requestExtraHeaders = const {}, + }) async { startDownloadCalls += 1; throw DioException(requestOptions: RequestOptions()); } @@ -93,15 +98,16 @@ class TestLanguageController extends LanguageController { final int _languageSize; // size in KB final Map _pages; // map of pages that are available final bool _initReturns; - TestLanguageController( - {List? downloadedLanguages, - int languageSize = 0, - Map pages = const {}, - initReturns = false}) - : _downloadedLanguages = downloadedLanguages, - _languageSize = languageSize, - _pages = pages, - _initReturns = initReturns; + + TestLanguageController({ + List? downloadedLanguages, + int languageSize = 0, + Map pages = const {}, + initReturns = false, + }) : _downloadedLanguages = downloadedLanguages, + _languageSize = languageSize, + _pages = pages, + _initReturns = initReturns; @override Language build() { @@ -113,21 +119,42 @@ class TestLanguageController extends LanguageController { if (_downloadedLanguages != null) { downloaded = _downloadedLanguages.contains(languageCode); } - return Language(downloaded ? languageCode : '', _pages, const [], const {}, - '', _languageSize, DateTime.utc(2023)); + return Language( + downloaded ? languageCode : '', + _pages, + const [], + const {}, + '', + _languageSize, + DateTime.utc(2023), + ); } @override Future download({bool force = false}) async { - state = Language(languageCode, _pages, const [], const {}, '', - _languageSize, DateTime.now().toUtc()); + state = Language( + languageCode, + _pages, + const [], + const {}, + '', + _languageSize, + DateTime.now().toUtc(), + ); return true; } @override Future deleteResources() async { - state = - Language('', const {}, const [], const {}, '', 0, DateTime.utc(2023)); + state = Language( + '', + const {}, + const [], + const {}, + '', + 0, + DateTime.utc(2023), + ); } @override @@ -141,7 +168,8 @@ class TestLanguageController extends LanguageController { /// Only structure/contents.json is existing (with dummy contents) /// But that's enough for Languages.lazyInit() Future createBasicFileSystem( - List downloadedLangs) async { + List downloadedLangs, +) async { var fileSystem = MemoryFileSystem(); for (final lang in downloadedLangs) { await fileSystem @@ -157,11 +185,17 @@ Future createBasicFileSystem( void main() { test('Test init() when no files are there', () async { var fakeController = FakeDownloadAssetsController(); - final ref = ProviderContainer(overrides: [ - languageProvider.overrideWith( - () => LanguageController(assetsController: fakeController)), - fileSystemProvider.overrideWith((ref) => MemoryFileSystem()) - ]); + final ref = ProviderContainer( + overrides: [ + languageProvider.overrideWith2( + (languageCode) => LanguageController( + languageCode: languageCode, + assetsController: fakeController, + ), + ), + fileSystemProvider.overrideWith((ref) => MemoryFileSystem()), + ], + ); final frTest = ref.read(languageProvider('fr').notifier); expect(await frTest.init(), false); expect(frTest.state.downloaded, false); @@ -171,11 +205,17 @@ void main() { test('Test lazyInit() when no files are there', () async { var fakeController = FakeDownloadAssetsController(); - final ref = ProviderContainer(overrides: [ - languageProvider.overrideWith( - () => LanguageController(assetsController: fakeController)), - fileSystemProvider.overrideWith((ref) => MemoryFileSystem()) - ]); + final ref = ProviderContainer( + overrides: [ + languageProvider.overrideWith2( + (languageCode) => LanguageController( + languageCode: languageCode, + assetsController: fakeController, + ), + ), + fileSystemProvider.overrideWith((ref) => MemoryFileSystem()), + ], + ); final frTest = ref.read(languageProvider('fr').notifier); expect(await frTest.lazyInit(), false); expect(frTest.state.downloaded, false); @@ -183,11 +223,17 @@ void main() { test('Test that download() starts the download', () async { var fakeController = FakeDownloadAssetsController(); - final ref = ProviderContainer(overrides: [ - languageProvider.overrideWith( - () => LanguageController(assetsController: fakeController)), - fileSystemProvider.overrideWith((ref) => MemoryFileSystem()) - ]); + final ref = ProviderContainer( + overrides: [ + languageProvider.overrideWith2( + (languageCode) => LanguageController( + languageCode: languageCode, + assetsController: fakeController, + ), + ), + fileSystemProvider.overrideWith((ref) => MemoryFileSystem()), + ], + ); final frTest = ref.read(languageProvider('fr').notifier); // as we're mocking, the language won't be available @@ -200,11 +246,17 @@ void main() { test('Test failing download', () async { var throwingController = ThrowingDownloadAssetsController(); - final ref = ProviderContainer(overrides: [ - languageProvider.overrideWith( - () => LanguageController(assetsController: throwingController)), - fileSystemProvider.overrideWith((ref) => MemoryFileSystem()) - ]); + final ref = ProviderContainer( + overrides: [ + languageProvider.overrideWith2( + (languageCode) => LanguageController( + languageCode: languageCode, + assetsController: throwingController, + ), + ), + fileSystemProvider.overrideWith((ref) => MemoryFileSystem()), + ], + ); final frTest = ref.read(languageProvider('fr').notifier); expect(await frTest.download(), false); @@ -216,11 +268,17 @@ void main() { group('Test correct behavior after downloading', () { group('Test error handling of incorrect files / structure', () { test('Test error handling when no files can be found at all', () async { - final ref = ProviderContainer(overrides: [ - languageProvider.overrideWith(() => LanguageController( - assetsController: createMockDownloadAssetsController())), - fileSystemProvider.overrideWith((ref) => MemoryFileSystem()) - ]); + final ref = ProviderContainer( + overrides: [ + languageProvider.overrideWith2( + (languageCode) => LanguageController( + languageCode: languageCode, + assetsController: createMockDownloadAssetsController(), + ), + ), + fileSystemProvider.overrideWith((ref) => MemoryFileSystem()), + ], + ); final deTest = ref.read(languageProvider('de').notifier); expect(await deTest.init(), false); @@ -232,15 +290,22 @@ void main() { await fileSystem .directory('assets-de/html-de-main/structure') .create(recursive: true); - var contentsJson = - fileSystem.file('assets-de/html-de-main/structure/contents.json'); + var contentsJson = fileSystem.file( + 'assets-de/html-de-main/structure/contents.json', + ); await contentsJson.writeAsString('invalid'); - final ref = ProviderContainer(overrides: [ - languageProvider.overrideWith(() => LanguageController( - assetsController: createMockDownloadAssetsController())), - fileSystemProvider.overrideWith((ref) => fileSystem) - ]); + final ref = ProviderContainer( + overrides: [ + languageProvider.overrideWith2( + (languageCode) => LanguageController( + languageCode: languageCode, + assetsController: createMockDownloadAssetsController(), + ), + ), + fileSystemProvider.overrideWith((ref) => fileSystem), + ], + ); final deTest = ref.read(languageProvider('de').notifier); expect(await deTest.init(), false); expect(deTest.state.downloaded, false); @@ -254,58 +319,91 @@ void main() { .directory('assets-de/html-de-main/structure') .create(recursive: true); var readFileSystem = ChrootFileSystem( - const LocalFileSystem(), path.canonicalize('test/')); + const LocalFileSystem(), + path.canonicalize('test/'), + ); String jsonPath = 'assets-de/html-de-main/structure/contents.json'; var contentsJson = fileSystem.file(jsonPath); - await contentsJson - .writeAsString(await readFileSystem.file(jsonPath).readAsString()); - - final ref = ProviderContainer(overrides: [ - languageProvider.overrideWith(() => LanguageController( - assetsController: createMockDownloadAssetsController())), - fileSystemProvider.overrideWith((ref) => fileSystem) - ]); + await contentsJson.writeAsString( + await readFileSystem.file(jsonPath).readAsString(), + ); + + final ref = ProviderContainer( + overrides: [ + languageProvider.overrideWith2( + (languageCode) => LanguageController( + languageCode: languageCode, + assetsController: createMockDownloadAssetsController(), + ), + ), + fileSystemProvider.overrideWith((ref) => fileSystem), + ], + ); // init() should work (even if expected HTML files are missing) final deTest = ref.read(languageProvider('de').notifier); expect(await deTest.init(), true); expect(deTest.state.downloaded, true); - expect(deTest.state.downloadTimestamp.compareTo(DateTime(2023)), - greaterThan(0)); + expect( + deTest.state.downloadTimestamp.compareTo(DateTime(2023)), + greaterThan(0), + ); }); }); test('Test lazyInit() when language is available', () async { // We construct a file system in memory with structure/contents.json final fileSystem = await createBasicFileSystem(['de']); - final ref = ProviderContainer(overrides: [ - languageProvider.overrideWith(() => LanguageController( - assetsController: createMockDownloadAssetsController())), - fileSystemProvider.overrideWith((ref) => fileSystem) - ]); + final ref = ProviderContainer( + overrides: [ + languageProvider.overrideWith2( + (languageCode) => LanguageController( + languageCode: languageCode, + assetsController: createMockDownloadAssetsController(), + ), + ), + fileSystemProvider.overrideWith((ref) => fileSystem), + ], + ); expect(await ref.read(languageProvider('de').notifier).lazyInit(), true); final deStatus = ref.read(languageProvider('de')); expect(deStatus.downloaded, true); expect(deStatus.path, equals('assets-de/html-de-main')); expect( - deStatus.downloadTimestamp.compareTo(DateTime(2023)), greaterThan(0)); + deStatus.downloadTimestamp.compareTo(DateTime(2023)), + greaterThan(0), + ); }); test('Test everything with real content from test/assets-de/', () async { - final ref = ProviderContainer(overrides: [ - languageProvider.overrideWith(() => LanguageController( - assetsController: createMockDownloadAssetsController())), - fileSystemProvider.overrideWith((ref) => ChrootFileSystem( - const LocalFileSystem(), path.canonicalize('test/'))) - ]); + final ref = ProviderContainer( + overrides: [ + languageProvider.overrideWith2( + (languageCode) => LanguageController( + languageCode: languageCode, + assetsController: createMockDownloadAssetsController(), + ), + ), + fileSystemProvider.overrideWith( + (ref) => ChrootFileSystem( + const LocalFileSystem(), + path.canonicalize('test/'), + ), + ), + ], + ); final deTest = ref.read(languageProvider('de').notifier); expect(await deTest.init(), true); // Loads Gottes_Geschichte_(fünf_Finger).html - String content = await ref.read(pageContentProvider( - (name: "God's_Story_(five_fingers)", langCode: 'de')).future); + String content = await ref.read( + pageContentProvider(( + name: "God's_Story_(five_fingers)", + langCode: 'de', + )).future, + ); expect(content, startsWith('

Gottes Geschichte')); // The link of this image should have been replaced with image content @@ -314,19 +412,22 @@ void main() { // This should still be there as the image file is missing expect(content, contains('src="files/Hand_5.png"')); // PDF should be available - expect(deTest.state.pages['Forgiving_Step_by_Step']?.pdfPath, - equals('assets-de/pdf-de-main/Schritte_der_Vergebung.pdf')); + expect( + deTest.state.pages['Forgiving_Step_by_Step']?.pdfPath, + equals('assets-de/pdf-de-main/Schritte_der_Vergebung.pdf'), + ); // This PDF is missing expect(deTest.state.pages['MissingTest']?.pdfPath, isNull); // Test Languages.getPageTitles() expect( - deTest.state.getPageTitles().values, - orderedEquals(const [ - 'Gottes Geschichte (fünf Finger)', - 'Schritte der Vergebung', - 'MissingTest' - ])); + deTest.state.getPageTitles().values, + orderedEquals(const [ + 'Gottes Geschichte (fünf Finger)', + 'Schritte der Vergebung', + 'MissingTest', + ]), + ); expect(deTest.state.sizeInKB, 147); expect(deTest.state.path, equals('assets-de/html-de-main')); @@ -336,18 +437,22 @@ void main() { ref.read(pageContentProvider((name: 'Invalid', langCode: 'de'))); await Future.delayed(const Duration(milliseconds: 500)); - final missingResult = - ref.read(pageContentProvider((name: 'MissingTest', langCode: 'de'))); + final missingResult = ref.read( + pageContentProvider((name: 'MissingTest', langCode: 'de')), + ); expect(missingResult.hasError, true); // In Riverpod v3, errors are wrapped in ProviderException var error = missingResult.error; if (error is ProviderException) error = error.exception; expect(error, isA()); - expect((error as LanguageCorruptedException).exception, - isA()); + expect( + (error as LanguageCorruptedException).exception, + isA(), + ); - final invalidResult = - ref.read(pageContentProvider((name: 'Invalid', langCode: 'de'))); + final invalidResult = ref.read( + pageContentProvider((name: 'Invalid', langCode: 'de')), + ); expect(invalidResult.hasError, true); error = invalidResult.error; if (error is ProviderException) error = error.exception; @@ -356,18 +461,24 @@ void main() { }); test('Test diskUsageProvider', () { - final ref = ProviderContainer(overrides: [ - languageProvider - .overrideWith(() => TestLanguageController(languageSize: 42)), - ]); + final ref = ProviderContainer( + overrides: [ + languageProvider.overrideWith2( + (languageCode) => TestLanguageController(languageSize: 42), + ), + ], + ); expect(ref.read(diskUsageProvider), countAvailableLanguages * 42); }); test('Test countDownloadedLanguagesProvider', () { - final ref = ProviderContainer(overrides: [ - languageProvider.overrideWith(() => - TestLanguageController(downloadedLanguages: ['de', 'fr', 'en'])), - ]); + final ref = ProviderContainer( + overrides: [ + languageProvider.overrideWith2( + (languageCode) => TestLanguageController(downloadedLanguages: ['de', 'fr', 'en']), + ), + ], + ); expect(ref.read(countDownloadedLanguagesProvider), 3); }); } diff --git a/test/main_drawer_test.dart b/test/main_drawer_test.dart index f81cdb7..e62febd 100644 --- a/test/main_drawer_test.dart +++ b/test/main_drawer_test.dart @@ -8,6 +8,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:app4training/data/globals.dart'; import 'package:shared_preferences/shared_preferences.dart'; + // ignore: implementation_imports, invalid_use_of_internal_member import 'package:riverpod/src/framework.dart' show $RefArg; @@ -27,7 +28,7 @@ class CustomTestLanguageController extends LanguageController { 'Forgiving_Step_by_Step': 'Schritte der Vergebung', 'Hearing_from_God': 'Gottes Reden wahrnehmen', 'Training_Meeting_Outline': 'Ablauf der Trainings-Treffen', - 'Overcoming_Fear_and_Anger': 'Angst und Wut überwinden' + 'Overcoming_Fear_and_Anger': 'Angst und Wut überwinden', }; Map pages = {}; List pageIndex = []; @@ -49,29 +50,33 @@ class TestApp extends ConsumerWidget { final String pageName; final String pageLanguage; final TestObserver? _navigatorObserver; + // for snackbar testing final GlobalKey? scaffoldMessengerKey; - const TestApp( - {this.pageName = 'Forgiving_Step_by_Step', - this.pageLanguage = 'en', - TestObserver? navigatorObserver, - this.scaffoldMessengerKey, - super.key}) - : _navigatorObserver = navigatorObserver; + + const TestApp({ + this.pageName = 'Forgiving_Step_by_Step', + this.pageLanguage = 'en', + TestObserver? navigatorObserver, + this.scaffoldMessengerKey, + super.key, + }) : _navigatorObserver = navigatorObserver; @override Widget build(BuildContext context, WidgetRef ref) { return MaterialApp( - locale: ref.watch(appLanguageProvider).locale, - localizationsDelegates: AppLocalizations.localizationsDelegates, - supportedLocales: AppLocalizations.supportedLocales, - onGenerateRoute: (settings) => generateRoutes(settings), - scaffoldMessengerKey: scaffoldMessengerKey, - navigatorObservers: - (_navigatorObserver != null) ? [_navigatorObserver] : [], - home: Scaffold( - appBar: AppBar(title: const Text('4training')), - drawer: MainDrawer(pageName, pageLanguage))); + locale: ref.watch(appLanguageProvider).locale, + localizationsDelegates: AppLocalizations.localizationsDelegates, + supportedLocales: AppLocalizations.supportedLocales, + onGenerateRoute: (settings) => generateRoutes(settings), + scaffoldMessengerKey: scaffoldMessengerKey, + navigatorObservers: + (_navigatorObserver != null) ? [_navigatorObserver] : [], + home: Scaffold( + appBar: AppBar(title: const Text('4training')), + drawer: MainDrawer(pageName, pageLanguage), + ), + ); } } @@ -80,11 +85,18 @@ void main() { SharedPreferences.setMockInitialValues({}); final prefs = await SharedPreferences.getInstance(); final testObserver = TestObserver(); - await tester.pumpWidget(ProviderScope(overrides: [ - appLanguageProvider.overrideWith(() => TestAppLanguage('en')), - languageProvider.overrideWith(() => CustomTestLanguageController()), - sharedPrefsProvider.overrideWithValue(prefs) - ], child: TestApp(navigatorObserver: testObserver))); + await tester.pumpWidget( + ProviderScope( + overrides: [ + appLanguageProvider.overrideWith(() => TestAppLanguage('en')), + languageProvider.overrideWith2( + (languageCode) => CustomTestLanguageController(), + ), + sharedPrefsProvider.overrideWithValue(prefs), + ], + child: TestApp(navigatorObserver: testObserver), + ), + ); expect(find.byIcon(Icons.menu), findsOneWidget); expect(find.text('Inner Healing'), findsNothing); @@ -122,11 +134,18 @@ void main() { SharedPreferences.setMockInitialValues({}); final prefs = await SharedPreferences.getInstance(); final testObserver = TestObserver(); - await tester.pumpWidget(ProviderScope(overrides: [ - appLanguageProvider.overrideWith(() => TestAppLanguage('de')), - languageProvider.overrideWith(() => CustomTestLanguageController()), - sharedPrefsProvider.overrideWithValue(prefs) - ], child: TestApp(pageLanguage: 'de', navigatorObserver: testObserver))); + await tester.pumpWidget( + ProviderScope( + overrides: [ + appLanguageProvider.overrideWith(() => TestAppLanguage('de')), + languageProvider.overrideWith2( + (languageCode) => CustomTestLanguageController(), + ), + sharedPrefsProvider.overrideWithValue(prefs), + ], + child: TestApp(pageLanguage: 'de', navigatorObserver: testObserver), + ), + ); expect(find.byIcon(Icons.menu), findsOneWidget); expect(find.text('Innere Heilung'), findsNothing); @@ -153,16 +172,24 @@ void main() { expect(testObserver.routes.last, equals('/view/Hearing_from_God/de')); }); - testWidgets('Test that we continue in selected different language', - (WidgetTester tester) async { + testWidgets('Test that we continue in selected different language', ( + WidgetTester tester, + ) async { SharedPreferences.setMockInitialValues({}); final prefs = await SharedPreferences.getInstance(); final testObserver = TestObserver(); - await tester.pumpWidget(ProviderScope(overrides: [ - appLanguageProvider.overrideWith(() => TestAppLanguage('de')), - languageProvider.overrideWith(() => CustomTestLanguageController()), - sharedPrefsProvider.overrideWithValue(prefs) - ], child: TestApp(pageLanguage: 'en', navigatorObserver: testObserver))); + await tester.pumpWidget( + ProviderScope( + overrides: [ + appLanguageProvider.overrideWith(() => TestAppLanguage('de')), + languageProvider.overrideWith2( + (languageCode) => CustomTestLanguageController(), + ), + sharedPrefsProvider.overrideWithValue(prefs), + ], + child: TestApp(pageLanguage: 'en', navigatorObserver: testObserver), + ), + ); final ScaffoldState state = tester.firstState(find.byType(Scaffold)); state.openDrawer(); @@ -175,50 +202,70 @@ void main() { expect(testObserver.routes.last, equals('/view/Hearing_from_God/en')); }); - testWidgets('Test fallback when worksheet is not available in other language', - (WidgetTester tester) async { - SharedPreferences.setMockInitialValues({}); - final prefs = await SharedPreferences.getInstance(); - final testObserver = TestObserver(); - final ref = ProviderContainer(overrides: [ - appLanguageProvider.overrideWith(() => TestAppLanguage('de')), - languageProvider.overrideWith(() => CustomTestLanguageController()), - sharedPrefsProvider.overrideWithValue(prefs) - ]); - addTearDown(ref.dispose); - await tester.pumpWidget(UncontrolledProviderScope( - container: ref, - child: TestApp( + testWidgets( + 'Test fallback when worksheet is not available in other language', + (WidgetTester tester) async { + SharedPreferences.setMockInitialValues({}); + final prefs = await SharedPreferences.getInstance(); + final testObserver = TestObserver(); + final ref = ProviderContainer( + overrides: [ + appLanguageProvider.overrideWith(() => TestAppLanguage('de')), + languageProvider.overrideWith2( + (languageCode) => CustomTestLanguageController(), + ), + sharedPrefsProvider.overrideWithValue(prefs), + ], + ); + addTearDown(ref.dispose); + await tester.pumpWidget( + UncontrolledProviderScope( + container: ref, + child: TestApp( pageName: 'Prayer', pageLanguage: 'fr', scaffoldMessengerKey: ref.read(scaffoldMessengerKeyProvider), - navigatorObserver: testObserver))); - - final ScaffoldState state = tester.firstState(find.byType(Scaffold)); - state.openDrawer(); - await tester.pumpAndSettle(); - - // Currently we have Prayer/fr open. Hearing from God is missing in French, - // so when clicking on it we should get redirected to its German version - await tester.tap(find.text('Gottes Reden wahrnehmen')); - await tester.pump(); - expect(testObserver.routes.last, equals('/view/Hearing_from_God/de')); - // Snackbar visible? - expect(find.textContaining('Sprache zurückgesetzt auf Deutsch'), - findsOneWidget); - // Let the SnackBar timer complete to avoid pending timer error in v3 - await tester.pumpAndSettle(const Duration(seconds: 5)); - }); - - testWidgets('Make sure drawer is closed after returning from settings', - (WidgetTester tester) async { + navigatorObserver: testObserver, + ), + ), + ); + + final ScaffoldState state = tester.firstState(find.byType(Scaffold)); + state.openDrawer(); + await tester.pumpAndSettle(); + + // Currently we have Prayer/fr open. Hearing from God is missing in French, + // so when clicking on it we should get redirected to its German version + await tester.tap(find.text('Gottes Reden wahrnehmen')); + await tester.pump(); + expect(testObserver.routes.last, equals('/view/Hearing_from_God/de')); + // Snackbar visible? + expect( + find.textContaining('Sprache zurückgesetzt auf Deutsch'), + findsOneWidget, + ); + // Let the SnackBar timer complete to avoid pending timer error in v3 + await tester.pumpAndSettle(const Duration(seconds: 5)); + }, + ); + + testWidgets('Make sure drawer is closed after returning from settings', ( + WidgetTester tester, + ) async { SharedPreferences.setMockInitialValues({}); final prefs = await SharedPreferences.getInstance(); - await tester.pumpWidget(ProviderScope(overrides: [ - appLanguageProvider.overrideWith(() => TestAppLanguage('en')), - languageProvider.overrideWith(() => CustomTestLanguageController()), - sharedPrefsProvider.overrideWith((ref) => prefs) - ], child: const TestApp())); + await tester.pumpWidget( + ProviderScope( + overrides: [ + appLanguageProvider.overrideWith(() => TestAppLanguage('en')), + languageProvider.overrideWith2( + (languageCode) => CustomTestLanguageController(), + ), + sharedPrefsProvider.overrideWith((ref) => prefs), + ], + child: const TestApp(), + ), + ); expect(find.byIcon(Icons.menu), findsOneWidget); @@ -238,13 +285,20 @@ void main() { expect(find.text('Essentials'), findsNothing); }); - testWidgets('Test error message when appLanguage is not downloaded', - (WidgetTester tester) async { - await tester.pumpWidget(ProviderScope(overrides: [ - appLanguageProvider.overrideWith(() => TestAppLanguage('de')), - languageProvider - .overrideWith(() => TestLanguageController(downloadedLanguages: [])), - ], child: const TestApp())); + testWidgets('Test error message when appLanguage is not downloaded', ( + WidgetTester tester, + ) async { + await tester.pumpWidget( + ProviderScope( + overrides: [ + appLanguageProvider.overrideWith(() => TestAppLanguage('de')), + languageProvider.overrideWith2( + (languageCode) => TestLanguageController(downloadedLanguages: []), + ), + ], + child: const TestApp(), + ), + ); expect(find.text('Einstellungen'), findsNothing); final ScaffoldState state = tester.firstState(find.byType(Scaffold)); diff --git a/test/routes_test.dart b/test/routes_test.dart index f0b262f..3bb3f03 100644 --- a/test/routes_test.dart +++ b/test/routes_test.dart @@ -36,6 +36,7 @@ class TestObserver extends NavigatorObserver { class TestApp extends StatelessWidget { final TestObserver observer; + const TestApp(this.observer, {super.key}); @override @@ -55,9 +56,12 @@ void main() { final prefs = await SharedPreferences.getInstance(); final TestObserver observer = TestObserver(); - await tester.pumpWidget(ProviderScope( + await tester.pumpWidget( + ProviderScope( overrides: [sharedPrefsProvider.overrideWithValue(prefs)], - child: TestApp(observer))); + child: TestApp(observer), + ), + ); // Test that onboarding process get's started on first app usage expect(find.byType(TestApp), findsOneWidget); @@ -66,18 +70,24 @@ void main() { expect(find.byType(WelcomePage), findsOneWidget); // Test second onboarding step - unawaited(Navigator.of(tester.element(find.byType(WelcomePage))) - .pushReplacementNamed('/onboarding/2')); + unawaited( + Navigator.of( + tester.element(find.byType(WelcomePage)), + ).pushReplacementNamed('/onboarding/2'), + ); await tester.pumpAndSettle(); expect(find.byType(DownloadLanguagesPage), findsOneWidget); // Go back again - unawaited(Navigator.of(tester.element(find.byType(DownloadLanguagesPage))) - .pushReplacementNamed('/onboarding/1')); + unawaited( + Navigator.of( + tester.element(find.byType(DownloadLanguagesPage)), + ).pushReplacementNamed('/onboarding/1'), + ); await tester.pumpAndSettle(); expect(find.byType(WelcomePage), findsOneWidget); -/* TODO for version 0.9 + /* TODO for version 0.9 // Test third onboarding step unawaited(Navigator.of(tester.element(find.byType(WelcomePage))) .pushReplacementNamed('/onboarding/3')); @@ -87,13 +97,14 @@ void main() { // Test that routes are handled expect( - observer.replacedRoutes, - orderedEquals([ - '/onboarding/1', - '/onboarding/2', - '/onboarding/1', -// '/onboarding/3' - ])); + observer.replacedRoutes, + orderedEquals([ + '/onboarding/1', + '/onboarding/2', + '/onboarding/1', + // '/onboarding/3' + ]), + ); }); testWidgets('Test normal startup', (WidgetTester tester) async { @@ -101,32 +112,45 @@ void main() { final prefs = await SharedPreferences.getInstance(); final TestObserver observer = TestObserver(); - await tester.pumpWidget(ProviderScope(overrides: [ - languageProvider - .overrideWith(() => TestLanguageController(initReturns: true)), - sharedPrefsProvider.overrideWithValue(prefs) - ], child: TestApp(observer))); + await tester.pumpWidget( + ProviderScope( + overrides: [ + languageProvider.overrideWith2( + (languageCode) => TestLanguageController(initReturns: true), + ), + sharedPrefsProvider.overrideWithValue(prefs), + ], + child: TestApp(observer), + ), + ); // Test initial route / expect(find.byType(TestApp), findsOneWidget); expect(find.byType(StartupPage), findsOneWidget); // Test home page - unawaited(Navigator.of(tester.element(find.byType(StartupPage))) - .pushNamed('/home')); + unawaited( + Navigator.of(tester.element(find.byType(StartupPage))).pushNamed('/home'), + ); await tester.pumpAndSettle(); expect(find.byType(HomePage), findsOneWidget); // Test settings page - unawaited(Navigator.of(tester.element(find.byType(HomePage))) - .pushNamed('/settings')); + unawaited( + Navigator.of( + tester.element(find.byType(HomePage)), + ).pushNamed('/settings'), + ); await tester.pumpAndSettle(); expect(find.byType(SettingsPage), findsOneWidget); // Test viewing the forgiveness page in English const String viewRoute = '/view/Forgiving_Step_by_Step/en'; - unawaited(Navigator.of(tester.element(find.byType(SettingsPage))) - .pushNamed(viewRoute)); + unawaited( + Navigator.of( + tester.element(find.byType(SettingsPage)), + ).pushNamed(viewRoute), + ); await tester.pumpAndSettle(); expect(find.byType(ViewPage), findsOneWidget); ViewPage viewPage = @@ -136,30 +160,40 @@ void main() { // Test that routes are handled expect( - observer.routes, orderedEquals(['/', '/home', '/settings', viewRoute])); + observer.routes, + orderedEquals(['/', '/home', '/settings', viewRoute]), + ); }); testWidgets('Test some edge cases', (WidgetTester tester) async { SharedPreferences.setMockInitialValues({'appLanguage': 'system'}); final prefs = await SharedPreferences.getInstance(); - await tester.pumpWidget(ProviderScope( + await tester.pumpWidget( + ProviderScope( overrides: [sharedPrefsProvider.overrideWithValue(prefs)], - child: TestApp(TestObserver()))); + child: TestApp(TestObserver()), + ), + ); // All of the following errorneous routes should result in showing /home - unawaited(Navigator.of(tester.element(find.byType(StartupPage))) - .pushNamed('/view')); + unawaited( + Navigator.of(tester.element(find.byType(StartupPage))).pushNamed('/view'), + ); await tester.pumpAndSettle(); expect(find.byType(HomePage), findsOneWidget); - unawaited(Navigator.of(tester.element(find.byType(HomePage))) - .pushNamed('/view//')); + unawaited( + Navigator.of(tester.element(find.byType(HomePage))).pushNamed('/view//'), + ); await tester.pumpAndSettle(); expect(find.byType(HomePage), findsOneWidget); - unawaited(Navigator.of(tester.element(find.byType(HomePage))) - .pushNamed('/view/Forgiving_Step_by_Step/')); + unawaited( + Navigator.of( + tester.element(find.byType(HomePage)), + ).pushNamed('/view/Forgiving_Step_by_Step/'), + ); await tester.pumpAndSettle(); expect(find.byType(HomePage), findsOneWidget); }); @@ -167,12 +201,18 @@ void main() { testWidgets('Test unknown route', (WidgetTester tester) async { SharedPreferences.setMockInitialValues({'appLanguage': 'system'}); final prefs = await SharedPreferences.getInstance(); - await tester.pumpWidget(ProviderScope( + await tester.pumpWidget( + ProviderScope( overrides: [sharedPrefsProvider.overrideWithValue(prefs)], - child: TestApp(TestObserver()))); + child: TestApp(TestObserver()), + ), + ); - unawaited(Navigator.of(tester.element(find.byType(StartupPage))) - .pushNamed('/unknown')); + unawaited( + Navigator.of( + tester.element(find.byType(StartupPage)), + ).pushNamed('/unknown'), + ); await tester.pumpAndSettle(); expect(find.byType(ErrorPage), findsOneWidget); expect(find.textContaining('Unknown route /unknown'), findsOneWidget); diff --git a/test/settings_page_test.dart b/test/settings_page_test.dart index 4a81e8b..97a2c2d 100644 --- a/test/settings_page_test.dart +++ b/test/settings_page_test.dart @@ -20,10 +20,11 @@ class TestSettingsPage extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { return MaterialApp( - locale: ref.watch(appLanguageProvider).locale, - localizationsDelegates: AppLocalizations.localizationsDelegates, - supportedLocales: AppLocalizations.supportedLocales, - home: const SettingsPage()); + locale: ref.watch(appLanguageProvider).locale, + localizationsDelegates: AppLocalizations.localizationsDelegates, + supportedLocales: AppLocalizations.supportedLocales, + home: const SettingsPage(), + ); } } @@ -32,13 +33,17 @@ void main() { SharedPreferences.setMockInitialValues({}); final prefs = await SharedPreferences.getInstance(); - await tester.pumpWidget(ProviderScope(overrides: [ - sharedPrefsProvider.overrideWithValue(prefs), - ], child: const TestSettingsPage())); + await tester.pumpWidget( + ProviderScope( + overrides: [sharedPrefsProvider.overrideWithValue(prefs)], + child: const TestSettingsPage(), + ), + ); expect(find.byType(DropdownButtonAppLanguage), findsOneWidget); - BuildContext context = - tester.element(find.byType(DropdownButtonAppLanguage)); + BuildContext context = tester.element( + find.byType(DropdownButtonAppLanguage), + ); final providerContainer = ProviderScope.containerOf(context); // Test changing language through the appLanguageProvider @@ -57,27 +62,38 @@ void main() { await tester.pump(); await tester.tap(find.text('Deutsch (de)')); await tester.pump(); - expect(AppLocalizations.of(context).appLanguage, - equals(AppLocalizationsDe().appLanguage)); + expect( + AppLocalizations.of(context).appLanguage, + equals(AppLocalizationsDe().appLanguage), + ); expect(find.text(AppLocalizationsDe().appLanguage), findsOneWidget); expect(find.text(AppLocalizationsEn().appLanguage), findsNothing); expect(prefs.getString('appLanguage'), equals('de')); }); - testWidgets('Test displaying memory usage on settings page', - (WidgetTester tester) async { + testWidgets('Test displaying memory usage on settings page', ( + WidgetTester tester, + ) async { SharedPreferences.setMockInitialValues({}); final prefs = await SharedPreferences.getInstance(); - await tester.pumpWidget(ProviderScope(overrides: [ - appLanguageProvider.overrideWith(() => TestAppLanguage('en')), - languageProvider - .overrideWith(() => TestLanguageController(languageSize: 42)), - sharedPrefsProvider.overrideWith((ref) => prefs) - ], child: const TestSettingsPage())); + await tester.pumpWidget( + ProviderScope( + overrides: [ + appLanguageProvider.overrideWith(() => TestAppLanguage('en')), + languageProvider.overrideWith2( + (languageCode) => TestLanguageController(languageSize: 42), + ), + sharedPrefsProvider.overrideWith((ref) => prefs), + ], + child: const TestSettingsPage(), + ), + ); int expectedSize = 42 * countAvailableLanguages; expect(find.textContaining('$expectedSize kB'), findsOneWidget); // language counter visibility basic test - expect(find.textContaining('($countAvailableLanguages languages)'), - findsOneWidget); + expect( + find.textContaining('($countAvailableLanguages languages)'), + findsOneWidget, + ); }); } diff --git a/test/share_button_test.dart b/test/share_button_test.dart index fb39bd9..6d4beee 100644 --- a/test/share_button_test.dart +++ b/test/share_button_test.dart @@ -21,33 +21,42 @@ class TestShareButton extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { return MaterialApp( - locale: ref.watch(appLanguageProvider).locale, - localizationsDelegates: AppLocalizations.localizationsDelegates, - supportedLocales: AppLocalizations.supportedLocales, - // we need ViewPage here because ShareButton uses - // context.findAncestorWidgetOfExactType() to get current page - home: const ViewPage('Healing', 'de')); + locale: ref.watch(appLanguageProvider).locale, + localizationsDelegates: AppLocalizations.localizationsDelegates, + supportedLocales: AppLocalizations.supportedLocales, + // we need ViewPage here because ShareButton uses + // context.findAncestorWidgetOfExactType() to get current page + home: const ViewPage('Healing', 'de'), + ); } } /// Find an ImageIcon with an AssetImage by the name of the asset /// (from our asset/ folder) Finder findAssetImageIcon(String assetName, [Color? color]) { - return find.byWidgetPredicate((Widget widget) => - widget is ImageIcon && - ((color == null) || (widget.color == color)) && - widget.image is AssetImage && - (widget.image as AssetImage).assetName == assetName); + return find.byWidgetPredicate( + (Widget widget) => + widget is ImageIcon && + ((color == null) || (widget.color == color)) && + widget.image is AssetImage && + (widget.image as AssetImage).assetName == assetName, + ); } class MockShareService extends Mock implements ShareService {} void main() { - testWidgets('Smoke test: open and close the share menu (English locale)', - (WidgetTester tester) async { - await tester.pumpWidget(ProviderScope(overrides: [ - appLanguageProvider.overrideWith(() => TestAppLanguage('en')), - ], child: const TestShareButton())); + testWidgets('Smoke test: open and close the share menu (English locale)', ( + WidgetTester tester, + ) async { + await tester.pumpWidget( + ProviderScope( + overrides: [ + appLanguageProvider.overrideWith(() => TestAppLanguage('en')), + ], + child: const TestShareButton(), + ), + ); expect(find.byIcon(Icons.share), findsOneWidget); expect(find.text('Open PDF'), findsNothing); @@ -73,9 +82,14 @@ void main() { }); testWidgets('Test when PDFs are not available', (WidgetTester tester) async { - await tester.pumpWidget(ProviderScope(overrides: [ - appLanguageProvider.overrideWith(() => TestAppLanguage('de')) - ], child: const TestShareButton())); + await tester.pumpWidget( + ProviderScope( + overrides: [ + appLanguageProvider.overrideWith(() => TestAppLanguage('de')), + ], + child: const TestShareButton(), + ), + ); expect(find.byIcon(Icons.share), findsOneWidget); expect(find.text('PDF öffnen'), findsNothing); @@ -117,29 +131,38 @@ void main() { const String testPath = '/path/to/Healing.pdf'; final mockShareService = MockShareService(); - when(() => mockShareService.share(any(that: isA()))) - .thenAnswer((_) async {}); - when(() => mockShareService.launchUrl(Uri.parse(testUrl))) - .thenAnswer((_) async => true); + when( + () => mockShareService.share(any(that: isA())), + ).thenAnswer((_) async {}); + when( + () => mockShareService.launchUrl(Uri.parse(testUrl)), + ).thenAnswer((_) async => true); when(() => mockShareService.shareFile(any())).thenAnswer((_) async { return const ShareResult('Success', ShareResultStatus.success); }); - when(() => mockShareService.open(any(that: isA()))) - .thenAnswer((_) async { + when(() => mockShareService.open(any(that: isA()))).thenAnswer(( + _, + ) async { return OpenResult(); }); - await tester.pumpWidget(ProviderScope(overrides: [ - appLanguageProvider.overrideWith(() => TestAppLanguage('de')), - shareProvider.overrideWithValue(mockShareService), - languageProvider.overrideWith(() => TestLanguageController( - downloadedLanguages: [ - 'de' - ], + await tester.pumpWidget( + ProviderScope( + overrides: [ + appLanguageProvider.overrideWith(() => TestAppLanguage('de')), + shareProvider.overrideWithValue(mockShareService), + languageProvider.overrideWith2( + (languageCode) => TestLanguageController( + downloadedLanguages: ['de'], pages: { - 'Healing': const Page('test', 'test', 'test', '1.0', testPath) - })) - ], child: const TestShareButton())); + 'Healing': const Page('test', 'test', 'test', '1.0', testPath), + }, + ), + ), + ], + child: const TestShareButton(), + ), + ); await tester.tap(find.byType(ShareButton)); await tester.pump(); diff --git a/test/startup_page_test.dart b/test/startup_page_test.dart index f33fdd6..6fb2547 100644 --- a/test/startup_page_test.dart +++ b/test/startup_page_test.dart @@ -29,20 +29,32 @@ void main() { } testWidgets('Test normal behaviour', (WidgetTester tester) async { - SharedPreferences.setMockInitialValues( - {'appLanguage': 'de', 'checkFrequency': 'weekly'}); + SharedPreferences.setMockInitialValues({ + 'appLanguage': 'de', + 'checkFrequency': 'weekly', + }); final prefs = await SharedPreferences.getInstance(); expect(route, isNull); - final ref = ProviderContainer(overrides: [ - languageProvider - .overrideWith(() => TestLanguageController(initReturns: true)), - backgroundSchedulerProvider.overrideWith(() => TestBackgroundScheduler()), - sharedPrefsProvider.overrideWith((ref) => prefs) - ]); - await tester.pumpWidget(UncontrolledProviderScope( + final ref = ProviderContainer( + overrides: [ + languageProvider.overrideWith2( + (languageCode) => TestLanguageController(initReturns: true), + ), + backgroundSchedulerProvider.overrideWith( + () => TestBackgroundScheduler(), + ), + sharedPrefsProvider.overrideWith((ref) => prefs), + ], + ); + await tester.pumpWidget( + UncontrolledProviderScope( container: ref, child: MaterialApp( - home: const StartupPage(), onGenerateRoute: generateRoutes))); + home: const StartupPage(), + onGenerateRoute: generateRoutes, + ), + ), + ); // First there should be the loading animation expect(find.byType(CircularProgressIndicator), findsOneWidget); expect(find.text('Loading'), findsOneWidget); @@ -51,34 +63,46 @@ void main() { expect(ref.read(backgroundSchedulerProvider), true); }); - testWidgets('Test different routing when no languages are downloaded', - (WidgetTester tester) async { + testWidgets('Test different routing when no languages are downloaded', ( + WidgetTester tester, + ) async { SharedPreferences.setMockInitialValues({'appLanguage': 'de'}); final prefs = await SharedPreferences.getInstance(); route = null; - final ref = ProviderContainer(overrides: [ - languageProvider - .overrideWith(() => TestLanguageController(downloadedLanguages: [])), - backgroundSchedulerProvider.overrideWith(() => TestBackgroundScheduler()), - sharedPrefsProvider.overrideWith((ref) => prefs) - ]); - await tester.pumpWidget(UncontrolledProviderScope( + final ref = ProviderContainer( + overrides: [ + languageProvider.overrideWith2( + (languageCode) => TestLanguageController(downloadedLanguages: []), + ), + backgroundSchedulerProvider.overrideWith( + () => TestBackgroundScheduler(), + ), + sharedPrefsProvider.overrideWith((ref) => prefs), + ], + ); + await tester.pumpWidget( + UncontrolledProviderScope( container: ref, child: MaterialApp( - home: const StartupPage(), onGenerateRoute: generateRoutes))); + home: const StartupPage(), + onGenerateRoute: generateRoutes, + ), + ), + ); expect(route, equals('/onboarding/2')); expect(ref.read(backgroundSchedulerProvider), false); }); -/* TODO for version 0.9 + /* TODO for version 0.9 testWidgets('Test continuing to third onboarding step', (WidgetTester tester) async { SharedPreferences.setMockInitialValues({'appLanguage': 'de'}); final prefs = await SharedPreferences.getInstance(); route = null; final ref = ProviderContainer(overrides: [ - languageProvider - .overrideWith(() => TestLanguageController(initReturns: true)), + languageProvider.overrideWith2( + (languageCode) => TestLanguageController(initReturns: true), + ), sharedPrefsProvider.overrideWith((ref) => prefs) ]); await tester.pumpWidget(UncontrolledProviderScope( @@ -96,7 +120,8 @@ void main() { final prefs = await SharedPreferences.getInstance(); completer = Completer(); route = null; - await tester.pumpWidget(ProviderScope( + await tester.pumpWidget( + ProviderScope( overrides: [sharedPrefsProvider.overrideWith((ref) => prefs)], child: MaterialApp( locale: const Locale('en'), @@ -104,7 +129,9 @@ void main() { supportedLocales: AppLocalizations.supportedLocales, home: StartupPage(initFunction: mockInitFunction), onGenerateRoute: generateRoutes, - ))); + ), + ), + ); expect(find.byType(CircularProgressIndicator), findsOneWidget); expect(find.text('Loading'), findsOneWidget); completer.completeError("Failed"); @@ -120,45 +147,67 @@ void main() { 'appLanguage': 'en', 'checkFrequency': 'weekly', 'recentPage': 'Healing', - 'recentLang': 'de' + 'recentLang': 'de', }); }); testWidgets('Recent page should be loaded', (WidgetTester tester) async { final prefs = await SharedPreferences.getInstance(); route = null; - final ref = ProviderContainer(overrides: [ - languageProvider - .overrideWith(() => TestLanguageController(initReturns: true)), - backgroundSchedulerProvider - .overrideWith(() => TestBackgroundScheduler()), - sharedPrefsProvider.overrideWithValue(prefs) - ]); + final ref = ProviderContainer( + overrides: [ + languageProvider.overrideWith2( + (languageCode) => TestLanguageController(initReturns: true), + ), + backgroundSchedulerProvider.overrideWith( + () => TestBackgroundScheduler(), + ), + sharedPrefsProvider.overrideWithValue(prefs), + ], + ); expect(ref.read(backgroundSchedulerProvider), false); - await tester.pumpWidget(UncontrolledProviderScope( + await tester.pumpWidget( + UncontrolledProviderScope( container: ref, child: MaterialApp( - home: const StartupPage(), onGenerateRoute: generateRoutes))); + home: const StartupPage(), + onGenerateRoute: generateRoutes, + ), + ), + ); await tester.pump(); expect(route, equals('/view/Healing/de')); expect(ref.read(backgroundSchedulerProvider), true); }); - testWidgets("Recent page should get ignored because German isn't loaded", - (WidgetTester tester) async { + testWidgets("Recent page should get ignored because German isn't loaded", ( + WidgetTester tester, + ) async { final prefs = await SharedPreferences.getInstance(); route = null; - final ref = ProviderContainer(overrides: [ - languageProvider.overrideWith(() => TestLanguageController( - downloadedLanguages: ['en'], initReturns: true)), - backgroundSchedulerProvider - .overrideWith(() => TestBackgroundScheduler()), - sharedPrefsProvider.overrideWithValue(prefs) - ]); + final ref = ProviderContainer( + overrides: [ + languageProvider.overrideWith2( + (languageCode) => TestLanguageController( + downloadedLanguages: ['en'], + initReturns: true, + ), + ), + backgroundSchedulerProvider.overrideWith( + () => TestBackgroundScheduler(), + ), + sharedPrefsProvider.overrideWithValue(prefs), + ], + ); expect(ref.read(backgroundSchedulerProvider), false); - await tester.pumpWidget(UncontrolledProviderScope( + await tester.pumpWidget( + UncontrolledProviderScope( container: ref, child: MaterialApp( - home: const StartupPage(), onGenerateRoute: generateRoutes))); + home: const StartupPage(), + onGenerateRoute: generateRoutes, + ), + ), + ); await tester.pump(); expect(route, equals('/home')); diff --git a/test/update_language_button_test.dart b/test/update_language_button_test.dart index ee286cd..9eef234 100644 --- a/test/update_language_button_test.dart +++ b/test/update_language_button_test.dart @@ -21,11 +21,12 @@ class TestUpdateLanguageButton extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { return MaterialApp( - locale: ref.watch(appLanguageProvider).locale, - localizationsDelegates: AppLocalizations.localizationsDelegates, - supportedLocales: AppLocalizations.supportedLocales, - scaffoldMessengerKey: ref.read(scaffoldMessengerKeyProvider), - home: Scaffold(body: UpdateLanguageButton(languageCode))); + locale: ref.watch(appLanguageProvider).locale, + localizationsDelegates: AppLocalizations.localizationsDelegates, + supportedLocales: AppLocalizations.supportedLocales, + scaffoldMessengerKey: ref.read(scaffoldMessengerKeyProvider), + home: Scaffold(body: UpdateLanguageButton(languageCode)), + ); } } @@ -35,11 +36,12 @@ class TestUpdateAllLanguagesButton extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { return MaterialApp( - locale: ref.read(appLanguageProvider).locale, - localizationsDelegates: AppLocalizations.localizationsDelegates, - supportedLocales: AppLocalizations.supportedLocales, - scaffoldMessengerKey: ref.read(scaffoldMessengerKeyProvider), - home: const Scaffold(body: UpdateAllLanguagesButton())); + locale: ref.read(appLanguageProvider).locale, + localizationsDelegates: AppLocalizations.localizationsDelegates, + supportedLocales: AppLocalizations.supportedLocales, + scaffoldMessengerKey: ref.read(scaffoldMessengerKeyProvider), + home: const Scaffold(body: UpdateAllLanguagesButton()), + ); } } @@ -47,15 +49,23 @@ void main() { testWidgets('Test UpdateLanguageButton', (WidgetTester tester) async { SharedPreferences.setMockInitialValues({}); final prefs = await SharedPreferences.getInstance(); - final ref = ProviderContainer(overrides: [ - languageProvider.overrideWith(() => TestLanguageController()), - appLanguageProvider.overrideWith(() => TestAppLanguage('de')), - sharedPrefsProvider.overrideWith((ref) => prefs), - httpClientProvider.overrideWith((ref) => mockReturnTwoUpdates()) - ]); - - await tester.pumpWidget(UncontrolledProviderScope( - container: ref, child: const TestUpdateLanguageButton('en'))); + final ref = ProviderContainer( + overrides: [ + languageProvider.overrideWith2( + (languageCode) => TestLanguageController(), + ), + appLanguageProvider.overrideWith(() => TestAppLanguage('de')), + sharedPrefsProvider.overrideWith((ref) => prefs), + httpClientProvider.overrideWith((ref) => mockReturnTwoUpdates()), + ], + ); + + await tester.pumpWidget( + UncontrolledProviderScope( + container: ref, + child: const TestUpdateLanguageButton('en'), + ), + ); expect(find.byIcon(Icons.refresh), findsOneWidget); expect(ref.read(languageProvider('en')).downloaded, true); @@ -76,24 +86,36 @@ void main() { expect(secondTimestamp.compareTo(firstTimestamp), greaterThan(0)); expect(ref.read(languageStatusProvider('en')).updatesAvailable, false); // Snackbar visible? - expect(find.text('Englisch (en) ist nun auf dem aktuellsten Stand'), - findsOneWidget); + expect( + find.text('Englisch (en) ist nun auf dem aktuellsten Stand'), + findsOneWidget, + ); }); testWidgets('Test when download fails', (WidgetTester tester) async { SharedPreferences.setMockInitialValues({}); final prefs = await SharedPreferences.getInstance(); var throwingController = ThrowingDownloadAssetsController(); - final ref = ProviderContainer(overrides: [ - appLanguageProvider.overrideWith(() => TestAppLanguage('de')), - languageProvider.overrideWith( - () => LanguageController(assetsController: throwingController)), - sharedPrefsProvider.overrideWith((ref) => prefs), - httpClientProvider.overrideWith((ref) => mockReturnTwoUpdates()) - ]); - - await tester.pumpWidget(UncontrolledProviderScope( - container: ref, child: const TestUpdateLanguageButton('en'))); + final ref = ProviderContainer( + overrides: [ + appLanguageProvider.overrideWith(() => TestAppLanguage('de')), + languageProvider.overrideWith2( + (languageCode) => LanguageController( + languageCode: languageCode, + assetsController: throwingController, + ), + ), + sharedPrefsProvider.overrideWith((ref) => prefs), + httpClientProvider.overrideWith((ref) => mockReturnTwoUpdates()), + ], + ); + + await tester.pumpWidget( + UncontrolledProviderScope( + container: ref, + child: const TestUpdateLanguageButton('en'), + ), + ); await ref.read(languageStatusProvider('en').notifier).check(); expect(ref.read(languageStatusProvider('en')).updatesAvailable, true); @@ -104,32 +126,44 @@ void main() { expect(throwingController.clearAssetsCalled, true); // Snackbar visible? expect( - find.textContaining('Aktualisierung fehlgeschlagen.'), findsOneWidget); + find.textContaining('Aktualisierung fehlgeschlagen.'), + findsOneWidget, + ); expect(ref.read(languageProvider('en')).downloaded, false); expect(ref.read(languageStatusProvider('en')).updatesAvailable, false); }); - testWidgets('Test UpdateAllLanguagesButton: Updating 3 languages', - (WidgetTester tester) async { + testWidgets('Test UpdateAllLanguagesButton: Updating 3 languages', ( + WidgetTester tester, + ) async { SharedPreferences.setMockInitialValues({}); final prefs = await SharedPreferences.getInstance(); - final ref = ProviderContainer(overrides: [ - appLanguageProvider.overrideWith(() => TestAppLanguage('de')), - languageProvider.overrideWith(() => TestLanguageController()), - sharedPrefsProvider.overrideWith((ref) => prefs), - httpClientProvider.overrideWith((ref) => mockReturnTwoUpdates()) - ]); + final ref = ProviderContainer( + overrides: [ + appLanguageProvider.overrideWith(() => TestAppLanguage('de')), + languageProvider.overrideWith2( + (languageCode) => TestLanguageController(), + ), + sharedPrefsProvider.overrideWith((ref) => prefs), + httpClientProvider.overrideWith((ref) => mockReturnTwoUpdates()), + ], + ); expect(ref.read(updatesAvailableProvider), false); // Simulate that there are updates for these three languages available for (String languageCode in ['de', 'en', 'fr']) { expect( - await ref.read(languageStatusProvider(languageCode).notifier).check(), - 2); + await ref.read(languageStatusProvider(languageCode).notifier).check(), + 2, + ); } expect(ref.read(updatesAvailableProvider), true); - await tester.pumpWidget(UncontrolledProviderScope( - container: ref, child: const TestUpdateAllLanguagesButton())); + await tester.pumpWidget( + UncontrolledProviderScope( + container: ref, + child: const TestUpdateAllLanguagesButton(), + ), + ); expect(find.byIcon(Icons.refresh), findsOneWidget); await tester.tap(find.byType(UpdateAllLanguagesButton)); @@ -137,14 +171,17 @@ void main() { expect(ref.read(updatesAvailableProvider), false); expect(find.byIcon(Icons.refresh), findsNothing); - expect(ref.read(languageProvider('ar')).downloadTimestamp, - equals(DateTime.utc(2023))); expect( - ref - .read(languageProvider('de')) - .downloadTimestamp - .compareTo(DateTime.utc(2023)), - greaterThan(0)); + ref.read(languageProvider('ar')).downloadTimestamp, + equals(DateTime.utc(2023)), + ); + expect( + ref + .read(languageProvider('de')) + .downloadTimestamp + .compareTo(DateTime.utc(2023)), + greaterThan(0), + ); expect(ref.read(languageStatusProvider('fr')).updatesAvailable, false); await tester.pumpAndSettle(); @@ -152,36 +189,50 @@ void main() { expect(find.text('3 Sprachen aktualisiert'), findsOneWidget); }); - testWidgets('Test UpdateAllLanguagesButton: Updating one language', - (WidgetTester tester) async { + testWidgets('Test UpdateAllLanguagesButton: Updating one language', ( + WidgetTester tester, + ) async { SharedPreferences.setMockInitialValues({}); final prefs = await SharedPreferences.getInstance(); - final ref = ProviderContainer(overrides: [ - appLanguageProvider.overrideWith(() => TestAppLanguage('de')), - languageProvider.overrideWith(() => TestLanguageController()), - sharedPrefsProvider.overrideWith((ref) => prefs), - httpClientProvider.overrideWith((ref) => mockReturnTwoUpdates()) - ]); - expect(ref.read(languageProvider('de')).downloadTimestamp, - equals(DateTime.utc(2023))); + final ref = ProviderContainer( + overrides: [ + appLanguageProvider.overrideWith(() => TestAppLanguage('de')), + languageProvider.overrideWith2( + (languageCode) => TestLanguageController(), + ), + sharedPrefsProvider.overrideWith((ref) => prefs), + httpClientProvider.overrideWith((ref) => mockReturnTwoUpdates()), + ], + ); + expect( + ref.read(languageProvider('de')).downloadTimestamp, + equals(DateTime.utc(2023)), + ); await ref.read(languageStatusProvider('de').notifier).check(); - await tester.pumpWidget(UncontrolledProviderScope( - container: ref, child: const TestUpdateAllLanguagesButton())); + await tester.pumpWidget( + UncontrolledProviderScope( + container: ref, + child: const TestUpdateAllLanguagesButton(), + ), + ); expect(find.byIcon(Icons.refresh), findsOneWidget); await tester.tap(find.byType(UpdateAllLanguagesButton)); await tester.pump(); expect( - ref - .read(languageProvider('de')) - .downloadTimestamp - .compareTo(DateTime.utc(2023)), - greaterThan(0)); + ref + .read(languageProvider('de')) + .downloadTimestamp + .compareTo(DateTime.utc(2023)), + greaterThan(0), + ); // Snackbar visible? - expect(find.text('Deutsch (de) ist nun auf dem aktuellsten Stand'), - findsOneWidget); + expect( + find.text('Deutsch (de) ist nun auf dem aktuellsten Stand'), + findsOneWidget, + ); }); // TODO Test correct handling / snackbar message when updates fail diff --git a/test/updates_test.dart b/test/updates_test.dart index 57de9b6..ea88e03 100644 --- a/test/updates_test.dart +++ b/test/updates_test.dart @@ -10,6 +10,7 @@ import 'package:app4training/data/updates.dart'; import 'package:http/http.dart'; import 'package:http/testing.dart'; import 'package:shared_preferences/shared_preferences.dart'; + // ignore: implementation_imports, invalid_use_of_internal_member import 'package:riverpod/src/framework.dart' show $RefArg; @@ -31,24 +32,32 @@ int countCheckCalled = 0; class TestLanguageStatus extends LanguageStatusNotifier { int _checkReturnValue; final List _langWithUpdates; - TestLanguageStatus( - {int checkReturnValue = 0, List langWithUpdates = const []}) - : _checkReturnValue = checkReturnValue, - _langWithUpdates = langWithUpdates; + + TestLanguageStatus({ + int checkReturnValue = 0, + List langWithUpdates = const [], + }) : _checkReturnValue = checkReturnValue, + _langWithUpdates = langWithUpdates; @override LanguageStatus build() { final arg = ref.$arg as String; return LanguageStatus( - _langWithUpdates.contains(arg), DateTime.utc(2023), DateTime.utc(2023)); + _langWithUpdates.contains(arg), + DateTime.utc(2023), + DateTime.utc(2023), + ); } @override Future check() async { countCheckCalled++; if (_checkReturnValue >= 0) { - state = LanguageStatus(_checkReturnValue > 0, state.downloadTimestamp, - DateTime.now().toUtc()); + state = LanguageStatus( + _checkReturnValue > 0, + state.downloadTimestamp, + DateTime.now().toUtc(), + ); } return _checkReturnValue; } @@ -95,8 +104,10 @@ Client mockCheckResponse(Map languageUpdateMap) { // Get our language code from the URL that looks something like this: // https://api.github.com/repos/4training/html-de/commits?since=... final String languageCode = request.url.pathSegments - .firstWhere((element) => element.startsWith('html-'), - orElse: () => 'html-xyz') + .firstWhere( + (element) => element.startsWith('html-'), + orElse: () => 'html-xyz', + ) .substring(5); int countUpdates = languageUpdateMap[languageCode] ?? 0; return fakeResponseNUpdates(countUpdates); @@ -114,30 +125,42 @@ void main() { }); testWidgets('Test getting localized messages', (WidgetTester tester) async { - await tester.pumpWidget(const ProviderScope( + await tester.pumpWidget( + const ProviderScope( child: MaterialApp( - locale: Locale('de'), - localizationsDelegates: AppLocalizations.localizationsDelegates, - supportedLocales: AppLocalizations.supportedLocales, - home: Scaffold()))); + locale: Locale('de'), + localizationsDelegates: AppLocalizations.localizationsDelegates, + supportedLocales: AppLocalizations.supportedLocales, + home: Scaffold(), + ), + ), + ); BuildContext context = tester.element(find.byType(Scaffold)); expect( - CheckFrequency.getLocalized(context, CheckFrequency.never), "niemals"); + CheckFrequency.getLocalized(context, CheckFrequency.never), + "niemals", + ); }); test('Test TestLanguageStatusNotifier class', () async { var testLanguageStatus = TestLanguageStatus(); - var ref = ProviderContainer(overrides: [ - languageStatusProvider.overrideWith(() => testLanguageStatus) - ]); + var ref = ProviderContainer( + overrides: [ + languageStatusProvider.overrideWith2( + (languageCode) => testLanguageStatus, + ), + ], + ); var deStatus = ref.read(languageStatusProvider('de')); expect(deStatus.updatesAvailable, false); expect(deStatus.lastCheckedTimestamp, equals(deStatus.downloadTimestamp)); expect(await ref.read(languageStatusProvider('de').notifier).check(), 0); deStatus = ref.read(languageStatusProvider('de')); expect(deStatus.updatesAvailable, false); - expect(deStatus.lastCheckedTimestamp.compareTo(deStatus.downloadTimestamp), - greaterThan(0)); + expect( + deStatus.lastCheckedTimestamp.compareTo(deStatus.downloadTimestamp), + greaterThan(0), + ); testLanguageStatus.setCheckReturnValue(2); await Future.delayed(const Duration(milliseconds: 1)); @@ -147,8 +170,9 @@ void main() { expect(deStatus2.downloadTimestamp, equals(deStatus.downloadTimestamp)); // last checked timestamp should be even newer expect( - deStatus2.lastCheckedTimestamp.compareTo(deStatus.lastCheckedTimestamp), - greaterThan(0)); + deStatus2.lastCheckedTimestamp.compareTo(deStatus.lastCheckedTimestamp), + greaterThan(0), + ); testLanguageStatus.setCheckReturnValue(-1); await Future.delayed(const Duration(milliseconds: 1)); @@ -158,7 +182,9 @@ void main() { expect(deStatus3.downloadTimestamp, equals(deStatus.downloadTimestamp)); // last checked timestamp should be the same expect( - deStatus3.lastCheckedTimestamp, equals(deStatus2.lastCheckedTimestamp)); + deStatus3.lastCheckedTimestamp, + equals(deStatus2.lastCheckedTimestamp), + ); testLanguageStatus.setCheckReturnValue(0); await Future.delayed(const Duration(milliseconds: 1)); @@ -168,30 +194,40 @@ void main() { expect(deStatus4.downloadTimestamp, equals(deStatus.downloadTimestamp)); // last checked timestamp should be newer expect( - deStatus4.lastCheckedTimestamp - .compareTo(deStatus3.lastCheckedTimestamp), - greaterThan(0)); - }); - test('Test TestLanguageStatusNotifier constructor parameter checkReturnValue', - () async { - final ref = ProviderContainer(overrides: [ - languageStatusProvider - .overrideWith(() => TestLanguageStatus(checkReturnValue: 2)) - ]); - expect(ref.read(languageStatusProvider('de')).updatesAvailable, false); - expect(await ref.read(languageStatusProvider('de').notifier).check(), 2); - expect(ref.read(languageStatusProvider('de')).updatesAvailable, true); - expect(ref.read(languageStatusProvider('en')).updatesAvailable, false); - }); - test('Test TestLanguageStatusNotifier constructor parameter langsWithUpdates', - () async { - var ref = ProviderContainer(overrides: [ - languageStatusProvider - .overrideWith(() => TestLanguageStatus(langWithUpdates: ['de'])) - ]); - expect(ref.read(languageStatusProvider('de')).updatesAvailable, true); - expect(ref.read(languageStatusProvider('en')).updatesAvailable, false); + deStatus4.lastCheckedTimestamp.compareTo(deStatus3.lastCheckedTimestamp), + greaterThan(0), + ); }); + test( + 'Test TestLanguageStatusNotifier constructor parameter checkReturnValue', + () async { + final ref = ProviderContainer( + overrides: [ + languageStatusProvider.overrideWith2( + (languageCode) => TestLanguageStatus(checkReturnValue: 2), + ), + ], + ); + expect(ref.read(languageStatusProvider('de')).updatesAvailable, false); + expect(await ref.read(languageStatusProvider('de').notifier).check(), 2); + expect(ref.read(languageStatusProvider('de')).updatesAvailable, true); + expect(ref.read(languageStatusProvider('en')).updatesAvailable, false); + }, + ); + test( + 'Test TestLanguageStatusNotifier constructor parameter langsWithUpdates', + () async { + var ref = ProviderContainer( + overrides: [ + languageStatusProvider.overrideWith2( + (languageCode) => TestLanguageStatus(langWithUpdates: ['de']), + ), + ], + ); + expect(ref.read(languageStatusProvider('de')).updatesAvailable, true); + expect(ref.read(languageStatusProvider('en')).updatesAvailable, false); + }, + ); group('Test reading LanguageStatus from SharedPreferences', () { late ProviderContainer ref; @@ -200,11 +236,15 @@ void main() { setUp(() async { SharedPreferences.setMockInitialValues({}); prefs = await SharedPreferences.getInstance(); - ref = ProviderContainer(overrides: [ - languageProvider.overrideWith( - () => TestLanguageController(downloadedLanguages: ['de'])), - sharedPrefsProvider.overrideWith((ref) => prefs) - ]); + ref = ProviderContainer( + overrides: [ + languageProvider.overrideWith2( + (languageCode) => + TestLanguageController(downloadedLanguages: ['de']), + ), + sharedPrefsProvider.overrideWith((ref) => prefs), + ], + ); }); test('Nothing saved in SharedPreferences', () async { @@ -267,16 +307,23 @@ void main() { test('Test checking for updates: no updates', () async { SharedPreferences.setMockInitialValues({}); final prefs = await SharedPreferences.getInstance(); - final ref = ProviderContainer(overrides: [ - httpClientProvider.overrideWith((ref) => MockClient((request) async { - expect(request.url.toString(), - equals(Globals.getCommitsSince('de', DateTime.utc(2023)))); + final ref = ProviderContainer( + overrides: [ + httpClientProvider.overrideWith( + (ref) => MockClient((request) async { + expect( + request.url.toString(), + equals(Globals.getCommitsSince('de', DateTime.utc(2023))), + ); return Response(json.encode([]), 200); - })), - languageProvider.overrideWith( - () => TestLanguageController(downloadedLanguages: ['de'])), - sharedPrefsProvider.overrideWith((ref) => prefs) - ]); + }), + ), + languageProvider.overrideWith2( + (languageCode) => TestLanguageController(downloadedLanguages: ['de']), + ), + sharedPrefsProvider.overrideWith((ref) => prefs), + ], + ); LanguageStatus deStatus = ref.read(languageStatusProvider('de')); expect(deStatus.downloadTimestamp, equals(DateTime.utc(2023))); expect(deStatus.lastCheckedTimestamp, equals(DateTime.utc(2023))); @@ -287,28 +334,38 @@ void main() { // The lastCheckedTimestamp should be updated to the current time now() deStatus = ref.read(languageStatusProvider('de')); expect(deStatus.downloadTimestamp, equals(DateTime.utc(2023))); - expect(deStatus.lastCheckedTimestamp.compareTo(deStatus.downloadTimestamp), - greaterThan(0)); expect( - ref.read(lastCheckedProvider), equals(deStatus.lastCheckedTimestamp)); + deStatus.lastCheckedTimestamp.compareTo(deStatus.downloadTimestamp), + greaterThan(0), + ); + expect( + ref.read(lastCheckedProvider), + equals(deStatus.lastCheckedTimestamp), + ); expect(deStatus.updatesAvailable, false); expect(prefs.getBool('updatesAvailable-de'), false); - expect(prefs.getString('lastChecked-de'), - equals(deStatus.lastCheckedTimestamp.toIso8601String())); + expect( + prefs.getString('lastChecked-de'), + equals(deStatus.lastCheckedTimestamp.toIso8601String()), + ); }); test('Test checking for updates: 2 updates', () async { SharedPreferences.setMockInitialValues({ 'lastChecked-de': '2023-02-02 00:00:00.000Z', - 'updatesAvailable-de': false + 'updatesAvailable-de': false, }); final prefs = await SharedPreferences.getInstance(); - final ref = ProviderContainer(overrides: [ - httpClientProvider.overrideWith((ref) => mockReturnTwoUpdates()), - languageProvider.overrideWith( - () => TestLanguageController(downloadedLanguages: ['de', 'en'])), - sharedPrefsProvider.overrideWith((ref) => prefs) - ]); + final ref = ProviderContainer( + overrides: [ + httpClientProvider.overrideWith((ref) => mockReturnTwoUpdates()), + languageProvider.overrideWith2( + (languageCode) => + TestLanguageController(downloadedLanguages: ['de', 'en']), + ), + sharedPrefsProvider.overrideWith((ref) => prefs), + ], + ); final deStatusNotifier = ref.read(languageStatusProvider('de').notifier); expect(ref.read(lastCheckedProvider), equals(DateTime.utc(2023))); expect(await deStatusNotifier.check(), 2); @@ -316,23 +373,31 @@ void main() { // lastCheckedTimestamp should be updated to the current time now() LanguageStatus deStatus = ref.read(languageStatusProvider('de')); expect(deStatus.downloadTimestamp, equals(DateTime.utc(2023))); - expect(deStatus.lastCheckedTimestamp.compareTo(deStatus.downloadTimestamp), - greaterThan(0)); + expect( + deStatus.lastCheckedTimestamp.compareTo(deStatus.downloadTimestamp), + greaterThan(0), + ); expect(deStatus.updatesAvailable, true); expect(prefs.getBool('updatesAvailable-de'), true); - expect(prefs.getString('lastChecked-de'), - equals(deStatus.lastCheckedTimestamp.toIso8601String())); + expect( + prefs.getString('lastChecked-de'), + equals(deStatus.lastCheckedTimestamp.toIso8601String()), + ); // If we check for updates a second time, we should get the same results expect(await deStatusNotifier.check(), 2); deStatus = ref.read(languageStatusProvider('de')); expect(deStatus.downloadTimestamp, equals(DateTime.utc(2023))); - expect(deStatus.lastCheckedTimestamp.compareTo(deStatus.downloadTimestamp), - greaterThan(0)); + expect( + deStatus.lastCheckedTimestamp.compareTo(deStatus.downloadTimestamp), + greaterThan(0), + ); expect(deStatus.updatesAvailable, true); expect(prefs.getBool('updatesAvailable-de'), true); - expect(prefs.getString('lastChecked-de'), - equals(deStatus.lastCheckedTimestamp.toIso8601String())); + expect( + prefs.getString('lastChecked-de'), + equals(deStatus.lastCheckedTimestamp.toIso8601String()), + ); // As we didn't check for updates for English: expect(ref.read(lastCheckedProvider), equals(DateTime.utc(2023))); @@ -341,20 +406,26 @@ void main() { test('Test checking and updating', () async { SharedPreferences.setMockInitialValues({}); final prefs = await SharedPreferences.getInstance(); - final ref = ProviderContainer(overrides: [ - httpClientProvider.overrideWith((ref) => mockReturnTwoUpdates()), - languageProvider.overrideWith( - () => TestLanguageController(downloadedLanguages: ['de', 'en'])), - sharedPrefsProvider.overrideWith((ref) => prefs) - ]); + final ref = ProviderContainer( + overrides: [ + httpClientProvider.overrideWith((ref) => mockReturnTwoUpdates()), + languageProvider.overrideWith2( + (languageCode) => + TestLanguageController(downloadedLanguages: ['de', 'en']), + ), + sharedPrefsProvider.overrideWith((ref) => prefs), + ], + ); expect(await ref.read(languageStatusProvider('de').notifier).check(), 2); // The lastCheckedTimestamp should be updated to the current time now() LanguageStatus deStatus = ref.read(languageStatusProvider('de')); expect(deStatus.downloadTimestamp, equals(DateTime.utc(2023))); - expect(deStatus.lastCheckedTimestamp.compareTo(deStatus.downloadTimestamp), - greaterThan(0)); + expect( + deStatus.lastCheckedTimestamp.compareTo(deStatus.downloadTimestamp), + greaterThan(0), + ); expect(deStatus.updatesAvailable, true); expect(ref.read(sharedPrefsProvider).getBool('updatesAvailable-de'), true); expect(ref.read(lastCheckedProvider), equals(DateTime.utc(2023))); @@ -367,7 +438,9 @@ void main() { final englishTimestamp = ref.read(languageStatusProvider('en')).lastCheckedTimestamp; expect( - ref.read(lastCheckedProvider), equals(deStatus.lastCheckedTimestamp)); + ref.read(lastCheckedProvider), + equals(deStatus.lastCheckedTimestamp), + ); // After downloading the resources there shouldn't be updates available await Future.delayed(const Duration(milliseconds: 1)); @@ -380,20 +453,27 @@ void main() { // lastCheckedProvider should have advanced a little bit again expect(ref.read(lastCheckedProvider), equals(englishTimestamp)); expect( - ref.read(lastCheckedProvider).compareTo(deStatus.lastCheckedTimestamp), - lessThan(0)); + ref.read(lastCheckedProvider).compareTo(deStatus.lastCheckedTimestamp), + lessThan(0), + ); }); test('Test correct behavior when checking for updates fails', () async { SharedPreferences.setMockInitialValues({}); final prefs = await SharedPreferences.getInstance(); - final ref = ProviderContainer(overrides: [ - httpClientProvider.overrideWith((ref) => MockClient((request) async { + final ref = ProviderContainer( + overrides: [ + httpClientProvider.overrideWith( + (ref) => MockClient((request) async { throw ClientException; - })), - languageProvider.overrideWith(() => TestLanguageController()), - sharedPrefsProvider.overrideWith((ref) => prefs) - ]); + }), + ), + languageProvider.overrideWith2( + (languageCode) => TestLanguageController(), + ), + sharedPrefsProvider.overrideWith((ref) => prefs), + ], + ); expect(await ref.read(languageStatusProvider('de').notifier).check(), -1); @@ -408,22 +488,31 @@ void main() { test('Test error handling when API query limit is exceeded', () async { SharedPreferences.setMockInitialValues({}); final prefs = await SharedPreferences.getInstance(); - final ref = ProviderContainer(overrides: [ - httpClientProvider.overrideWith((ref) => MockClient((request) async { + final ref = ProviderContainer( + overrides: [ + httpClientProvider.overrideWith( + (ref) => MockClient((request) async { return Response( - json.encode({ - "message": - "API rate limit exceeded for 1.1.1.1. (But here's the good news: Authenticated requests get a higher rate limit. Check out the documentation for more details.)", - "documentation_url": - "https://docs.github.com/rest/overview/resources-in-the-rest-api#rate-limiting" - }), - 403); - })), - languageProvider.overrideWith(() => TestLanguageController()), - sharedPrefsProvider.overrideWith((ref) => prefs) - ]); - expect(await ref.read(languageStatusProvider('de').notifier).check(), - apiRateLimitExceeded); + json.encode({ + "message": + "API rate limit exceeded for 1.1.1.1. (But here's the good news: Authenticated requests get a higher rate limit. Check out the documentation for more details.)", + "documentation_url": + "https://docs.github.com/rest/overview/resources-in-the-rest-api#rate-limiting", + }), + 403, + ); + }), + ), + languageProvider.overrideWith2( + (languageCode) => TestLanguageController(), + ), + sharedPrefsProvider.overrideWith((ref) => prefs), + ], + ); + expect( + await ref.read(languageStatusProvider('de').notifier).check(), + apiRateLimitExceeded, + ); final deStatus = ref.read(languageStatusProvider('de')); expect(deStatus.downloadTimestamp, equals(DateTime.utc(2023))); @@ -435,11 +524,15 @@ void main() { test('Test updatesAvailableProvider', () async { SharedPreferences.setMockInitialValues({}); final prefs = await SharedPreferences.getInstance(); - final ref = ProviderContainer(overrides: [ - httpClientProvider.overrideWith((ref) => mockReturnTwoUpdates()), - languageProvider.overrideWith(() => TestLanguageController()), - sharedPrefsProvider.overrideWith((ref) => prefs) - ]); + final ref = ProviderContainer( + overrides: [ + httpClientProvider.overrideWith((ref) => mockReturnTwoUpdates()), + languageProvider.overrideWith2( + (languageCode) => TestLanguageController(), + ), + sharedPrefsProvider.overrideWith((ref) => prefs), + ], + ); // No updates available LanguageStatus deStatus = ref.read(languageStatusProvider('de')); @@ -457,8 +550,9 @@ void main() { // Updating the German resources expect( - await ref.read(languageProvider('de').notifier).download(force: true), - true); + await ref.read(languageProvider('de').notifier).download(force: true), + true, + ); // Now there should again be no updates available deStatus = ref.read(languageStatusProvider('de'));