From e62b03e8a892afd8939eb3943bdb0c81faa82faa Mon Sep 17 00:00:00 2001 From: Roland Geider Date: Mon, 13 Sep 2021 20:52:08 +0200 Subject: [PATCH] Further work on reworking the exercise provider --- lib/models/exercises/base.dart | 5 +- lib/models/exercises/exercise.dart | 76 +++----- lib/models/exercises/exercise.g.dart | 39 +--- lib/models/exercises/exercise2.dart | 90 --------- lib/models/exercises/exercise2.g.dart | 38 ---- lib/providers/exercises.dart | 177 ++++++++++++------ lib/screens/dashboard.dart | 2 +- lib/screens/home_tabs_screen.dart | 1 - lib/widgets/exercises/exercises.dart | 2 +- lib/widgets/exercises/list_tile.dart | 2 +- lib/widgets/workouts/forms.dart | 4 +- lib/widgets/workouts/gym_mode.dart | 2 +- .../exercises_detail_widget_test.dart | 2 +- test/gym_mode_screen_test.dart | 17 +- test/gym_mode_screen_test.mocks.dart | 156 +++++++++++++++ test/workout_plan_model_test.dart | 6 +- test/workout_set_form_test.mocks.dart | 76 +++++--- test_data/exercises.dart | 96 ++++++---- test_data/workouts.dart | 10 +- 19 files changed, 450 insertions(+), 351 deletions(-) delete mode 100644 lib/models/exercises/exercise2.dart delete mode 100644 lib/models/exercises/exercise2.g.dart create mode 100644 test/gym_mode_screen_test.mocks.dart diff --git a/lib/models/exercises/base.dart b/lib/models/exercises/base.dart index 27cf2ab9..bb9d3f1f 100644 --- a/lib/models/exercises/base.dart +++ b/lib/models/exercises/base.dart @@ -20,11 +20,10 @@ import 'package:json_annotation/json_annotation.dart'; import 'package:wger/models/exercises/category.dart'; import 'package:wger/models/exercises/comment.dart'; import 'package:wger/models/exercises/equipment.dart'; +import 'package:wger/models/exercises/exercise.dart'; import 'package:wger/models/exercises/image.dart'; import 'package:wger/models/exercises/muscle.dart'; -import 'exercise2.dart'; - part 'base.g.dart'; @JsonSerializable(explicitToJson: true) @@ -69,7 +68,7 @@ class ExerciseBase { List images = []; @JsonKey(ignore: true) - List exercises = []; + List exercises = []; ExerciseBase( {required this.id, diff --git a/lib/models/exercises/exercise.dart b/lib/models/exercises/exercise.dart index 640f5868..76b99de9 100644 --- a/lib/models/exercises/exercise.dart +++ b/lib/models/exercises/exercise.dart @@ -17,22 +17,33 @@ */ import 'package:json_annotation/json_annotation.dart'; +import 'package:wger/models/exercises/base.dart'; import 'package:wger/models/exercises/category.dart'; import 'package:wger/models/exercises/comment.dart'; import 'package:wger/models/exercises/equipment.dart'; import 'package:wger/models/exercises/image.dart'; +import 'package:wger/models/exercises/language.dart'; import 'package:wger/models/exercises/muscle.dart'; part 'exercise.g.dart'; -@JsonSerializable(explicitToJson: true) +@JsonSerializable() class Exercise { @JsonKey(required: true) final int id; + @JsonKey(required: true, name: 'exercise_base') + final int baseId; + @JsonKey(required: true) final String uuid; + @JsonKey(required: true, name: 'language') + final int languageId; + + @JsonKey(ignore: true) + late Language language; + @JsonKey(required: true, name: 'creation_date') final DateTime creationDate; @@ -42,62 +53,37 @@ class Exercise { @JsonKey(required: true) final String description; - @JsonKey(required: false, ignore: true) - late int categoryId; + @JsonKey(ignore: true) + late ExerciseBase base; - @JsonKey(required: true, name: 'category') - late final ExerciseCategory categoryObj; - - @JsonKey(required: true) - List muscles = []; - - @JsonKey(required: true, name: 'muscles_secondary') - List musclesSecondary = []; - - @JsonKey(required: true) - List equipment = []; - - @JsonKey(required: true) - List images = []; - - @JsonKey(required: true, name: 'comments') + @JsonKey(ignore: true) List tips = []; Exercise( {required this.id, + required this.baseId, required this.uuid, required this.creationDate, + required this.languageId, required this.name, required this.description, - List? muscles, - List? musclesSecondary, - List? equipment, - List? images, - List? tips, - ExerciseCategory? category}) { - this.tips = tips ?? []; - this.images = images ?? []; - this.equipment = equipment ?? []; - this.musclesSecondary = musclesSecondary ?? []; - this.muscles = muscles ?? []; - if (category != null) { - this.categoryObj = category; - this.categoryId = category.id; + base, + language}) { + if (base != null) { + this.base = base; + } + + if (language != null) { + this.language = language; } } - ExerciseImage? get getMainImage { - try { - return images.firstWhere((image) => image.isMain); - } on StateError catch (e) { - return null; - } - } - - set category(ExerciseCategory category) { - this.categoryId = category.id; - this.categoryObj = category; - } + ExerciseImage? get getMainImage => base.getMainImage; + ExerciseCategory get category => base.category; + List get images => base.images; + List get equipment => base.equipment; + List get muscles => base.muscles; + List get musclesSecondary => base.musclesSecondary; // Boilerplate factory Exercise.fromJson(Map json) => _$ExerciseFromJson(json); diff --git a/lib/models/exercises/exercise.g.dart b/lib/models/exercises/exercise.g.dart index 6b2724e4..e81b7ffb 100644 --- a/lib/models/exercises/exercise.g.dart +++ b/lib/models/exercises/exercise.g.dart @@ -9,53 +9,30 @@ part of 'exercise.dart'; Exercise _$ExerciseFromJson(Map json) { $checkKeys(json, requiredKeys: const [ 'id', + 'exercise_base', 'uuid', + 'language', 'creation_date', 'name', - 'description', - 'category', - 'muscles', - 'muscles_secondary', - 'equipment', - 'images', - 'comments' + 'description' ]); return Exercise( id: json['id'] as int, + baseId: json['exercise_base'] as int, uuid: json['uuid'] as String, creationDate: DateTime.parse(json['creation_date'] as String), + languageId: json['language'] as int, name: json['name'] as String, description: json['description'] as String, - muscles: (json['muscles'] as List?) - ?.map((e) => Muscle.fromJson(e as Map)) - .toList(), - musclesSecondary: (json['muscles_secondary'] as List?) - ?.map((e) => Muscle.fromJson(e as Map)) - .toList(), - equipment: (json['equipment'] as List?) - ?.map((e) => Equipment.fromJson(e as Map)) - .toList(), - images: (json['images'] as List?) - ?.map((e) => ExerciseImage.fromJson(e as Map)) - .toList(), - tips: (json['comments'] as List?) - ?.map((e) => Comment.fromJson(e as Map)) - .toList(), - )..categoryObj = - ExerciseCategory.fromJson(json['category'] as Map); + ); } Map _$ExerciseToJson(Exercise instance) => { 'id': instance.id, + 'exercise_base': instance.baseId, 'uuid': instance.uuid, + 'language': instance.languageId, 'creation_date': instance.creationDate.toIso8601String(), 'name': instance.name, 'description': instance.description, - 'category': instance.categoryObj.toJson(), - 'muscles': instance.muscles.map((e) => e.toJson()).toList(), - 'muscles_secondary': - instance.musclesSecondary.map((e) => e.toJson()).toList(), - 'equipment': instance.equipment.map((e) => e.toJson()).toList(), - 'images': instance.images.map((e) => e.toJson()).toList(), - 'comments': instance.tips.map((e) => e.toJson()).toList(), }; diff --git a/lib/models/exercises/exercise2.dart b/lib/models/exercises/exercise2.dart deleted file mode 100644 index a9525da6..00000000 --- a/lib/models/exercises/exercise2.dart +++ /dev/null @@ -1,90 +0,0 @@ -/* - * This file is part of wger Workout Manager . - * Copyright (C) 2020, 2021 wger Team - * - * wger Workout Manager is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -import 'package:json_annotation/json_annotation.dart'; -import 'package:wger/models/exercises/base.dart'; -import 'package:wger/models/exercises/category.dart'; -import 'package:wger/models/exercises/comment.dart'; -import 'package:wger/models/exercises/equipment.dart'; -import 'package:wger/models/exercises/image.dart'; -import 'package:wger/models/exercises/language.dart'; -import 'package:wger/models/exercises/muscle.dart'; - -part 'exercise2.g.dart'; - -@JsonSerializable() -class Exercise2 { - @JsonKey(required: true) - final int id; - - @JsonKey(required: true, name: 'exercise_base') - final int baseId; - - @JsonKey(required: true) - final String uuid; - - @JsonKey(required: true, name: 'language') - final int languageId; - - @JsonKey(ignore: true) - late Language language; - - @JsonKey(required: true, name: 'creation_date') - final DateTime creationDate; - - @JsonKey(required: true) - final String name; - - @JsonKey(required: true) - final String description; - - @JsonKey(ignore: true) - late ExerciseBase base; - - @JsonKey(ignore: true) - List tips = []; - - Exercise2( - {required this.id, - required this.baseId, - required this.uuid, - required this.creationDate, - required this.languageId, - required this.name, - required this.description, - base, - language}) { - if (base != null) { - this.base = base; - } - - if (language != null) { - this.language = language; - } - } - - ExerciseImage? get getMainImage => base.getMainImage; - ExerciseCategory get category => base.category; - List get equipment => base.equipment; - List get muscles => base.muscles; - List get musclesSecondary => base.musclesSecondary; - - // Boilerplate - factory Exercise2.fromJson(Map json) => _$Exercise2FromJson(json); - Map toJson() => _$Exercise2ToJson(this); -} diff --git a/lib/models/exercises/exercise2.g.dart b/lib/models/exercises/exercise2.g.dart deleted file mode 100644 index 7a4add8a..00000000 --- a/lib/models/exercises/exercise2.g.dart +++ /dev/null @@ -1,38 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'exercise2.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -Exercise2 _$Exercise2FromJson(Map json) { - $checkKeys(json, requiredKeys: const [ - 'id', - 'exercise_base', - 'uuid', - 'language', - 'creation_date', - 'name', - 'description' - ]); - return Exercise2( - id: json['id'] as int, - baseId: json['exercise_base'] as int, - uuid: json['uuid'] as String, - creationDate: DateTime.parse(json['creation_date'] as String), - languageId: json['language'] as int, - name: json['name'] as String, - description: json['description'] as String, - ); -} - -Map _$Exercise2ToJson(Exercise2 instance) => { - 'id': instance.id, - 'exercise_base': instance.baseId, - 'uuid': instance.uuid, - 'language': instance.languageId, - 'creation_date': instance.creationDate.toIso8601String(), - 'name': instance.name, - 'description': instance.description, - }; diff --git a/lib/providers/exercises.dart b/lib/providers/exercises.dart index 337a283e..9f2a7645 100644 --- a/lib/providers/exercises.dart +++ b/lib/providers/exercises.dart @@ -21,6 +21,7 @@ import 'dart:convert'; import 'dart:developer'; import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; import 'package:json_annotation/json_annotation.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:wger/exceptions/no_such_entry_exception.dart'; @@ -29,7 +30,6 @@ import 'package:wger/models/exercises/base.dart'; import 'package:wger/models/exercises/category.dart'; import 'package:wger/models/exercises/equipment.dart'; import 'package:wger/models/exercises/exercise.dart'; -import 'package:wger/models/exercises/exercise2.dart'; import 'package:wger/models/exercises/language.dart'; import 'package:wger/models/exercises/muscle.dart'; import 'package:wger/providers/base_provider.dart'; @@ -37,7 +37,7 @@ import 'package:wger/providers/base_provider.dart'; class ExercisesProvider with ChangeNotifier { final WgerBaseProvider baseProvider; - static const daysToCache = 7; + static const EXERCISE_CACHE_DAYS = 7; static const _exerciseInfoUrlPath = 'exerciseinfo'; static const _exerciseBaseUrlPath = 'exercise-base'; @@ -51,6 +51,7 @@ class ExercisesProvider with ChangeNotifier { static const _equipmentUrlPath = 'equipment'; static const _languageUrlPath = 'language'; + List _exerciseBases = []; List _exercises = []; List _categories = []; List _muscles = []; @@ -97,13 +98,22 @@ class ExercisesProvider with ChangeNotifier { // Filter by exercise category and equipment (REPLACE WITH HTTP REQUEST) return items - .where((exercise) => filters!.exerciseCategories.selected.contains(exercise.categoryObj)) + .where((exercise) => filters!.exerciseCategories.selected.contains(exercise.category)) .toList(); } + /// Clears all lists + clear() { + _equipment = []; + _muscles = []; + _categories = []; + _languages = []; + _exerciseBases = []; + } + List findByCategory(ExerciseCategory? category) { if (category == null) return this.items; - return this.items.where((exercise) => exercise.categoryObj == category).toList(); + return this.items.where((exercise) => exercise.category == category).toList(); } /// Find exercise by ID @@ -198,70 +208,90 @@ class ExercisesProvider with ChangeNotifier { Future fetchAndSetExercise(int exerciseId) async { try { return findExerciseById(exerciseId); - } on StateError { + } on NoSuchEntryException { // Get exercise from the server and save to cache - final data = - await baseProvider.fetch(baseProvider.makeUrl(_exerciseInfoUrlPath, id: exerciseId)); - final exercise = Exercise.fromJson(data); - _exercises.add(exercise); + // TODO: do this right (and save to cache) + final exerciseTranslationData = await baseProvider.fetch( + baseProvider.makeUrl( + _exerciseUrlPath, + id: exerciseId, + ), + ); + final exercise = Exercise.fromJson(exerciseTranslationData); + + final exerciseBaseData = await baseProvider.fetch( + baseProvider.makeUrl(_exerciseBaseUrlPath, id: exercise.baseId), + ); + final base = ExerciseBase.fromJson(exerciseBaseData); + setExerciseBaseData(base, [exercise]); + + /* final prefs = await SharedPreferences.getInstance(); - final exerciseData = json.decode(prefs.getString(PREFS_EXERCISES)!); - exerciseData['exercises'].add(exercise.toJson()); - prefs.setString(PREFS_EXERCISES, json.encode(exerciseData)); - log("Saved exercise '${exercise.name}' to cache."); - return exercise; - } - } - - Future fetchAndSetExercisesTEST() async { - // Load categories, muscles, equipment and languages - await Future.wait([ - fetchAndSetCategories(), - fetchAndSetMuscles(), - fetchAndSetEquipment(), - fetchAndSetLanguages(), - ]); - - final exercisesData = await baseProvider.fetch( - baseProvider.makeUrl(_exerciseBaseUrlPath, query: {'limit': '10'}), - ); - - exercisesData['results'].forEach((e) async { - var base = ExerciseBase.fromJson(e); - - base.category = findCategoryById(base.categoryId); - base.muscles = base.musclesIds.map((e) => findMuscleById(e)).toList(); - base.musclesSecondary = base.musclesSecondaryIds.map((e) => findMuscleById(e)).toList(); - base.equipment = base.equipmentIds.map((e) => findEquipmentById(e)).toList(); final exerciseTranslationData = await baseProvider.fetch( baseProvider.makeUrl( _exerciseUrlPath, - query: {'limit': '10', 'exercise_base': base.id.toString()}, + id: exerciseId, ), ); - exerciseTranslationData['results'].forEach((e) async { - var exercise = Exercise2.fromJson(e); - exercise.base = base; - exercise.language = findLanguageById(exercise.languageId); - base.exercises.add(exercise); - }); + final exercise = Exercise.fromJson(exerciseTranslationData); + final exerciseBaseData = await baseProvider.fetch( + baseProvider.makeUrl(_exerciseBaseUrlPath, id: exercise.baseId), + ); + + final base = setExerciseBaseData(ExerciseBase.fromJson(exerciseBaseData), [exercise]); + + //exerciseData['exercises'].add(exercise.toJson()); + //prefs.setString(PREFS_EXERCISES, json.encode(exerciseData)); + //log("Saved exercise '${exercise.name}' to cache."); + + */ + return exercise; + } + } + + /// Helper function that sets different objects such as category, etc. + ExerciseBase setExerciseBaseData(ExerciseBase base, List exercises) { + base.category = findCategoryById(base.categoryId); + base.muscles = base.musclesIds.map((e) => findMuscleById(e)).toList(); + base.musclesSecondary = base.musclesSecondaryIds.map((e) => findMuscleById(e)).toList(); + base.equipment = base.equipmentIds.map((e) => findEquipmentById(e)).toList(); + + exercises.forEach((e) { + e.base = base; + e.language = findLanguageById(e.languageId); }); + base.exercises = []; + base.exercises = exercises; + + return base; } Future fetchAndSetExercises() async { - // Load exercises from cache, if available + this.clear(); + print(Intl.getCurrentLocale()); + print(Intl.shortLocale(Intl.getCurrentLocale())); + print('---------'); final prefs = await SharedPreferences.getInstance(); + if (prefs.containsKey(PREFS_EXERCISES)) { - final exerciseData = json.decode(prefs.getString(PREFS_EXERCISES)!); - if (DateTime.parse(exerciseData['expiresIn']).isAfter(DateTime.now())) { - exerciseData['exercises'].forEach((e) => _exercises.add(Exercise.fromJson(e))); - exerciseData['equipment'].forEach((e) => _equipment.add(Equipment.fromJson(e))); - exerciseData['muscles'].forEach((e) => _muscles.add(Muscle.fromJson(e))); - exerciseData['categories'].forEach((e) => _categories.add(ExerciseCategory.fromJson(e))); - log("Read ${exerciseData['exercises'].length} exercises from cache. Valid till ${exerciseData['expiresIn']}"); + final cacheData = json.decode(prefs.getString(PREFS_EXERCISES)!); + if (DateTime.parse(cacheData['expiresIn']).isAfter(DateTime.now())) { + cacheData['equipment'].forEach((e) => _equipment.add(Equipment.fromJson(e))); + cacheData['muscles'].forEach((e) => _muscles.add(Muscle.fromJson(e))); + cacheData['categories'].forEach((e) => _categories.add(ExerciseCategory.fromJson(e))); + cacheData['languages'].forEach((e) => _languages.add(Language.fromJson(e))); + cacheData['exercise-translations'].forEach((e) => _exercises.add(Exercise.fromJson(e))); + cacheData['bases'].forEach((e) { + var base = setExerciseBaseData( + ExerciseBase.fromJson(e), + _exercises.where((element) => element.baseId == e['id']).toList(), + ); + _exerciseBases.add(base); + }); + log("Read ${_exerciseBases.length} exercises from cache. Valid till ${cacheData['expiresIn']}"); return; } } @@ -274,26 +304,49 @@ class ExercisesProvider with ChangeNotifier { fetchAndSetLanguages(), ]); - final exercisesData = await baseProvider.fetch( - baseProvider.makeUrl(_exerciseInfoUrlPath, query: {'limit': '1000'}), + final exerciseBaseData = await baseProvider.fetch( + baseProvider.makeUrl(_exerciseBaseUrlPath, query: {'limit': '1000'}), + ); + final exerciseTranslationData = await baseProvider.fetch( + baseProvider.makeUrl( + _exerciseUrlPath, + query: {'limit': '1000'}, + ), ); + List exerciseTranslation = exerciseTranslationData['results'].map((e) { + return Exercise.fromJson(e); + }).toList(); + + for (var e in exerciseBaseData['results']) { + var base = setExerciseBaseData( + ExerciseBase.fromJson(e), + exerciseTranslation.where((element) => element.baseId == e['id']).toList(), + ); + _exerciseBases.add(base); + } + try { - // Load exercises - exercisesData['results'].forEach((e) => _exercises.add(Exercise.fromJson(e))); + List exerciseTranslations = []; + _exerciseBases.forEach((base) { + base.exercises.forEach((exercise) { + exerciseTranslations.add(exercise); + }); + }); // Save the result to the cache - final exerciseData = { + final cacheData = { 'date': DateTime.now().toIso8601String(), - 'expiresIn': DateTime.now().add(Duration(days: daysToCache)).toIso8601String(), - 'exercises': _exercises.map((e) => e.toJson()).toList(), + 'expiresIn': DateTime.now().add(Duration(days: EXERCISE_CACHE_DAYS)).toIso8601String(), 'equipment': _equipment.map((e) => e.toJson()).toList(), 'categories': _categories.map((e) => e.toJson()).toList(), 'muscles': _muscles.map((e) => e.toJson()).toList(), + 'languages': _languages.map((e) => e.toJson()).toList(), + 'exercise-translations': exerciseTranslations.map((e) => e.toJson()).toList(), + 'bases': _exerciseBases.map((e) => e.toJson()).toList(), }; - log("Saved ${_exercises.length} exercises from cache. Valid till ${exerciseData['expiresIn']}"); - - prefs.setString(PREFS_EXERCISES, json.encode(exerciseData)); + log("Saved ${_exerciseBases.length} exercises to cache. Valid till ${cacheData['expiresIn']}"); + prefs.setString(PREFS_EXERCISES, json.encode(cacheData)); notifyListeners(); } on MissingRequiredKeysException catch (error) { log(error.missingKeys.toString()); diff --git a/lib/screens/dashboard.dart b/lib/screens/dashboard.dart index 15313d80..4e108c31 100644 --- a/lib/screens/dashboard.dart +++ b/lib/screens/dashboard.dart @@ -42,7 +42,7 @@ class _DashboardScreenState extends State { children: [ IconButton( onPressed: () { - Provider.of(context, listen: false).fetchAndSetExercisesTEST(); + Provider.of(context, listen: false).fetchAndSetExercises(); }, icon: Icon(Icons.update), ), diff --git a/lib/screens/home_tabs_screen.dart b/lib/screens/home_tabs_screen.dart index 19b9bad0..1c58dc0b 100644 --- a/lib/screens/home_tabs_screen.dart +++ b/lib/screens/home_tabs_screen.dart @@ -32,7 +32,6 @@ import 'package:wger/providers/workout_plans.dart'; import 'package:wger/screens/dashboard.dart'; import 'package:wger/screens/exercises_screen.dart'; import 'package:wger/screens/gallery_screen.dart'; -import 'package:wger/screens/nutritional_plans_screen.dart'; import 'package:wger/screens/weight_screen.dart'; import 'package:wger/screens/workout_plans_screen.dart'; import 'package:wger/theme/theme.dart'; diff --git a/lib/widgets/exercises/exercises.dart b/lib/widgets/exercises/exercises.dart index aa31d8b3..204c64df 100644 --- a/lib/widgets/exercises/exercises.dart +++ b/lib/widgets/exercises/exercises.dart @@ -39,7 +39,7 @@ class ExerciseDetail extends StatelessWidget { AppLocalizations.of(context).category, style: Theme.of(context).textTheme.headline6, ), - Text(_exercise.categoryObj.name), + Text(_exercise.category.name), SizedBox(height: 8), // Equipment diff --git a/lib/widgets/exercises/list_tile.dart b/lib/widgets/exercises/list_tile.dart index 8ba43d57..5c7f23a3 100644 --- a/lib/widgets/exercises/list_tile.dart +++ b/lib/widgets/exercises/list_tile.dart @@ -38,7 +38,7 @@ class ExerciseListTile extends StatelessWidget { borderRadius: BorderRadius.circular(5), ), child: Text( - exercise.categoryObj.name, + exercise.category.name, ), ), Text( diff --git a/lib/widgets/workouts/forms.dart b/lib/widgets/workouts/forms.dart index 37c06d36..79094667 100644 --- a/lib/widgets/workouts/forms.dart +++ b/lib/widgets/workouts/forms.dart @@ -409,7 +409,7 @@ class _SetFormWidgetState extends State { ), title: Text(exercise.name), subtitle: Text( - '${exercise.categoryObj.name} / ${exercise.equipment.map((e) => e.name).join(', ')}'), + '${exercise.category.name} / ${exercise.equipment.map((e) => e.name).join(', ')}'), ); }, transitionBuilder: (context, suggestionsBox, controller) { @@ -639,7 +639,7 @@ class ExerciseSetting extends StatelessWidget { _exercise.name, style: Theme.of(context).textTheme.headline6, ), - subtitle: Text(_exercise.categoryObj.name), + subtitle: Text(_exercise.category.name), contentPadding: EdgeInsets.zero, leading: ExerciseImageWidget(image: _exercise.getMainImage), trailing: IconButton( diff --git a/lib/widgets/workouts/gym_mode.dart b/lib/widgets/workouts/gym_mode.dart index e771e8f9..71cef3d0 100644 --- a/lib/widgets/workouts/gym_mode.dart +++ b/lib/widgets/workouts/gym_mode.dart @@ -658,7 +658,7 @@ class ExerciseOverview extends StatelessWidget { padding: EdgeInsets.symmetric(horizontal: 15), children: [ Text( - _exercise.categoryObj.name, + _exercise.category.name, style: Theme.of(context).textTheme.headline6, textAlign: TextAlign.center, ), diff --git a/test/exercises/exercises_detail_widget_test.dart b/test/exercises/exercises_detail_widget_test.dart index c2f9ef5a..1caaf889 100644 --- a/test/exercises/exercises_detail_widget_test.dart +++ b/test/exercises/exercises_detail_widget_test.dart @@ -31,7 +31,7 @@ main() { supportedLocales: AppLocalizations.supportedLocales, navigatorKey: GlobalKey(), home: Scaffold( - body: ExerciseDetail(exercise1), + body: ExerciseDetail(getExercise()[0]), ), ); } diff --git a/test/gym_mode_screen_test.dart b/test/gym_mode_screen_test.dart index dc352217..8ac43c76 100644 --- a/test/gym_mode_screen_test.dart +++ b/test/gym_mode_screen_test.dart @@ -19,34 +19,45 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; import 'package:provider/provider.dart'; import 'package:wger/models/workouts/workout_plan.dart'; +import 'package:wger/providers/exercises.dart'; import 'package:wger/providers/workout_plans.dart'; import 'package:wger/screens/gym_mode.dart'; import 'package:wger/screens/workout_plan_screen.dart'; import 'package:wger/widgets/workouts/forms.dart'; import 'package:wger/widgets/workouts/gym_mode.dart'; +import '../test_data/exercises.dart'; import '../test_data/workouts.dart'; import 'base_provider_test.mocks.dart'; +import 'gym_mode_screen_test.mocks.dart'; import 'utils.dart'; +@GenerateMocks([ExercisesProvider]) void main() { Widget createHomeScreen({locale = 'en'}) { final key = GlobalKey(); final client = MockClient(); + final mockExerciseProvider = MockExercisesProvider(); WorkoutPlan workoutPlan = getWorkout(); + when(mockExerciseProvider.findExerciseById(1)).thenReturn(getExercise()[0]); + when(mockExerciseProvider.findExerciseById(2)).thenReturn(getExercise()[1]); + when(mockExerciseProvider.findExerciseById(3)).thenReturn(getExercise()[2]); + return ChangeNotifierProvider( create: (context) => WorkoutPlansProvider( testAuthProvider, - testExercisesProvider, + mockExerciseProvider, [workoutPlan], client, ), - child: ChangeNotifierProvider( - create: (context) => testExercisesProvider, + child: ChangeNotifierProvider( + create: (context) => mockExerciseProvider, child: MaterialApp( locale: Locale(locale), localizationsDelegates: AppLocalizations.localizationsDelegates, diff --git a/test/gym_mode_screen_test.mocks.dart b/test/gym_mode_screen_test.mocks.dart new file mode 100644 index 00000000..89755532 --- /dev/null +++ b/test/gym_mode_screen_test.mocks.dart @@ -0,0 +1,156 @@ +// Mocks generated by Mockito 5.0.15 from annotations +// in wger/test/gym_mode_screen_test.dart. +// Do not manually edit this file. + +import 'dart:async' as _i10; +import 'dart:ui' as _i11; + +import 'package:mockito/mockito.dart' as _i1; +import 'package:wger/models/exercises/base.dart' as _i8; +import 'package:wger/models/exercises/category.dart' as _i4; +import 'package:wger/models/exercises/equipment.dart' as _i5; +import 'package:wger/models/exercises/exercise.dart' as _i3; +import 'package:wger/models/exercises/language.dart' as _i7; +import 'package:wger/models/exercises/muscle.dart' as _i6; +import 'package:wger/providers/base_provider.dart' as _i2; +import 'package:wger/providers/exercises.dart' as _i9; + +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis + +class _FakeWgerBaseProvider_0 extends _i1.Fake implements _i2.WgerBaseProvider { +} + +class _FakeExercise_1 extends _i1.Fake implements _i3.Exercise {} + +class _FakeExerciseCategory_2 extends _i1.Fake implements _i4.ExerciseCategory { +} + +class _FakeEquipment_3 extends _i1.Fake implements _i5.Equipment {} + +class _FakeMuscle_4 extends _i1.Fake implements _i6.Muscle {} + +class _FakeLanguage_5 extends _i1.Fake implements _i7.Language {} + +class _FakeExerciseBase_6 extends _i1.Fake implements _i8.ExerciseBase {} + +/// A class which mocks [ExercisesProvider]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockExercisesProvider extends _i1.Mock implements _i9.ExercisesProvider { + MockExercisesProvider() { + _i1.throwOnMissingStub(this); + } + + @override + _i2.WgerBaseProvider get baseProvider => + (super.noSuchMethod(Invocation.getter(#baseProvider), + returnValue: _FakeWgerBaseProvider_0()) as _i2.WgerBaseProvider); + @override + List<_i3.Exercise> get items => (super.noSuchMethod(Invocation.getter(#items), + returnValue: <_i3.Exercise>[]) as List<_i3.Exercise>); + @override + List<_i4.ExerciseCategory> get categories => + (super.noSuchMethod(Invocation.getter(#categories), + returnValue: <_i4.ExerciseCategory>[]) as List<_i4.ExerciseCategory>); + @override + bool get hasListeners => + (super.noSuchMethod(Invocation.getter(#hasListeners), returnValue: false) + as bool); + @override + void initFilters() => super.noSuchMethod(Invocation.method(#initFilters, []), + returnValueForMissingStub: null); + @override + List<_i3.Exercise> findByFilters() => + (super.noSuchMethod(Invocation.method(#findByFilters, []), + returnValue: <_i3.Exercise>[]) as List<_i3.Exercise>); + @override + List<_i3.Exercise> findByCategory(_i4.ExerciseCategory? category) => + (super.noSuchMethod(Invocation.method(#findByCategory, [category]), + returnValue: <_i3.Exercise>[]) as List<_i3.Exercise>); + @override + _i3.Exercise findExerciseById(int? id) => + (super.noSuchMethod(Invocation.method(#findExerciseById, [id]), + returnValue: _FakeExercise_1()) as _i3.Exercise); + @override + _i4.ExerciseCategory findCategoryById(int? id) => + (super.noSuchMethod(Invocation.method(#findCategoryById, [id]), + returnValue: _FakeExerciseCategory_2()) as _i4.ExerciseCategory); + @override + _i5.Equipment findEquipmentById(int? id) => + (super.noSuchMethod(Invocation.method(#findEquipmentById, [id]), + returnValue: _FakeEquipment_3()) as _i5.Equipment); + @override + _i6.Muscle findMuscleById(int? id) => + (super.noSuchMethod(Invocation.method(#findMuscleById, [id]), + returnValue: _FakeMuscle_4()) as _i6.Muscle); + @override + _i7.Language findLanguageById(int? id) => + (super.noSuchMethod(Invocation.method(#findLanguageById, [id]), + returnValue: _FakeLanguage_5()) as _i7.Language); + @override + _i10.Future fetchAndSetCategories() => (super.noSuchMethod( + Invocation.method(#fetchAndSetCategories, []), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i10.Future); + @override + _i10.Future fetchAndSetMuscles() => (super.noSuchMethod( + Invocation.method(#fetchAndSetMuscles, []), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i10.Future); + @override + _i10.Future fetchAndSetEquipment() => (super.noSuchMethod( + Invocation.method(#fetchAndSetEquipment, []), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i10.Future); + @override + _i10.Future fetchAndSetLanguages() => (super.noSuchMethod( + Invocation.method(#fetchAndSetLanguages, []), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i10.Future); + @override + _i10.Future<_i3.Exercise> fetchAndSetExercise(int? exerciseId) => + (super.noSuchMethod(Invocation.method(#fetchAndSetExercise, [exerciseId]), + returnValue: Future<_i3.Exercise>.value(_FakeExercise_1())) + as _i10.Future<_i3.Exercise>); + @override + _i8.ExerciseBase setExerciseBaseData( + _i8.ExerciseBase? base, List<_i3.Exercise>? exercises) => + (super.noSuchMethod( + Invocation.method(#setExerciseBaseData, [base, exercises]), + returnValue: _FakeExerciseBase_6()) as _i8.ExerciseBase); + @override + _i10.Future fetchAndSetExercises() => (super.noSuchMethod( + Invocation.method(#fetchAndSetExercises, []), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i10.Future); + @override + _i10.Future> searchExercise(String? name, + [String? languageCode = r'en']) => + (super.noSuchMethod( + Invocation.method(#searchExercise, [name, languageCode]), + returnValue: Future>.value([])) + as _i10.Future>); + @override + String toString() => super.toString(); + @override + void addListener(_i11.VoidCallback? listener) => + super.noSuchMethod(Invocation.method(#addListener, [listener]), + returnValueForMissingStub: null); + @override + void removeListener(_i11.VoidCallback? listener) => + super.noSuchMethod(Invocation.method(#removeListener, [listener]), + returnValueForMissingStub: null); + @override + void dispose() => super.noSuchMethod(Invocation.method(#dispose, []), + returnValueForMissingStub: null); + @override + void notifyListeners() => + super.noSuchMethod(Invocation.method(#notifyListeners, []), + returnValueForMissingStub: null); +} diff --git a/test/workout_plan_model_test.dart b/test/workout_plan_model_test.dart index b0250774..f74bce1c 100644 --- a/test/workout_plan_model_test.dart +++ b/test/workout_plan_model_test.dart @@ -27,16 +27,16 @@ void main() { final workout = getWorkout(); expect(workout.logs.length, 3); - final logExercise1 = workout.filterLogsByExercise(exercise1); + final logExercise1 = workout.filterLogsByExercise(getExercise()[0]); expect(logExercise1.length, 2); expect(logExercise1[0].id, 1); expect(logExercise1[1].id, 2); - final logExercise2 = workout.filterLogsByExercise(exercise2); + final logExercise2 = workout.filterLogsByExercise(getExercise()[1]); expect(logExercise2.length, 1); expect(logExercise2[0].id, 3); - expect(workout.filterLogsByExercise(exercise3).length, 0); + expect(workout.filterLogsByExercise(getExercise()[2]).length, 0); }); }); } diff --git a/test/workout_set_form_test.mocks.dart b/test/workout_set_form_test.mocks.dart index d1d3651c..e8dec906 100644 --- a/test/workout_set_form_test.mocks.dart +++ b/test/workout_set_form_test.mocks.dart @@ -2,16 +2,18 @@ // in wger/test/workout_set_form_test.dart. // Do not manually edit this file. -import 'dart:async' as _i8; -import 'dart:ui' as _i9; +import 'dart:async' as _i10; +import 'dart:ui' as _i11; import 'package:mockito/mockito.dart' as _i1; +import 'package:wger/models/exercises/base.dart' as _i8; import 'package:wger/models/exercises/category.dart' as _i4; import 'package:wger/models/exercises/equipment.dart' as _i5; import 'package:wger/models/exercises/exercise.dart' as _i3; +import 'package:wger/models/exercises/language.dart' as _i7; import 'package:wger/models/exercises/muscle.dart' as _i6; import 'package:wger/providers/base_provider.dart' as _i2; -import 'package:wger/providers/exercises.dart' as _i7; +import 'package:wger/providers/exercises.dart' as _i9; // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters @@ -33,10 +35,14 @@ class _FakeEquipment_3 extends _i1.Fake implements _i5.Equipment {} class _FakeMuscle_4 extends _i1.Fake implements _i6.Muscle {} +class _FakeLanguage_5 extends _i1.Fake implements _i7.Language {} + +class _FakeExerciseBase_6 extends _i1.Fake implements _i8.ExerciseBase {} + /// A class which mocks [ExercisesProvider]. /// /// See the documentation for Mockito's code generation for more information. -class MockExercisesProvider extends _i1.Mock implements _i7.ExercisesProvider { +class MockExercisesProvider extends _i1.Mock implements _i9.ExercisesProvider { MockExercisesProvider() { _i1.throwOnMissingStub(this); } @@ -84,50 +90,60 @@ class MockExercisesProvider extends _i1.Mock implements _i7.ExercisesProvider { (super.noSuchMethod(Invocation.method(#findMuscleById, [id]), returnValue: _FakeMuscle_4()) as _i6.Muscle); @override - _i8.Future fetchAndSetCategories() => - (super.noSuchMethod(Invocation.method(#fetchAndSetCategories, []), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i8.Future); + _i7.Language findLanguageById(int? id) => + (super.noSuchMethod(Invocation.method(#findLanguageById, [id]), + returnValue: _FakeLanguage_5()) as _i7.Language); @override - _i8.Future fetchAndSetMuscles() => - (super.noSuchMethod(Invocation.method(#fetchAndSetMuscles, []), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i8.Future); + _i10.Future fetchAndSetCategories() => (super.noSuchMethod( + Invocation.method(#fetchAndSetCategories, []), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i10.Future); @override - _i8.Future fetchAndSetEquipment() => - (super.noSuchMethod(Invocation.method(#fetchAndSetEquipment, []), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i8.Future); + _i10.Future fetchAndSetMuscles() => (super.noSuchMethod( + Invocation.method(#fetchAndSetMuscles, []), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i10.Future); @override - _i8.Future<_i3.Exercise> fetchAndSetExercise(int? exerciseId) => + _i10.Future fetchAndSetEquipment() => (super.noSuchMethod( + Invocation.method(#fetchAndSetEquipment, []), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i10.Future); + @override + _i10.Future fetchAndSetLanguages() => (super.noSuchMethod( + Invocation.method(#fetchAndSetLanguages, []), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i10.Future); + @override + _i10.Future<_i3.Exercise> fetchAndSetExercise(int? exerciseId) => (super.noSuchMethod(Invocation.method(#fetchAndSetExercise, [exerciseId]), returnValue: Future<_i3.Exercise>.value(_FakeExercise_1())) - as _i8.Future<_i3.Exercise>); + as _i10.Future<_i3.Exercise>); @override - _i8.Future fetchAndSetExercisesTEST() => - (super.noSuchMethod(Invocation.method(#fetchAndSetExercisesTEST, []), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i8.Future); + _i8.ExerciseBase setExerciseBaseData( + _i8.ExerciseBase? base, List<_i3.Exercise>? exercises) => + (super.noSuchMethod( + Invocation.method(#setExerciseBaseData, [base, exercises]), + returnValue: _FakeExerciseBase_6()) as _i8.ExerciseBase); @override - _i8.Future fetchAndSetExercises() => - (super.noSuchMethod(Invocation.method(#fetchAndSetExercises, []), - returnValue: Future.value(), - returnValueForMissingStub: Future.value()) as _i8.Future); + _i10.Future fetchAndSetExercises() => (super.noSuchMethod( + Invocation.method(#fetchAndSetExercises, []), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i10.Future); @override - _i8.Future> searchExercise(String? name, + _i10.Future> searchExercise(String? name, [String? languageCode = r'en']) => (super.noSuchMethod( Invocation.method(#searchExercise, [name, languageCode]), returnValue: Future>.value([])) - as _i8.Future>); + as _i10.Future>); @override String toString() => super.toString(); @override - void addListener(_i9.VoidCallback? listener) => + void addListener(_i11.VoidCallback? listener) => super.noSuchMethod(Invocation.method(#addListener, [listener]), returnValueForMissingStub: null); @override - void removeListener(_i9.VoidCallback? listener) => + void removeListener(_i11.VoidCallback? listener) => super.noSuchMethod(Invocation.method(#removeListener, [listener]), returnValueForMissingStub: null); @override diff --git a/test_data/exercises.dart b/test_data/exercises.dart index 57b77cef..1981b23c 100644 --- a/test_data/exercises.dart +++ b/test_data/exercises.dart @@ -16,6 +16,7 @@ * along with this program. If not, see . */ +import 'package:wger/models/exercises/base.dart'; import 'package:wger/models/exercises/category.dart'; import 'package:wger/models/exercises/equipment.dart'; import 'package:wger/models/exercises/exercise.dart'; @@ -32,38 +33,67 @@ const category3 = ExerciseCategory(id: 3, name: 'Abs'); const equipment1 = Equipment(id: 1, name: 'Bench'); const equipment2 = Equipment(id: 1, name: 'Dumbbell'); -final exercise1 = Exercise( - id: 1, - uuid: 'uuid', - creationDate: DateTime(2021, 1, 15), - name: 'test exercise 1', - description: 'add clever text', - category: category1, - muscles: [muscle1, muscle2], - musclesSecondary: [muscle3], - equipment: [equipment1, equipment2], -); +List getExercise() { + final base1 = ExerciseBase( + id: 1, + uuid: 'uuid1', + creationDate: DateTime(2021, 09, 01), + updateDate: DateTime(2021, 09, 10), + category: category1, + equipment: [equipment1, equipment2], + muscles: [muscle1, muscle2], + musclesSecondary: [muscle3], + ); -final exercise2 = Exercise( - id: 2, - uuid: '111-2222-44444', - creationDate: DateTime(2021, 1, 15), - name: 'test exercise 2', - description: 'Lorem ipsum etc', - category: category2, - muscles: [muscle1], - musclesSecondary: [muscle2], - equipment: [equipment2], -); + final exercise1 = Exercise( + id: 1, + baseId: 1, + uuid: 'uuid', + languageId: 1, + creationDate: DateTime(2021, 1, 15), + name: 'test exercise 1', + description: 'add clever text', + base: base1); -final exercise3 = Exercise( - id: 3, - uuid: 'a3b6c7bb-9d22-4119-a5fc-818584d5e9bc', - creationDate: DateTime(2021, 4, 1), - name: 'test exercise 3', - description: 'The man in black fled across the desert, and the gunslinger followed', - category: category3, - muscles: [muscle1], - musclesSecondary: [muscle2], - equipment: [equipment2], -); + final base2 = ExerciseBase( + id: 2, + uuid: 'uuid2', + creationDate: DateTime(2021, 08, 01), + updateDate: DateTime(2021, 08, 10), + category: category2, + equipment: [equipment2], + muscles: [muscle1], + musclesSecondary: [muscle2], + ); + final exercise2 = Exercise( + id: 2, + baseId: 2, + uuid: '111-2222-44444', + languageId: 2, + creationDate: DateTime(2021, 1, 15), + name: 'test exercise 2', + description: 'Lorem ipsum etc', + base: base2); + + final base3 = ExerciseBase( + id: 3, + uuid: 'uuid3', + creationDate: DateTime(2021, 08, 01), + updateDate: DateTime(2021, 08, 01), + category: category3, + equipment: [equipment2], + muscles: [muscle1], + musclesSecondary: [muscle2], + ); + final exercise3 = Exercise( + id: 3, + baseId: 2, + uuid: 'a3b6c7bb-9d22-4119-a5fc-818584d5e9bc', + languageId: 2, + creationDate: DateTime(2021, 4, 1), + name: 'test exercise 3', + description: 'The man in black fled across the desert, and the gunslinger followed', + base: base3); + + return [exercise1, exercise2, exercise3]; +} diff --git a/test_data/workouts.dart b/test_data/workouts.dart index f8877f14..ae67cb50 100644 --- a/test_data/workouts.dart +++ b/test_data/workouts.dart @@ -45,7 +45,7 @@ WorkoutPlan getWorkout() { ); setting1.repetitionUnit = repetitionUnit1; setting1.weightUnit = weightUnit1; - setting1.exercise = exercise1; + setting1.exercise = getExercise()[0]; setting1.weight = 10; var log1 = Log.empty() @@ -55,7 +55,7 @@ WorkoutPlan getWorkout() { ..date = DateTime(2021, 5, 1) ..reps = 10 ..workoutPlan = 1; - log1.exercise = exercise1; + log1.exercise = getExercise()[0]; log1.weightUnit = weightUnit1; log1.repetitionUnit = repetitionUnit1; @@ -66,7 +66,7 @@ WorkoutPlan getWorkout() { ..date = DateTime(2021, 5, 1) ..reps = 12 ..workoutPlan = 1; - log2.exercise = exercise1; + log2.exercise = getExercise()[0]; log2.weightUnit = weightUnit1; log2.repetitionUnit = repetitionUnit1; @@ -77,7 +77,7 @@ WorkoutPlan getWorkout() { ..date = DateTime(2021, 5, 2) ..reps = 8 ..workoutPlan = 1; - log3.exercise = exercise2; + log3.exercise = getExercise()[1]; log3.weightUnit = weightUnit1; log3.repetitionUnit = repetitionUnit1; @@ -88,7 +88,7 @@ WorkoutPlan getWorkout() { order: 1, comment: 'Important to do exercises correctly', ); - set1.addExercise(exercise1); + set1.addExercise(getExercise()[0]); set1.settings.add(setting1); set1.settingsComputed = [setting1, setting1];