From 4f4f6f553a1f2acfaf2e591ac3609930dc0abb5f Mon Sep 17 00:00:00 2001 From: Roland Geider Date: Mon, 6 Feb 2023 20:33:29 +0100 Subject: [PATCH] Add paginated fetches This allows the app to fetch all available items from an enpoint, without having to hope that the ?limit=999 is enough Closes #133 --- lib/main.dart | 7 +- lib/models/body_weight/weight_entry.g.dart | 3 +- lib/models/exercises/base.g.dart | 23 +++-- lib/models/exercises/category.g.dart | 3 +- lib/models/exercises/image.g.dart | 3 +- lib/models/exercises/translation.g.dart | 8 +- .../measurements/measurement_category.g.dart | 4 +- .../measurements/measurement_entry.g.dart | 3 +- lib/models/nutrition/ingredient.g.dart | 3 +- .../nutrition/ingredient_weight_unit.g.dart | 6 +- lib/models/nutrition/log.g.dart | 9 +- lib/models/nutrition/nutritional_plan.g.dart | 3 +- lib/models/nutrition/weight_unit.g.dart | 3 +- lib/models/workouts/repetition_unit.g.dart | 3 +- lib/models/workouts/session.g.dart | 12 ++- lib/models/workouts/weight_unit.g.dart | 3 +- lib/models/workouts/workout_plan.g.dart | 3 +- lib/providers/base_provider.dart | 23 ++++- lib/providers/body_weight.dart | 41 +++++---- lib/providers/exercises.dart | 56 ++++-------- lib/providers/workout_plans.dart | 43 +++------ test/exercises/exercise_provider_test.dart | 13 +-- test/fixtures/pagination/pagination1.json | 15 ++++ test/fixtures/pagination/pagination2.json | 15 ++++ test/fixtures/pagination/pagination3.json | 11 +++ test/fixtures/weight/weight_entries.json | 73 +++++++++++++++ test/gallery/gallery_screen_test.mocks.dart | 20 ++++- ...surement_categories_screen_test.mocks.dart | 12 ++- .../measurement_provider_test.mocks.dart | 20 ++++- .../nutritional_plan_form_test.mocks.dart | 56 ++++++++---- .../nutritional_plan_screen_test.dart | 2 +- test/other/base_provider_test.dart | 50 +++++++++++ test/other/base_provider_test.mocks.dart | 9 +- test/weight/weight_provider_test.dart | 89 ++++++++++--------- test/weight/weight_screen_test.dart | 44 +++------ test/workout/gym_mode_screen_test.mocks.dart | 24 +++-- .../weight_unit_form_widget_test.mocks.dart | 32 +++++-- test/workout/workout_form_test.mocks.dart | 56 ++++++++---- test/workout/workout_set_form_test.mocks.dart | 24 +++-- 39 files changed, 561 insertions(+), 266 deletions(-) create mode 100644 test/fixtures/pagination/pagination1.json create mode 100644 test/fixtures/pagination/pagination2.json create mode 100644 test/fixtures/pagination/pagination3.json create mode 100644 test/fixtures/weight/weight_entries.json diff --git a/lib/main.dart b/lib/main.dart index 1ceeefeb..134998a5 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -102,9 +102,10 @@ class MyApp extends StatelessWidget { update: (context, base, previous) => previous ?? UserProvider(WgerBaseProvider(base)), ), ChangeNotifierProxyProvider( - create: (context) => - BodyWeightProvider(Provider.of(context, listen: false), []), - update: (context, auth, previous) => previous ?? BodyWeightProvider(auth, []), + create: (context) => BodyWeightProvider( + WgerBaseProvider(Provider.of(context, listen: false))), + update: (context, base, previous) => + previous ?? BodyWeightProvider(WgerBaseProvider(base)), ), ChangeNotifierProxyProvider( create: (context) => diff --git a/lib/models/body_weight/weight_entry.g.dart b/lib/models/body_weight/weight_entry.g.dart index d1152d20..60ee02c9 100644 --- a/lib/models/body_weight/weight_entry.g.dart +++ b/lib/models/body_weight/weight_entry.g.dart @@ -18,7 +18,8 @@ WeightEntry _$WeightEntryFromJson(Map json) { ); } -Map _$WeightEntryToJson(WeightEntry instance) => { +Map _$WeightEntryToJson(WeightEntry instance) => + { 'id': instance.id, 'weight': numToString(instance.weight), 'date': toDate(instance.date), diff --git a/lib/models/exercises/base.g.dart b/lib/models/exercises/base.g.dart index 42f5bd9d..9e670e33 100644 --- a/lib/models/exercises/base.g.dart +++ b/lib/models/exercises/base.g.dart @@ -24,19 +24,26 @@ ExerciseBase _$ExerciseBaseFromJson(Map json) { return ExerciseBase( id: json['id'] as int?, uuid: json['uuid'] as String?, - creationDate: - json['creation_date'] == null ? null : DateTime.parse(json['creation_date'] as String), - updateDate: json['update_date'] == null ? null : DateTime.parse(json['update_date'] as String), + creationDate: json['creation_date'] == null + ? null + : DateTime.parse(json['creation_date'] as String), + updateDate: json['update_date'] == null + ? null + : DateTime.parse(json['update_date'] as String), variationId: json['variations'] as int?, ) ..categoryId = json['category'] as int - ..musclesIds = (json['muscles'] as List).map((e) => e as int).toList() - ..musclesSecondaryIds = - (json['muscles_secondary'] as List).map((e) => e as int).toList() - ..equipmentIds = (json['equipment'] as List).map((e) => e as int).toList(); + ..musclesIds = + (json['muscles'] as List).map((e) => e as int).toList() + ..musclesSecondaryIds = (json['muscles_secondary'] as List) + .map((e) => e as int) + .toList() + ..equipmentIds = + (json['equipment'] as List).map((e) => e as int).toList(); } -Map _$ExerciseBaseToJson(ExerciseBase instance) => { +Map _$ExerciseBaseToJson(ExerciseBase instance) => + { 'id': instance.id, 'uuid': instance.uuid, 'variations': instance.variationId, diff --git a/lib/models/exercises/category.g.dart b/lib/models/exercises/category.g.dart index 915120d1..19383107 100644 --- a/lib/models/exercises/category.g.dart +++ b/lib/models/exercises/category.g.dart @@ -17,7 +17,8 @@ ExerciseCategory _$ExerciseCategoryFromJson(Map json) { ); } -Map _$ExerciseCategoryToJson(ExerciseCategory instance) => { +Map _$ExerciseCategoryToJson(ExerciseCategory instance) => + { 'id': instance.id, 'name': instance.name, }; diff --git a/lib/models/exercises/image.g.dart b/lib/models/exercises/image.g.dart index 6ef7c62e..1240dc38 100644 --- a/lib/models/exercises/image.g.dart +++ b/lib/models/exercises/image.g.dart @@ -20,7 +20,8 @@ ExerciseImage _$ExerciseImageFromJson(Map json) { ); } -Map _$ExerciseImageToJson(ExerciseImage instance) => { +Map _$ExerciseImageToJson(ExerciseImage instance) => + { 'id': instance.id, 'uuid': instance.uuid, 'exercise_base': instance.exerciseBaseId, diff --git a/lib/models/exercises/translation.g.dart b/lib/models/exercises/translation.g.dart index cb5d3426..a725a476 100644 --- a/lib/models/exercises/translation.g.dart +++ b/lib/models/exercises/translation.g.dart @@ -22,15 +22,17 @@ Translation _$TranslationFromJson(Map json) { return Translation( id: json['id'] as int?, uuid: json['uuid'] as String?, - creationDate: - json['creation_date'] == null ? null : DateTime.parse(json['creation_date'] as String), + creationDate: json['creation_date'] == null + ? null + : DateTime.parse(json['creation_date'] as String), name: json['name'] as String, description: json['description'] as String, baseId: json['exercise_base'] as int?, )..languageId = json['language'] as int; } -Map _$TranslationToJson(Translation instance) => { +Map _$TranslationToJson(Translation instance) => + { 'id': instance.id, 'uuid': instance.uuid, 'language': instance.languageId, diff --git a/lib/models/measurements/measurement_category.g.dart b/lib/models/measurements/measurement_category.g.dart index eb887b2e..ef4ebcd3 100644 --- a/lib/models/measurements/measurement_category.g.dart +++ b/lib/models/measurements/measurement_category.g.dart @@ -22,7 +22,9 @@ MeasurementCategory _$MeasurementCategoryFromJson(Map json) { ); } -Map _$MeasurementCategoryToJson(MeasurementCategory instance) => { +Map _$MeasurementCategoryToJson( + MeasurementCategory instance) => + { 'id': instance.id, 'name': instance.name, 'unit': instance.unit, diff --git a/lib/models/measurements/measurement_entry.g.dart b/lib/models/measurements/measurement_entry.g.dart index 7f6e346a..4b096960 100644 --- a/lib/models/measurements/measurement_entry.g.dart +++ b/lib/models/measurements/measurement_entry.g.dart @@ -20,7 +20,8 @@ MeasurementEntry _$MeasurementEntryFromJson(Map json) { ); } -Map _$MeasurementEntryToJson(MeasurementEntry instance) => { +Map _$MeasurementEntryToJson(MeasurementEntry instance) => + { 'id': instance.id, 'category': instance.category, 'date': toDate(instance.date), diff --git a/lib/models/nutrition/ingredient.g.dart b/lib/models/nutrition/ingredient.g.dart index 28dfe637..eaace92b 100644 --- a/lib/models/nutrition/ingredient.g.dart +++ b/lib/models/nutrition/ingredient.g.dart @@ -40,7 +40,8 @@ Ingredient _$IngredientFromJson(Map json) { ); } -Map _$IngredientToJson(Ingredient instance) => { +Map _$IngredientToJson(Ingredient instance) => + { 'id': instance.id, 'code': instance.code, 'name': instance.name, diff --git a/lib/models/nutrition/ingredient_weight_unit.g.dart b/lib/models/nutrition/ingredient_weight_unit.g.dart index f147a2ee..74b1f101 100644 --- a/lib/models/nutrition/ingredient_weight_unit.g.dart +++ b/lib/models/nutrition/ingredient_weight_unit.g.dart @@ -13,14 +13,16 @@ IngredientWeightUnit _$IngredientWeightUnitFromJson(Map json) { ); return IngredientWeightUnit( id: json['id'] as int, - weightUnit: WeightUnit.fromJson(json['weight_unit'] as Map), + weightUnit: + WeightUnit.fromJson(json['weight_unit'] as Map), ingredient: Ingredient.fromJson(json['ingredient'] as Map), grams: json['grams'] as int, amount: (json['amount'] as num).toDouble(), ); } -Map _$IngredientWeightUnitToJson(IngredientWeightUnit instance) => +Map _$IngredientWeightUnitToJson( + IngredientWeightUnit instance) => { 'id': instance.id, 'weight_unit': instance.weightUnit, diff --git a/lib/models/nutrition/log.g.dart b/lib/models/nutrition/log.g.dart index 61690155..39068768 100644 --- a/lib/models/nutrition/log.g.dart +++ b/lib/models/nutrition/log.g.dart @@ -9,7 +9,14 @@ part of 'log.dart'; Log _$LogFromJson(Map json) { $checkKeys( json, - requiredKeys: const ['id', 'plan', 'datetime', 'ingredient', 'weight_unit', 'amount'], + requiredKeys: const [ + 'id', + 'plan', + 'datetime', + 'ingredient', + 'weight_unit', + 'amount' + ], ); return Log( id: json['id'] as int?, diff --git a/lib/models/nutrition/nutritional_plan.g.dart b/lib/models/nutrition/nutritional_plan.g.dart index 661a9f47..9b10d8a5 100644 --- a/lib/models/nutrition/nutritional_plan.g.dart +++ b/lib/models/nutrition/nutritional_plan.g.dart @@ -18,7 +18,8 @@ NutritionalPlan _$NutritionalPlanFromJson(Map json) { ); } -Map _$NutritionalPlanToJson(NutritionalPlan instance) => { +Map _$NutritionalPlanToJson(NutritionalPlan instance) => + { 'id': instance.id, 'description': instance.description, 'creation_date': toDate(instance.creationDate), diff --git a/lib/models/nutrition/weight_unit.g.dart b/lib/models/nutrition/weight_unit.g.dart index 6f2b9607..74e1f0cf 100644 --- a/lib/models/nutrition/weight_unit.g.dart +++ b/lib/models/nutrition/weight_unit.g.dart @@ -17,7 +17,8 @@ WeightUnit _$WeightUnitFromJson(Map json) { ); } -Map _$WeightUnitToJson(WeightUnit instance) => { +Map _$WeightUnitToJson(WeightUnit instance) => + { 'id': instance.id, 'name': instance.name, }; diff --git a/lib/models/workouts/repetition_unit.g.dart b/lib/models/workouts/repetition_unit.g.dart index 33e6a815..115dc338 100644 --- a/lib/models/workouts/repetition_unit.g.dart +++ b/lib/models/workouts/repetition_unit.g.dart @@ -17,7 +17,8 @@ RepetitionUnit _$RepetitionUnitFromJson(Map json) { ); } -Map _$RepetitionUnitToJson(RepetitionUnit instance) => { +Map _$RepetitionUnitToJson(RepetitionUnit instance) => + { 'id': instance.id, 'name': instance.name, }; diff --git a/lib/models/workouts/session.g.dart b/lib/models/workouts/session.g.dart index 79f4fee4..e16affad 100644 --- a/lib/models/workouts/session.g.dart +++ b/lib/models/workouts/session.g.dart @@ -9,7 +9,14 @@ part of 'session.dart'; WorkoutSession _$WorkoutSessionFromJson(Map json) { $checkKeys( json, - requiredKeys: const ['id', 'workout', 'date', 'impression', 'time_start', 'time_end'], + requiredKeys: const [ + 'id', + 'workout', + 'date', + 'impression', + 'time_start', + 'time_end' + ], ); return WorkoutSession() ..id = json['id'] as int? @@ -21,7 +28,8 @@ WorkoutSession _$WorkoutSessionFromJson(Map json) { ..timeEnd = stringToTime(json['time_end'] as String?); } -Map _$WorkoutSessionToJson(WorkoutSession instance) => { +Map _$WorkoutSessionToJson(WorkoutSession instance) => + { 'id': instance.id, 'workout': instance.workoutId, 'date': toDate(instance.date), diff --git a/lib/models/workouts/weight_unit.g.dart b/lib/models/workouts/weight_unit.g.dart index 6f2b9607..74e1f0cf 100644 --- a/lib/models/workouts/weight_unit.g.dart +++ b/lib/models/workouts/weight_unit.g.dart @@ -17,7 +17,8 @@ WeightUnit _$WeightUnitFromJson(Map json) { ); } -Map _$WeightUnitToJson(WeightUnit instance) => { +Map _$WeightUnitToJson(WeightUnit instance) => + { 'id': instance.id, 'name': instance.name, }; diff --git a/lib/models/workouts/workout_plan.g.dart b/lib/models/workouts/workout_plan.g.dart index ac1d6fa9..ef156e3e 100644 --- a/lib/models/workouts/workout_plan.g.dart +++ b/lib/models/workouts/workout_plan.g.dart @@ -19,7 +19,8 @@ WorkoutPlan _$WorkoutPlanFromJson(Map json) { ); } -Map _$WorkoutPlanToJson(WorkoutPlan instance) => { +Map _$WorkoutPlanToJson(WorkoutPlan instance) => + { 'id': instance.id, 'creation_date': instance.creationDate.toIso8601String(), 'name': instance.name, diff --git a/lib/providers/base_provider.dart b/lib/providers/base_provider.dart index 15ef98da..7be7856e 100644 --- a/lib/providers/base_provider.dart +++ b/lib/providers/base_provider.dart @@ -69,7 +69,28 @@ class WgerBaseProvider { } // Process the response - return json.decode(utf8.decode(response.bodyBytes)) as Map; + return json.decode(utf8.decode(response.bodyBytes)) as dynamic; + } + + /// Fetch and retrieve the overview list of objects, returns the JSON parsed response + Future> fetchPaginated(Uri uri) async { + final out = []; + var url = uri; + var allPagesProcessed = false; + + while (!allPagesProcessed) { + final data = await fetch(url); + + data['results'].forEach((e) => out.add(e)); + + if (data['next'] == null) { + allPagesProcessed = true; + } else { + url = Uri.parse(data['next']); + } + } + + return out; } /// POSTs a new object diff --git a/lib/providers/body_weight.dart b/lib/providers/body_weight.dart index 251858cd..82b0ad4d 100644 --- a/lib/providers/body_weight.dart +++ b/lib/providers/body_weight.dart @@ -17,24 +17,27 @@ */ import 'package:flutter/material.dart'; -import 'package:http/http.dart' as http; import 'package:wger/exceptions/http_exception.dart'; import 'package:wger/models/body_weight/weight_entry.dart'; -import 'package:wger/providers/auth.dart'; import 'package:wger/providers/base_provider.dart'; -class BodyWeightProvider extends WgerBaseProvider with ChangeNotifier { - static const bodyWeightUrl = 'weightentry'; +class BodyWeightProvider with ChangeNotifier { + final WgerBaseProvider baseProvider; + + static const BODY_WEIGHT_URL = 'weightentry'; List _entries = []; - BodyWeightProvider(AuthProvider auth, List entries, [http.Client? client]) - : _entries = entries, - super(auth, client); + + BodyWeightProvider(this.baseProvider); List get items { return [..._entries]; } + void set items(List entries) { + _entries = entries; + } + /// Clears all lists void clear() { _entries = []; @@ -59,20 +62,22 @@ class BodyWeightProvider extends WgerBaseProvider with ChangeNotifier { Future> fetchAndSetEntries() async { // Process the response - final data = await fetch(makeUrl(bodyWeightUrl, query: {'ordering': '-date'})); - final List loadedEntries = []; - for (final entry in data['results']) { - loadedEntries.add(WeightEntry.fromJson(entry)); + final data = await baseProvider.fetchPaginated(baseProvider.makeUrl( + BODY_WEIGHT_URL, + query: {'ordering': '-date', 'limit': '100'}, + )); + _entries = []; + for (final entry in data) { + _entries.add(WeightEntry.fromJson(entry)); } - _entries = loadedEntries; notifyListeners(); return _entries; } Future addEntry(WeightEntry entry) async { // Create entry and return it - final data = await post(entry.toJson(), makeUrl(bodyWeightUrl)); + final data = await baseProvider.post(entry.toJson(), baseProvider.makeUrl(BODY_WEIGHT_URL)); final WeightEntry weightEntry = WeightEntry.fromJson(data); _entries.add(weightEntry); _entries.sort((a, b) => b.date.compareTo(a.date)); @@ -82,7 +87,10 @@ class BodyWeightProvider extends WgerBaseProvider with ChangeNotifier { /// Update an existing weight entry Future editEntry(WeightEntry entry) async { - await patch(entry.toJson(), makeUrl(bodyWeightUrl, id: entry.id)); + await baseProvider.patch( + entry.toJson(), + baseProvider.makeUrl(BODY_WEIGHT_URL, id: entry.id), + ); notifyListeners(); } @@ -93,10 +101,7 @@ class BodyWeightProvider extends WgerBaseProvider with ChangeNotifier { _entries.removeAt(existingEntryIndex); notifyListeners(); - final response = await deleteRequest( - bodyWeightUrl, - id, - ); + final response = await baseProvider.deleteRequest(BODY_WEIGHT_URL, id); // ...but that didn't work, put it back again if (response.statusCode >= 400) { diff --git a/lib/providers/exercises.dart b/lib/providers/exercises.dart index 73f8b8f4..252f0ff4 100644 --- a/lib/providers/exercises.dart +++ b/lib/providers/exercises.dart @@ -240,59 +240,41 @@ class ExercisesProvider with ChangeNotifier { } Future fetchAndSetCategories() async { - final categories = await baseProvider.fetch(baseProvider.makeUrl(_categoriesUrlPath)); - try { - for (final category in categories['results']) { - _categories.add(ExerciseCategory.fromJson(category)); - } - } catch (error) { - rethrow; + final categories = await baseProvider.fetchPaginated(baseProvider.makeUrl(_categoriesUrlPath)); + for (final category in categories) { + _categories.add(ExerciseCategory.fromJson(category)); } } Future fetchAndSetVariations() async { - final variations = await baseProvider.fetch(baseProvider.makeUrl(_exerciseVariationsUrlPath)); - try { - for (final variation in variations['results']) { - _variations.add(Variation.fromJson(variation)); - } - } catch (error) { - rethrow; + final variations = + await baseProvider.fetchPaginated(baseProvider.makeUrl(_exerciseVariationsUrlPath)); + for (final variation in variations) { + _variations.add(Variation.fromJson(variation)); } } Future fetchAndSetMuscles() async { - final muscles = await baseProvider.fetch(baseProvider.makeUrl(_musclesUrlPath)); - try { - for (final muscle in muscles['results']) { - _muscles.add(Muscle.fromJson(muscle)); - } - } catch (error) { - rethrow; + final muscles = await baseProvider.fetchPaginated(baseProvider.makeUrl(_musclesUrlPath)); + + for (final muscle in muscles) { + _muscles.add(Muscle.fromJson(muscle)); } } Future fetchAndSetEquipment() async { - final equipments = await baseProvider.fetch(baseProvider.makeUrl(_equipmentUrlPath)); - try { - for (final equipment in equipments['results']) { - _equipment.add(Equipment.fromJson(equipment)); - } - } catch (error) { - rethrow; + final equipments = await baseProvider.fetchPaginated(baseProvider.makeUrl(_equipmentUrlPath)); + + for (final equipment in equipments) { + _equipment.add(Equipment.fromJson(equipment)); } } Future fetchAndSetLanguages() async { - final languageData = await baseProvider.fetch( - baseProvider.makeUrl(_languageUrlPath, query: {'limit': '100'}), - ); - try { - for (final language in languageData['results']) { - _languages.add(Language.fromJson(language)); - } - } catch (error) { - rethrow; + final languageData = await baseProvider.fetchPaginated(baseProvider.makeUrl(_languageUrlPath)); + + for (final language in languageData) { + _languages.add(Language.fromJson(language)); } } diff --git a/lib/providers/workout_plans.dart b/lib/providers/workout_plans.dart index 3a971c0f..e7f18b4c 100644 --- a/lib/providers/workout_plans.dart +++ b/lib/providers/workout_plans.dart @@ -231,37 +231,22 @@ class WorkoutPlansProvider extends WgerBaseProvider with ChangeNotifier { plan.days = days; // Logs - // - // TODO(x): looping through all results in the pagination is something we will - // probably need in the future. We should put this in some function or something - // so that we can reuse it. plan.logs = []; - var allItemsProcessed = false; - var logsURL = makeUrl(_logsUrlPath, query: { - 'workout': workoutId.toString(), - 'limit': '1000', - }); - while (!allItemsProcessed) { - final logData = await fetch(logsURL); - - for (final entry in logData['results']) { - try { - final log = Log.fromJson(entry); - log.weightUnit = _weightUnits.firstWhere((e) => e.id == log.weightUnitId); - log.repetitionUnit = _repetitionUnit.firstWhere((e) => e.id == log.weightUnitId); - log.exerciseBase = await _exercises.fetchAndSetExerciseBase(log.exerciseBaseId); - plan.logs.add(log); - } catch (e) { - dev.log('fire! fire!'); - dev.log(e.toString()); - } - } - - if (logData['next'] == null) { - allItemsProcessed = true; - } else { - logsURL = Uri.parse(logData['next']); + final logData = await fetchPaginated(makeUrl( + _logsUrlPath, + query: {'workout': workoutId.toString(), 'limit': '100'}, + )); + for (final logEntry in logData) { + try { + final log = Log.fromJson(logEntry); + log.weightUnit = _weightUnits.firstWhere((e) => e.id == log.weightUnitId); + log.repetitionUnit = _repetitionUnit.firstWhere((e) => e.id == log.weightUnitId); + log.exerciseBase = await _exercises.fetchAndSetExerciseBase(log.exerciseBaseId); + plan.logs.add(log); + } catch (e) { + dev.log('fire! fire!'); + dev.log(e.toString()); } } diff --git a/test/exercises/exercise_provider_test.dart b/test/exercises/exercise_provider_test.dart index 13f4440a..b580f61b 100644 --- a/test/exercises/exercise_provider_test.dart +++ b/test/exercises/exercise_provider_test.dart @@ -88,21 +88,24 @@ void main() { // Mock categories when(mockBaseProvider.makeUrl(categoryUrl)).thenReturn(tCategoryEntriesUri); - when(mockBaseProvider.fetch(tCategoryEntriesUri)).thenAnswer((_) => Future.value(tCategoryMap)); + when(mockBaseProvider.fetchPaginated(tCategoryEntriesUri)) + .thenAnswer((_) => Future.value(tCategoryMap['results'])); // Mock muscles when(mockBaseProvider.makeUrl(muscleUrl)).thenReturn(tMuscleEntriesUri); - when(mockBaseProvider.fetch(tMuscleEntriesUri)).thenAnswer((_) => Future.value(tMuscleMap)); + when(mockBaseProvider.fetchPaginated(tMuscleEntriesUri)) + .thenAnswer((_) => Future.value(tMuscleMap['results'])); // Mock equipment when(mockBaseProvider.makeUrl(equipmentUrl)).thenReturn(tEquipmentEntriesUri); - when(mockBaseProvider.fetch(tEquipmentEntriesUri)) - .thenAnswer((_) => Future.value(tEquipmentMap)); + when(mockBaseProvider.fetchPaginated(tEquipmentEntriesUri)) + .thenAnswer((_) => Future.value(tEquipmentMap['results'])); // Mock languages when(mockBaseProvider.makeUrl(languageUrl, query: anyNamed('query'))) .thenReturn(tLanguageEntriesUri); - when(mockBaseProvider.fetch(tLanguageEntriesUri)).thenAnswer((_) => Future.value(tLanguageMap)); + when(mockBaseProvider.fetchPaginated(tLanguageEntriesUri)) + .thenAnswer((_) => Future.value(tLanguageMap['results'])); // Mock base info response when(mockBaseProvider.makeUrl(exerciseBaseInfoUrl)).thenReturn(texerciseBaseInfoUri); diff --git a/test/fixtures/pagination/pagination1.json b/test/fixtures/pagination/pagination1.json new file mode 100644 index 00000000..3bf91357 --- /dev/null +++ b/test/fixtures/pagination/pagination1.json @@ -0,0 +1,15 @@ +{ + "count": 2, + "next": "https://localhost/api/v2/itcrowd/?limit=20&offset=20", + "previous": null, + "results": [ + { + "id": 1, + "value": "You wouldn't steal a handbag." + }, + { + "id": 2, + "value": "You wouldn't steal a car." + } + ] +} \ No newline at end of file diff --git a/test/fixtures/pagination/pagination2.json b/test/fixtures/pagination/pagination2.json new file mode 100644 index 00000000..091eb266 --- /dev/null +++ b/test/fixtures/pagination/pagination2.json @@ -0,0 +1,15 @@ +{ + "count": 2, + "next": "https://localhost/api/v2/itcrowd/?limit=20&offset=40", + "previous": "https://localhost/api/v2/itcrowd/?limit=20", + "results": [ + { + "id": 3, + "value": "You wouldn't steal a baby." + }, + { + "id": 4, + "value": "You wouldn't shoot a policeman." + } + ] +} \ No newline at end of file diff --git a/test/fixtures/pagination/pagination3.json b/test/fixtures/pagination/pagination3.json new file mode 100644 index 00000000..9121ee58 --- /dev/null +++ b/test/fixtures/pagination/pagination3.json @@ -0,0 +1,11 @@ +{ + "count": 1, + "next": null, + "previous": "https://localhost/api/v2/itcrowd/?limit=20&offset=40", + "results": [ + { + "id": 5, + "value": "And then steal his helmet." + } + ] +} \ No newline at end of file diff --git a/test/fixtures/weight/weight_entries.json b/test/fixtures/weight/weight_entries.json new file mode 100644 index 00000000..02db5368 --- /dev/null +++ b/test/fixtures/weight/weight_entries.json @@ -0,0 +1,73 @@ +{ + "count": 11, + "next": null, + "previous": null, + "results": [ + { + "id": 6, + "date": "2021-11-01", + "weight": "99.00", + "user": 3 + }, + { + "id": 48, + "date": "2021-12-01", + "weight": "88.00", + "user": 3 + }, + { + "id": 30, + "date": "2021-12-05", + "weight": "70.00", + "user": 3 + }, + { + "id": 2, + "date": "2021-12-15", + "weight": "84.00", + "user": 3 + }, + { + "id": 9, + "date": "2021-12-17", + "weight": "94.00", + "user": 3 + }, + { + "id": 23, + "date": "2021-12-28", + "weight": "80.00", + "user": 3 + }, + { + "id": 22, + "date": "2022-01-04", + "weight": "89.00", + "user": 3 + }, + { + "id": 21, + "date": "2022-01-08", + "weight": "98.00", + "user": 3 + }, + { + "id": 90, + "date": "2022-01-24", + "weight": "100.00", + "user": 3 + }, + { + "id": 98, + "date": "2022-01-31", + "weight": "60.00", + "user": 3 + }, + { + "id": 97, + "date": "2022-02-03", + "weight": "55.00", + "user": 3 + } + ] +} \ No newline at end of file diff --git a/test/gallery/gallery_screen_test.mocks.dart b/test/gallery/gallery_screen_test.mocks.dart index 409cf08f..476d4939 100644 --- a/test/gallery/gallery_screen_test.mocks.dart +++ b/test/gallery/gallery_screen_test.mocks.dart @@ -181,7 +181,8 @@ class MockGalleryProvider extends _i1.Mock implements _i4.GalleryProvider { returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); @override - Map getDefaultHeaders({dynamic includeAuth = false}) => (super.noSuchMethod( + Map getDefaultHeaders({dynamic includeAuth = false}) => + (super.noSuchMethod( Invocation.method( #getDefaultHeaders, [], @@ -225,9 +226,18 @@ class MockGalleryProvider extends _i1.Mock implements _i4.GalleryProvider { #fetch, [uri], ), - returnValue: _i6.Future>.value({}), + returnValue: + _i6.Future>.value({}), ) as _i6.Future>); @override + _i6.Future> fetchPaginated(Uri? uri) => (super.noSuchMethod( + Invocation.method( + #fetchPaginated, + [uri], + ), + returnValue: _i6.Future>.value([]), + ) as _i6.Future>); + @override _i6.Future> post( Map? data, Uri? uri, @@ -240,7 +250,8 @@ class MockGalleryProvider extends _i1.Mock implements _i4.GalleryProvider { uri, ], ), - returnValue: _i6.Future>.value({}), + returnValue: + _i6.Future>.value({}), ) as _i6.Future>); @override _i6.Future> patch( @@ -255,7 +266,8 @@ class MockGalleryProvider extends _i1.Mock implements _i4.GalleryProvider { uri, ], ), - returnValue: _i6.Future>.value({}), + returnValue: + _i6.Future>.value({}), ) as _i6.Future>); @override _i6.Future<_i3.Response> deleteRequest( diff --git a/test/measurements/measurement_categories_screen_test.mocks.dart b/test/measurements/measurement_categories_screen_test.mocks.dart index 9eda4a2f..eab13e15 100644 --- a/test/measurements/measurement_categories_screen_test.mocks.dart +++ b/test/measurements/measurement_categories_screen_test.mocks.dart @@ -23,7 +23,8 @@ import 'package:wger/providers/measurement.dart' as _i4; // ignore_for_file: camel_case_types // ignore_for_file: subtype_of_sealed_class -class _FakeWgerBaseProvider_0 extends _i1.SmartFake implements _i2.WgerBaseProvider { +class _FakeWgerBaseProvider_0 extends _i1.SmartFake + implements _i2.WgerBaseProvider { _FakeWgerBaseProvider_0( Object parent, Invocation parentInvocation, @@ -33,7 +34,8 @@ class _FakeWgerBaseProvider_0 extends _i1.SmartFake implements _i2.WgerBaseProvi ); } -class _FakeMeasurementCategory_1 extends _i1.SmartFake implements _i3.MeasurementCategory { +class _FakeMeasurementCategory_1 extends _i1.SmartFake + implements _i3.MeasurementCategory { _FakeMeasurementCategory_1( Object parent, Invocation parentInvocation, @@ -46,7 +48,8 @@ class _FakeMeasurementCategory_1 extends _i1.SmartFake implements _i3.Measuremen /// A class which mocks [MeasurementProvider]. /// /// See the documentation for Mockito's code generation for more information. -class MockMeasurementProvider extends _i1.Mock implements _i4.MeasurementProvider { +class MockMeasurementProvider extends _i1.Mock + implements _i4.MeasurementProvider { MockMeasurementProvider() { _i1.throwOnMissingStub(this); } @@ -119,7 +122,8 @@ class MockMeasurementProvider extends _i1.Mock implements _i4.MeasurementProvide returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); @override - _i5.Future addCategory(_i3.MeasurementCategory? category) => (super.noSuchMethod( + _i5.Future addCategory(_i3.MeasurementCategory? category) => + (super.noSuchMethod( Invocation.method( #addCategory, [category], diff --git a/test/measurements/measurement_provider_test.mocks.dart b/test/measurements/measurement_provider_test.mocks.dart index 200245dd..0def8526 100644 --- a/test/measurements/measurement_provider_test.mocks.dart +++ b/test/measurements/measurement_provider_test.mocks.dart @@ -102,7 +102,8 @@ class MockWgerBaseProvider extends _i1.Mock implements _i4.WgerBaseProvider { returnValueForMissingStub: null, ); @override - Map getDefaultHeaders({dynamic includeAuth = false}) => (super.noSuchMethod( + Map getDefaultHeaders({dynamic includeAuth = false}) => + (super.noSuchMethod( Invocation.method( #getDefaultHeaders, [], @@ -146,9 +147,18 @@ class MockWgerBaseProvider extends _i1.Mock implements _i4.WgerBaseProvider { #fetch, [uri], ), - returnValue: _i5.Future>.value({}), + returnValue: + _i5.Future>.value({}), ) as _i5.Future>); @override + _i5.Future> fetchPaginated(Uri? uri) => (super.noSuchMethod( + Invocation.method( + #fetchPaginated, + [uri], + ), + returnValue: _i5.Future>.value([]), + ) as _i5.Future>); + @override _i5.Future> post( Map? data, Uri? uri, @@ -161,7 +171,8 @@ class MockWgerBaseProvider extends _i1.Mock implements _i4.WgerBaseProvider { uri, ], ), - returnValue: _i5.Future>.value({}), + returnValue: + _i5.Future>.value({}), ) as _i5.Future>); @override _i5.Future> patch( @@ -176,7 +187,8 @@ class MockWgerBaseProvider extends _i1.Mock implements _i4.WgerBaseProvider { uri, ], ), - returnValue: _i5.Future>.value({}), + returnValue: + _i5.Future>.value({}), ) as _i5.Future>); @override _i5.Future<_i3.Response> deleteRequest( diff --git a/test/nutrition/nutritional_plan_form_test.mocks.dart b/test/nutrition/nutritional_plan_form_test.mocks.dart index 2a2a0f3d..e9542750 100644 --- a/test/nutrition/nutritional_plan_form_test.mocks.dart +++ b/test/nutrition/nutritional_plan_form_test.mocks.dart @@ -46,7 +46,8 @@ class _FakeClient_1 extends _i1.SmartFake implements _i3.Client { ); } -class _FakeNutritionalPlan_2 extends _i1.SmartFake implements _i4.NutritionalPlan { +class _FakeNutritionalPlan_2 extends _i1.SmartFake + implements _i4.NutritionalPlan { _FakeNutritionalPlan_2( Object parent, Invocation parentInvocation, @@ -109,7 +110,8 @@ class _FakeResponse_7 extends _i1.SmartFake implements _i3.Response { /// A class which mocks [NutritionPlansProvider]. /// /// See the documentation for Mockito's code generation for more information. -class MockNutritionPlansProvider extends _i1.Mock implements _i8.NutritionPlansProvider { +class MockNutritionPlansProvider extends _i1.Mock + implements _i8.NutritionPlansProvider { MockNutritionPlansProvider() { _i1.throwOnMissingStub(this); } @@ -202,12 +204,14 @@ class MockNutritionPlansProvider extends _i1.Mock implements _i8.NutritionPlansP returnValueForMissingStub: _i9.Future.value(), ) as _i9.Future); @override - _i9.Future<_i4.NutritionalPlan> fetchAndSetPlanSparse(int? planId) => (super.noSuchMethod( + _i9.Future<_i4.NutritionalPlan> fetchAndSetPlanSparse(int? planId) => + (super.noSuchMethod( Invocation.method( #fetchAndSetPlanSparse, [planId], ), - returnValue: _i9.Future<_i4.NutritionalPlan>.value(_FakeNutritionalPlan_2( + returnValue: + _i9.Future<_i4.NutritionalPlan>.value(_FakeNutritionalPlan_2( this, Invocation.method( #fetchAndSetPlanSparse, @@ -216,12 +220,14 @@ class MockNutritionPlansProvider extends _i1.Mock implements _i8.NutritionPlansP )), ) as _i9.Future<_i4.NutritionalPlan>); @override - _i9.Future<_i4.NutritionalPlan> fetchAndSetPlanFull(int? planId) => (super.noSuchMethod( + _i9.Future<_i4.NutritionalPlan> fetchAndSetPlanFull(int? planId) => + (super.noSuchMethod( Invocation.method( #fetchAndSetPlanFull, [planId], ), - returnValue: _i9.Future<_i4.NutritionalPlan>.value(_FakeNutritionalPlan_2( + returnValue: + _i9.Future<_i4.NutritionalPlan>.value(_FakeNutritionalPlan_2( this, Invocation.method( #fetchAndSetPlanFull, @@ -230,12 +236,14 @@ class MockNutritionPlansProvider extends _i1.Mock implements _i8.NutritionPlansP )), ) as _i9.Future<_i4.NutritionalPlan>); @override - _i9.Future<_i4.NutritionalPlan> addPlan(_i4.NutritionalPlan? planData) => (super.noSuchMethod( + _i9.Future<_i4.NutritionalPlan> addPlan(_i4.NutritionalPlan? planData) => + (super.noSuchMethod( Invocation.method( #addPlan, [planData], ), - returnValue: _i9.Future<_i4.NutritionalPlan>.value(_FakeNutritionalPlan_2( + returnValue: + _i9.Future<_i4.NutritionalPlan>.value(_FakeNutritionalPlan_2( this, Invocation.method( #addPlan, @@ -333,7 +341,8 @@ class MockNutritionPlansProvider extends _i1.Mock implements _i8.NutritionPlansP )), ) as _i9.Future<_i6.MealItem>); @override - _i9.Future deleteMealItem(_i6.MealItem? mealItem) => (super.noSuchMethod( + _i9.Future deleteMealItem(_i6.MealItem? mealItem) => + (super.noSuchMethod( Invocation.method( #deleteMealItem, [mealItem], @@ -342,7 +351,8 @@ class MockNutritionPlansProvider extends _i1.Mock implements _i8.NutritionPlansP returnValueForMissingStub: _i9.Future.value(), ) as _i9.Future); @override - _i9.Future<_i7.Ingredient> fetchIngredient(int? ingredientId) => (super.noSuchMethod( + _i9.Future<_i7.Ingredient> fetchIngredient(int? ingredientId) => + (super.noSuchMethod( Invocation.method( #fetchIngredient, [ingredientId], @@ -380,7 +390,8 @@ class MockNutritionPlansProvider extends _i1.Mock implements _i8.NutritionPlansP returnValue: _i9.Future>.value([]), ) as _i9.Future>); @override - _i9.Future<_i7.Ingredient?> searchIngredientWithCode(String? code) => (super.noSuchMethod( + _i9.Future<_i7.Ingredient?> searchIngredientWithCode(String? code) => + (super.noSuchMethod( Invocation.method( #searchIngredientWithCode, [code], @@ -431,7 +442,8 @@ class MockNutritionPlansProvider extends _i1.Mock implements _i8.NutritionPlansP returnValueForMissingStub: _i9.Future.value(), ) as _i9.Future); @override - _i9.Future fetchAndSetLogs(_i4.NutritionalPlan? plan) => (super.noSuchMethod( + _i9.Future fetchAndSetLogs(_i4.NutritionalPlan? plan) => + (super.noSuchMethod( Invocation.method( #fetchAndSetLogs, [plan], @@ -440,7 +452,8 @@ class MockNutritionPlansProvider extends _i1.Mock implements _i8.NutritionPlansP returnValueForMissingStub: _i9.Future.value(), ) as _i9.Future); @override - Map getDefaultHeaders({dynamic includeAuth = false}) => (super.noSuchMethod( + Map getDefaultHeaders({dynamic includeAuth = false}) => + (super.noSuchMethod( Invocation.method( #getDefaultHeaders, [], @@ -484,9 +497,18 @@ class MockNutritionPlansProvider extends _i1.Mock implements _i8.NutritionPlansP #fetch, [uri], ), - returnValue: _i9.Future>.value({}), + returnValue: + _i9.Future>.value({}), ) as _i9.Future>); @override + _i9.Future> fetchPaginated(Uri? uri) => (super.noSuchMethod( + Invocation.method( + #fetchPaginated, + [uri], + ), + returnValue: _i9.Future>.value([]), + ) as _i9.Future>); + @override _i9.Future> post( Map? data, Uri? uri, @@ -499,7 +521,8 @@ class MockNutritionPlansProvider extends _i1.Mock implements _i8.NutritionPlansP uri, ], ), - returnValue: _i9.Future>.value({}), + returnValue: + _i9.Future>.value({}), ) as _i9.Future>); @override _i9.Future> patch( @@ -514,7 +537,8 @@ class MockNutritionPlansProvider extends _i1.Mock implements _i8.NutritionPlansP uri, ], ), - returnValue: _i9.Future>.value({}), + returnValue: + _i9.Future>.value({}), ) as _i9.Future>); @override _i9.Future<_i3.Response> deleteRequest( diff --git a/test/nutrition/nutritional_plan_screen_test.dart b/test/nutrition/nutritional_plan_screen_test.dart index 407514cd..4b1546f3 100644 --- a/test/nutrition/nutritional_plan_screen_test.dart +++ b/test/nutrition/nutritional_plan_screen_test.dart @@ -42,7 +42,7 @@ void main() { create: (context) => NutritionPlansProvider(testAuthProvider, [], client), ), ChangeNotifierProvider( - create: (context) => BodyWeightProvider(testAuthProvider, [], client), + create: (context) => BodyWeightProvider(mockBaseProvider), ), ], child: MaterialApp( diff --git a/test/other/base_provider_test.dart b/test/other/base_provider_test.dart index bd3c4715..e416e1b8 100644 --- a/test/other/base_provider_test.dart +++ b/test/other/base_provider_test.dart @@ -18,10 +18,14 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:http/http.dart' as http; +import 'package:http/http.dart'; import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; import 'package:wger/providers/base_provider.dart'; +import '../fixtures/fixture_reader.dart'; import '../utils.dart'; +import 'base_provider_test.mocks.dart'; @GenerateMocks([http.Client]) void main() { @@ -94,4 +98,50 @@ void main() { ); }); }); + + group('Retrieving and fetching data', () { + test('Test loading paginated data', () async { + // Arrange + final mockHttpClient = MockClient(); + final response1 = Response(fixture('pagination/pagination1.json'), 200); + final response2 = Response(fixture('pagination/pagination2.json'), 200); + final response3 = Response(fixture('pagination/pagination3.json'), 200); + final Uri paginationUri1 = Uri( + scheme: 'https', + host: 'localhost', + path: 'api/v2/itcrowd/', + ); + final Uri paginationUri2 = Uri( + scheme: 'https', + host: 'localhost', + path: 'api/v2/itcrowd/', + query: 'limit=20&offset=20', + ); + final Uri paginationUri3 = Uri( + scheme: 'https', + host: 'localhost', + path: 'api/v2/itcrowd/', + query: 'limit=20&offset=40', + ); + + when(mockHttpClient.get(paginationUri1, headers: anyNamed('headers'))) + .thenAnswer((_) => Future.value(response1)); + when(mockHttpClient.get(paginationUri2, headers: anyNamed('headers'))) + .thenAnswer((_) => Future.value(response2)); + when(mockHttpClient.get(paginationUri3, headers: anyNamed('headers'))) + .thenAnswer((_) => Future.value(response3)); + + // Act + final WgerBaseProvider provider = WgerBaseProvider(testAuthProvider, mockHttpClient); + final data = await provider.fetchPaginated(paginationUri1); + + // Assert + expect(data.length, 5); + expect(data[0], {'id': 1, 'value': "You wouldn't steal a handbag."}); + expect(data[1], {'id': 2, 'value': "You wouldn't steal a car."}); + expect(data[2], {'id': 3, 'value': "You wouldn't steal a baby."}); + expect(data[3], {'id': 4, 'value': "You wouldn't shoot a policeman."}); + expect(data[4], {'id': 5, 'value': 'And then steal his helmet.'}); + }); + }); } diff --git a/test/other/base_provider_test.mocks.dart b/test/other/base_provider_test.mocks.dart index d5c998c8..5483f2a0 100644 --- a/test/other/base_provider_test.mocks.dart +++ b/test/other/base_provider_test.mocks.dart @@ -31,7 +31,8 @@ class _FakeResponse_0 extends _i1.SmartFake implements _i2.Response { ); } -class _FakeStreamedResponse_1 extends _i1.SmartFake implements _i2.StreamedResponse { +class _FakeStreamedResponse_1 extends _i1.SmartFake + implements _i2.StreamedResponse { _FakeStreamedResponse_1( Object parent, Invocation parentInvocation, @@ -236,12 +237,14 @@ class MockClient extends _i1.Mock implements _i2.Client { returnValue: _i3.Future<_i5.Uint8List>.value(_i5.Uint8List(0)), ) as _i3.Future<_i5.Uint8List>); @override - _i3.Future<_i2.StreamedResponse> send(_i2.BaseRequest? request) => (super.noSuchMethod( + _i3.Future<_i2.StreamedResponse> send(_i2.BaseRequest? request) => + (super.noSuchMethod( Invocation.method( #send, [request], ), - returnValue: _i3.Future<_i2.StreamedResponse>.value(_FakeStreamedResponse_1( + returnValue: + _i3.Future<_i2.StreamedResponse>.value(_FakeStreamedResponse_1( this, Invocation.method( #send, diff --git a/test/weight/weight_provider_test.dart b/test/weight/weight_provider_test.dart index f120b15a..dd2e0ab5 100644 --- a/test/weight/weight_provider_test.dart +++ b/test/weight/weight_provider_test.dart @@ -16,82 +16,83 @@ * along with this program. If not, see . */ +import 'dart:convert'; + import 'package:flutter_test/flutter_test.dart'; -import 'package:http/http.dart' as http; +import 'package:http/http.dart'; import 'package:mockito/mockito.dart'; import 'package:wger/models/body_weight/weight_entry.dart'; import 'package:wger/providers/body_weight.dart'; -import '../other/base_provider_test.mocks.dart'; +import '../fixtures/fixture_reader.dart'; import '../utils.dart'; void main() { group('test body weight provider', () { test('Test that the weight entries are correctly loaded', () async { - final client = MockClient(); + final uri = Uri( + scheme: 'https', + host: 'localhost', + path: 'api/v2/weightentry/', + ); + when(mockBaseProvider.makeUrl(any, query: anyNamed('query'))).thenReturn(uri); + final Map weightEntries = jsonDecode( + fixture('weight/weight_entries.json'), + ); - // Mock the server response - when(client.get( - Uri.https('localhost', 'api/v2/weightentry/', {'ordering': '-date'}), - headers: anyNamed('headers'), - )).thenAnswer((_) async => http.Response( - '{"results": [{"id": 1, "date": "2021-01-01", "weight": "80.00"}, ' - '{"id": 2, "date": "2021-01-10", "weight": "99"},' - '{"id": 3, "date": "2021-01-20", "weight": "100.01"}]}', - 200)); + when(mockBaseProvider.fetchPaginated(uri)).thenAnswer( + (_) => Future.value(weightEntries['results']), + ); // Load the entries - final BodyWeightProvider provider = BodyWeightProvider(testAuthProvider, [], client); + final BodyWeightProvider provider = BodyWeightProvider(mockBaseProvider); await provider.fetchAndSetEntries(); // Check that everything is ok expect(provider.items, isA>()); - expect(provider.items.length, 3); + expect(provider.items.length, 11); }); test('Test adding a new weight entry', () async { - final client = MockClient(); + // Arrange + final uri = Uri( + scheme: 'https', + host: 'localhost', + path: 'api/v2/weightentry/', + ); + when(mockBaseProvider.makeUrl(any, query: anyNamed('query'))).thenReturn(uri); + when(mockBaseProvider.post({'id': null, 'weight': '80', 'date': '2021-01-01'}, uri)) + .thenAnswer((_) => Future.value({'id': 25, 'date': '2021-01-01', 'weight': '80'})); - // Mock the server response - when( - client.post( - Uri.https('localhost', 'api/v2/weightentry/'), - headers: anyNamed('headers'), - body: '{"id":null,"weight":"80","date":"2021-01-01"}', - ), - ).thenAnswer( - (_) async => http.Response('{"id": 25, "date": "2021-01-01", "weight": "80"}', 200)); - - // POST the data to the server + // Act + final BodyWeightProvider provider = BodyWeightProvider(mockBaseProvider); final WeightEntry weightEntry = WeightEntry(date: DateTime(2021, 1, 1), weight: 80); - final BodyWeightProvider provider = BodyWeightProvider(testAuthProvider, [], client); final WeightEntry weightEntryNew = await provider.addEntry(weightEntry); - // Check that the server response is what we expect + // Assert expect(weightEntryNew.id, 25); expect(weightEntryNew.date, DateTime(2021, 1, 1)); expect(weightEntryNew.weight, 80); }); test('Test deleting an existing weight entry', () async { - final client = MockClient(); - - // Mock the server response - when(client.delete( - Uri.https('localhost', 'api/v2/weightentry/4/'), - headers: anyNamed('headers'), - )).thenAnswer((_) async => http.Response('', 200)); + // Arrange + final uri = Uri( + scheme: 'https', + host: 'localhost', + path: 'api/v2/weightentry/4/', + ); + when(mockBaseProvider.makeUrl(any, query: anyNamed('query'))).thenReturn(uri); + when(mockBaseProvider.deleteRequest('weightentry', 4)).thenAnswer( + (_) => Future.value(Response("{'id': 4, 'date': '2021-01-01', 'weight': '80'}", 204))); // DELETE the data from the server - final BodyWeightProvider provider = BodyWeightProvider( - testAuthProvider, - [ - WeightEntry(id: 4, weight: 80, date: DateTime(2021, 1, 1)), - WeightEntry(id: 2, weight: 100, date: DateTime(2021, 2, 2)), - WeightEntry(id: 5, weight: 60, date: DateTime(2021, 2, 2)) - ], - client, - ); + final BodyWeightProvider provider = BodyWeightProvider(mockBaseProvider); + provider.items = [ + WeightEntry(id: 4, weight: 80, date: DateTime(2021, 1, 1)), + WeightEntry(id: 2, weight: 100, date: DateTime(2021, 2, 2)), + WeightEntry(id: 5, weight: 60, date: DateTime(2021, 2, 2)) + ]; await provider.deleteEntry(4); // Check that the entry was removed from the entry list diff --git a/test/weight/weight_screen_test.dart b/test/weight/weight_screen_test.dart index d0db6acd..54ead57f 100644 --- a/test/weight/weight_screen_test.dart +++ b/test/weight/weight_screen_test.dart @@ -19,7 +19,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:http/http.dart' as http; import 'package:mockito/mockito.dart'; import 'package:provider/provider.dart'; import 'package:wger/providers/body_weight.dart'; @@ -29,48 +28,32 @@ import 'package:wger/widgets/core/charts.dart'; import 'package:wger/widgets/weight/forms.dart'; import '../../test_data/body_weight.dart'; -import '../other/base_provider_test.mocks.dart'; -import '../utils.dart'; +import '../workout/weight_unit_form_widget_test.mocks.dart'; void main() { - Widget createHomeScreen({locale = 'en'}) { - final client = MockClient(); - when(client.delete( - any, - headers: anyNamed('headers'), - )).thenAnswer((_) async => http.Response('', 200)); - when(client.post( - any, - headers: anyNamed('headers'), - body: anyNamed('body'), - )).thenAnswer( - (_) async => http.Response('{"id": 3, "date": "2021-01-01", "weight": "80"}', 200)); + MockBodyWeightProvider mockWeightProvider = MockBodyWeightProvider(); + + Widget createWeightScreen({locale = 'en'}) { + mockWeightProvider = MockBodyWeightProvider(); + when(mockWeightProvider.items).thenReturn(getWeightEntries()); return ChangeNotifierProvider( - create: (context) => BodyWeightProvider( - testAuthProvider, - [ - weightEntry1, - weightEntry2, - ], - client, - ), + create: (context) => mockWeightProvider, child: MaterialApp( locale: Locale(locale), localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, home: WeightScreen(), routes: { - FormScreen.routeName: (ctx) => FormScreen(), + FormScreen.routeName: (_) => FormScreen(), }, ), ); } testWidgets('Test the widgets on the body weight screen', (WidgetTester tester) async { - await tester.pumpWidget(createHomeScreen()); + await tester.pumpWidget(createWeightScreen()); - //debugDumpApp(); expect(find.text('Weight'), findsOneWidget); expect(find.byType(MeasurementChartWidget), findsOneWidget); expect(find.byType(Dismissible), findsNWidgets(2)); @@ -78,15 +61,16 @@ void main() { }); testWidgets('Test deleting an item by dragging the dismissible', (WidgetTester tester) async { - await tester.pumpWidget(createHomeScreen()); + await tester.pumpWidget(createWeightScreen()); await tester.drag(find.byKey(const Key('1')), const Offset(-500.0, 0.0)); await tester.pumpAndSettle(); + verify(mockWeightProvider.deleteEntry(1)).called(1); expect(find.byType(ListTile), findsOneWidget); }); testWidgets('Test the form on the body weight screen', (WidgetTester tester) async { - await tester.pumpWidget(createHomeScreen()); + await tester.pumpWidget(createWeightScreen()); expect(find.byType(WeightForm), findsNothing); await tester.tap(find.byType(FloatingActionButton)); @@ -95,14 +79,14 @@ void main() { }); testWidgets('Tests the localization of dates - EN', (WidgetTester tester) async { - await tester.pumpWidget(createHomeScreen()); + await tester.pumpWidget(createWeightScreen()); expect(find.text('1/1/2021'), findsOneWidget); expect(find.text('1/10/2021'), findsOneWidget); }); testWidgets('Tests the localization of dates - DE', (WidgetTester tester) async { - await tester.pumpWidget(createHomeScreen(locale: 'de')); + await tester.pumpWidget(createWeightScreen(locale: 'de')); expect(find.text('1.1.2021'), findsOneWidget); expect(find.text('10.1.2021'), findsOneWidget); diff --git a/test/workout/gym_mode_screen_test.mocks.dart b/test/workout/gym_mode_screen_test.mocks.dart index 63fb4e2f..362ca2e8 100644 --- a/test/workout/gym_mode_screen_test.mocks.dart +++ b/test/workout/gym_mode_screen_test.mocks.dart @@ -26,7 +26,8 @@ import 'package:wger/providers/exercises.dart' as _i8; // ignore_for_file: camel_case_types // ignore_for_file: subtype_of_sealed_class -class _FakeWgerBaseProvider_0 extends _i1.SmartFake implements _i2.WgerBaseProvider { +class _FakeWgerBaseProvider_0 extends _i1.SmartFake + implements _i2.WgerBaseProvider { _FakeWgerBaseProvider_0( Object parent, Invocation parentInvocation, @@ -46,7 +47,8 @@ class _FakeExerciseBase_1 extends _i1.SmartFake implements _i3.ExerciseBase { ); } -class _FakeExerciseCategory_2 extends _i1.SmartFake implements _i4.ExerciseCategory { +class _FakeExerciseCategory_2 extends _i1.SmartFake + implements _i4.ExerciseCategory { _FakeExerciseCategory_2( Object parent, Invocation parentInvocation, @@ -103,7 +105,8 @@ class MockExercisesProvider extends _i1.Mock implements _i8.ExercisesProvider { ), ) as _i2.WgerBaseProvider); @override - set exerciseBases(List<_i3.ExerciseBase>? exercisesBases) => super.noSuchMethod( + set exerciseBases(List<_i3.ExerciseBase>? exercisesBases) => + super.noSuchMethod( Invocation.setter( #exerciseBases, exercisesBases, @@ -116,7 +119,8 @@ class MockExercisesProvider extends _i1.Mock implements _i8.ExercisesProvider { returnValue: <_i3.ExerciseBase>[], ) as List<_i3.ExerciseBase>); @override - set filteredExerciseBases(List<_i3.ExerciseBase>? newFilteredExercises) => super.noSuchMethod( + set filteredExerciseBases(List<_i3.ExerciseBase>? newFilteredExercises) => + super.noSuchMethod( Invocation.setter( #filteredExerciseBases, newFilteredExercises, @@ -124,7 +128,8 @@ class MockExercisesProvider extends _i1.Mock implements _i8.ExercisesProvider { returnValueForMissingStub: null, ); @override - Map> get exerciseBasesByVariation => (super.noSuchMethod( + Map> get exerciseBasesByVariation => + (super.noSuchMethod( Invocation.getter(#exerciseBasesByVariation), returnValue: >{}, ) as Map>); @@ -321,7 +326,8 @@ class MockExercisesProvider extends _i1.Mock implements _i8.ExercisesProvider { returnValueForMissingStub: _i9.Future.value(), ) as _i9.Future); @override - _i9.Future<_i3.ExerciseBase> fetchAndSetExerciseBase(int? exerciseBaseId) => (super.noSuchMethod( + _i9.Future<_i3.ExerciseBase> fetchAndSetExerciseBase(int? exerciseBaseId) => + (super.noSuchMethod( Invocation.method( #fetchAndSetExerciseBase, [exerciseBaseId], @@ -335,7 +341,8 @@ class MockExercisesProvider extends _i1.Mock implements _i8.ExercisesProvider { )), ) as _i9.Future<_i3.ExerciseBase>); @override - _i3.ExerciseBase readExerciseBaseFromBaseInfo(dynamic baseData) => (super.noSuchMethod( + _i3.ExerciseBase readExerciseBaseFromBaseInfo(dynamic baseData) => + (super.noSuchMethod( Invocation.method( #readExerciseBaseFromBaseInfo, [baseData], @@ -379,7 +386,8 @@ class MockExercisesProvider extends _i1.Mock implements _i8.ExercisesProvider { languageCode, ], ), - returnValue: _i9.Future>.value(<_i3.ExerciseBase>[]), + returnValue: + _i9.Future>.value(<_i3.ExerciseBase>[]), ) as _i9.Future>); @override void addListener(_i10.VoidCallback? listener) => super.noSuchMethod( diff --git a/test/workout/weight_unit_form_widget_test.mocks.dart b/test/workout/weight_unit_form_widget_test.mocks.dart index b35c3408..4cdb549f 100644 --- a/test/workout/weight_unit_form_widget_test.mocks.dart +++ b/test/workout/weight_unit_form_widget_test.mocks.dart @@ -76,7 +76,8 @@ class _FakeResponse_4 extends _i1.SmartFake implements _i3.Response { /// A class which mocks [BodyWeightProvider]. /// /// See the documentation for Mockito's code generation for more information. -class MockBodyWeightProvider extends _i1.Mock implements _i5.BodyWeightProvider { +class MockBodyWeightProvider extends _i1.Mock + implements _i5.BodyWeightProvider { MockBodyWeightProvider() { _i1.throwOnMissingStub(this); } @@ -146,7 +147,8 @@ class MockBodyWeightProvider extends _i1.Mock implements _i5.BodyWeightProvider ), ) as _i4.WeightEntry); @override - _i4.WeightEntry? findByDate(DateTime? date) => (super.noSuchMethod(Invocation.method( + _i4.WeightEntry? findByDate(DateTime? date) => + (super.noSuchMethod(Invocation.method( #findByDate, [date], )) as _i4.WeightEntry?); @@ -156,10 +158,12 @@ class MockBodyWeightProvider extends _i1.Mock implements _i5.BodyWeightProvider #fetchAndSetEntries, [], ), - returnValue: _i6.Future>.value(<_i4.WeightEntry>[]), + returnValue: + _i6.Future>.value(<_i4.WeightEntry>[]), ) as _i6.Future>); @override - _i6.Future<_i4.WeightEntry> addEntry(_i4.WeightEntry? entry) => (super.noSuchMethod( + _i6.Future<_i4.WeightEntry> addEntry(_i4.WeightEntry? entry) => + (super.noSuchMethod( Invocation.method( #addEntry, [entry], @@ -191,7 +195,8 @@ class MockBodyWeightProvider extends _i1.Mock implements _i5.BodyWeightProvider returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); @override - Map getDefaultHeaders({dynamic includeAuth = false}) => (super.noSuchMethod( + Map getDefaultHeaders({dynamic includeAuth = false}) => + (super.noSuchMethod( Invocation.method( #getDefaultHeaders, [], @@ -235,9 +240,18 @@ class MockBodyWeightProvider extends _i1.Mock implements _i5.BodyWeightProvider #fetch, [uri], ), - returnValue: _i6.Future>.value({}), + returnValue: + _i6.Future>.value({}), ) as _i6.Future>); @override + _i6.Future> fetchPaginated(Uri? uri) => (super.noSuchMethod( + Invocation.method( + #fetchPaginated, + [uri], + ), + returnValue: _i6.Future>.value([]), + ) as _i6.Future>); + @override _i6.Future> post( Map? data, Uri? uri, @@ -250,7 +264,8 @@ class MockBodyWeightProvider extends _i1.Mock implements _i5.BodyWeightProvider uri, ], ), - returnValue: _i6.Future>.value({}), + returnValue: + _i6.Future>.value({}), ) as _i6.Future>); @override _i6.Future> patch( @@ -265,7 +280,8 @@ class MockBodyWeightProvider extends _i1.Mock implements _i5.BodyWeightProvider uri, ], ), - returnValue: _i6.Future>.value({}), + returnValue: + _i6.Future>.value({}), ) as _i6.Future>); @override _i6.Future<_i3.Response> deleteRequest( diff --git a/test/workout/workout_form_test.mocks.dart b/test/workout/workout_form_test.mocks.dart index 3ff48844..2ce40999 100644 --- a/test/workout/workout_form_test.mocks.dart +++ b/test/workout/workout_form_test.mocks.dart @@ -42,7 +42,8 @@ class _FakeWeightUnit_0 extends _i1.SmartFake implements _i2.WeightUnit { ); } -class _FakeRepetitionUnit_1 extends _i1.SmartFake implements _i3.RepetitionUnit { +class _FakeRepetitionUnit_1 extends _i1.SmartFake + implements _i3.RepetitionUnit { _FakeRepetitionUnit_1( Object parent, Invocation parentInvocation, @@ -112,7 +113,8 @@ class _FakeSetting_7 extends _i1.SmartFake implements _i9.Setting { ); } -class _FakeWorkoutSession_8 extends _i1.SmartFake implements _i10.WorkoutSession { +class _FakeWorkoutSession_8 extends _i1.SmartFake + implements _i10.WorkoutSession { _FakeWorkoutSession_8( Object parent, Invocation parentInvocation, @@ -155,7 +157,8 @@ class _FakeResponse_11 extends _i1.SmartFake implements _i5.Response { /// A class which mocks [WorkoutPlansProvider]. /// /// See the documentation for Mockito's code generation for more information. -class MockWorkoutPlansProvider extends _i1.Mock implements _i12.WorkoutPlansProvider { +class MockWorkoutPlansProvider extends _i1.Mock + implements _i12.WorkoutPlansProvider { MockWorkoutPlansProvider() { _i1.throwOnMissingStub(this); } @@ -301,7 +304,8 @@ class MockWorkoutPlansProvider extends _i1.Mock implements _i12.WorkoutPlansProv returnValueForMissingStub: _i13.Future.value(), ) as _i13.Future); @override - _i13.Future<_i6.WorkoutPlan> fetchAndSetPlanSparse(int? planId) => (super.noSuchMethod( + _i13.Future<_i6.WorkoutPlan> fetchAndSetPlanSparse(int? planId) => + (super.noSuchMethod( Invocation.method( #fetchAndSetPlanSparse, [planId], @@ -315,7 +319,8 @@ class MockWorkoutPlansProvider extends _i1.Mock implements _i12.WorkoutPlansProv )), ) as _i13.Future<_i6.WorkoutPlan>); @override - _i13.Future<_i6.WorkoutPlan> fetchAndSetWorkoutPlanFull(int? workoutId) => (super.noSuchMethod( + _i13.Future<_i6.WorkoutPlan> fetchAndSetWorkoutPlanFull(int? workoutId) => + (super.noSuchMethod( Invocation.method( #fetchAndSetWorkoutPlanFull, [workoutId], @@ -329,7 +334,8 @@ class MockWorkoutPlansProvider extends _i1.Mock implements _i12.WorkoutPlansProv )), ) as _i13.Future<_i6.WorkoutPlan>); @override - _i13.Future<_i6.WorkoutPlan> addWorkout(_i6.WorkoutPlan? workout) => (super.noSuchMethod( + _i13.Future<_i6.WorkoutPlan> addWorkout(_i6.WorkoutPlan? workout) => + (super.noSuchMethod( Invocation.method( #addWorkout, [workout], @@ -343,7 +349,8 @@ class MockWorkoutPlansProvider extends _i1.Mock implements _i12.WorkoutPlansProv )), ) as _i13.Future<_i6.WorkoutPlan>); @override - _i13.Future editWorkout(_i6.WorkoutPlan? workout) => (super.noSuchMethod( + _i13.Future editWorkout(_i6.WorkoutPlan? workout) => + (super.noSuchMethod( Invocation.method( #editWorkout, [workout], @@ -373,7 +380,8 @@ class MockWorkoutPlansProvider extends _i1.Mock implements _i12.WorkoutPlansProv base, ], ), - returnValue: _i13.Future>.value({}), + returnValue: + _i13.Future>.value({}), ) as _i13.Future>); @override _i13.Future fetchAndSetRepetitionUnits() => (super.noSuchMethod( @@ -483,7 +491,8 @@ class MockWorkoutPlansProvider extends _i1.Mock implements _i12.WorkoutPlansProv returnValue: _i13.Future>.value(<_i8.Set>[]), ) as _i13.Future>); @override - _i13.Future fetchComputedSettings(_i8.Set? workoutSet) => (super.noSuchMethod( + _i13.Future fetchComputedSettings(_i8.Set? workoutSet) => + (super.noSuchMethod( Invocation.method( #fetchComputedSettings, [workoutSet], @@ -516,7 +525,8 @@ class MockWorkoutPlansProvider extends _i1.Mock implements _i12.WorkoutPlansProv returnValueForMissingStub: _i13.Future.value(), ) as _i13.Future); @override - _i13.Future<_i9.Setting> addSetting(_i9.Setting? workoutSetting) => (super.noSuchMethod( + _i13.Future<_i9.Setting> addSetting(_i9.Setting? workoutSetting) => + (super.noSuchMethod( Invocation.method( #addSetting, [workoutSetting], @@ -538,12 +548,14 @@ class MockWorkoutPlansProvider extends _i1.Mock implements _i12.WorkoutPlansProv returnValue: _i13.Future.value(), ) as _i13.Future); @override - _i13.Future<_i10.WorkoutSession> addSession(_i10.WorkoutSession? session) => (super.noSuchMethod( + _i13.Future<_i10.WorkoutSession> addSession(_i10.WorkoutSession? session) => + (super.noSuchMethod( Invocation.method( #addSession, [session], ), - returnValue: _i13.Future<_i10.WorkoutSession>.value(_FakeWorkoutSession_8( + returnValue: + _i13.Future<_i10.WorkoutSession>.value(_FakeWorkoutSession_8( this, Invocation.method( #addSession, @@ -575,7 +587,8 @@ class MockWorkoutPlansProvider extends _i1.Mock implements _i12.WorkoutPlansProv returnValueForMissingStub: _i13.Future.value(), ) as _i13.Future); @override - Map getDefaultHeaders({dynamic includeAuth = false}) => (super.noSuchMethod( + Map getDefaultHeaders({dynamic includeAuth = false}) => + (super.noSuchMethod( Invocation.method( #getDefaultHeaders, [], @@ -619,9 +632,18 @@ class MockWorkoutPlansProvider extends _i1.Mock implements _i12.WorkoutPlansProv #fetch, [uri], ), - returnValue: _i13.Future>.value({}), + returnValue: + _i13.Future>.value({}), ) as _i13.Future>); @override + _i13.Future> fetchPaginated(Uri? uri) => (super.noSuchMethod( + Invocation.method( + #fetchPaginated, + [uri], + ), + returnValue: _i13.Future>.value([]), + ) as _i13.Future>); + @override _i13.Future> post( Map? data, Uri? uri, @@ -634,7 +656,8 @@ class MockWorkoutPlansProvider extends _i1.Mock implements _i12.WorkoutPlansProv uri, ], ), - returnValue: _i13.Future>.value({}), + returnValue: + _i13.Future>.value({}), ) as _i13.Future>); @override _i13.Future> patch( @@ -649,7 +672,8 @@ class MockWorkoutPlansProvider extends _i1.Mock implements _i12.WorkoutPlansProv uri, ], ), - returnValue: _i13.Future>.value({}), + returnValue: + _i13.Future>.value({}), ) as _i13.Future>); @override _i13.Future<_i5.Response> deleteRequest( diff --git a/test/workout/workout_set_form_test.mocks.dart b/test/workout/workout_set_form_test.mocks.dart index a87bd4e3..c66ac24b 100644 --- a/test/workout/workout_set_form_test.mocks.dart +++ b/test/workout/workout_set_form_test.mocks.dart @@ -26,7 +26,8 @@ import 'package:wger/providers/exercises.dart' as _i8; // ignore_for_file: camel_case_types // ignore_for_file: subtype_of_sealed_class -class _FakeWgerBaseProvider_0 extends _i1.SmartFake implements _i2.WgerBaseProvider { +class _FakeWgerBaseProvider_0 extends _i1.SmartFake + implements _i2.WgerBaseProvider { _FakeWgerBaseProvider_0( Object parent, Invocation parentInvocation, @@ -46,7 +47,8 @@ class _FakeExerciseBase_1 extends _i1.SmartFake implements _i3.ExerciseBase { ); } -class _FakeExerciseCategory_2 extends _i1.SmartFake implements _i4.ExerciseCategory { +class _FakeExerciseCategory_2 extends _i1.SmartFake + implements _i4.ExerciseCategory { _FakeExerciseCategory_2( Object parent, Invocation parentInvocation, @@ -103,7 +105,8 @@ class MockExercisesProvider extends _i1.Mock implements _i8.ExercisesProvider { ), ) as _i2.WgerBaseProvider); @override - set exerciseBases(List<_i3.ExerciseBase>? exercisesBases) => super.noSuchMethod( + set exerciseBases(List<_i3.ExerciseBase>? exercisesBases) => + super.noSuchMethod( Invocation.setter( #exerciseBases, exercisesBases, @@ -116,7 +119,8 @@ class MockExercisesProvider extends _i1.Mock implements _i8.ExercisesProvider { returnValue: <_i3.ExerciseBase>[], ) as List<_i3.ExerciseBase>); @override - set filteredExerciseBases(List<_i3.ExerciseBase>? newFilteredExercises) => super.noSuchMethod( + set filteredExerciseBases(List<_i3.ExerciseBase>? newFilteredExercises) => + super.noSuchMethod( Invocation.setter( #filteredExerciseBases, newFilteredExercises, @@ -124,7 +128,8 @@ class MockExercisesProvider extends _i1.Mock implements _i8.ExercisesProvider { returnValueForMissingStub: null, ); @override - Map> get exerciseBasesByVariation => (super.noSuchMethod( + Map> get exerciseBasesByVariation => + (super.noSuchMethod( Invocation.getter(#exerciseBasesByVariation), returnValue: >{}, ) as Map>); @@ -321,7 +326,8 @@ class MockExercisesProvider extends _i1.Mock implements _i8.ExercisesProvider { returnValueForMissingStub: _i9.Future.value(), ) as _i9.Future); @override - _i9.Future<_i3.ExerciseBase> fetchAndSetExerciseBase(int? exerciseBaseId) => (super.noSuchMethod( + _i9.Future<_i3.ExerciseBase> fetchAndSetExerciseBase(int? exerciseBaseId) => + (super.noSuchMethod( Invocation.method( #fetchAndSetExerciseBase, [exerciseBaseId], @@ -335,7 +341,8 @@ class MockExercisesProvider extends _i1.Mock implements _i8.ExercisesProvider { )), ) as _i9.Future<_i3.ExerciseBase>); @override - _i3.ExerciseBase readExerciseBaseFromBaseInfo(dynamic baseData) => (super.noSuchMethod( + _i3.ExerciseBase readExerciseBaseFromBaseInfo(dynamic baseData) => + (super.noSuchMethod( Invocation.method( #readExerciseBaseFromBaseInfo, [baseData], @@ -379,7 +386,8 @@ class MockExercisesProvider extends _i1.Mock implements _i8.ExercisesProvider { languageCode, ], ), - returnValue: _i9.Future>.value(<_i3.ExerciseBase>[]), + returnValue: + _i9.Future>.value(<_i3.ExerciseBase>[]), ) as _i9.Future>); @override void addListener(_i10.VoidCallback? listener) => super.noSuchMethod(