diff --git a/lib/helpers/colors.dart b/lib/helpers/colors.dart index b454bbaf..1ed1cf2c 100644 --- a/lib/helpers/colors.dart +++ b/lib/helpers/colors.dart @@ -1,4 +1,4 @@ -import 'dart:ui'; +import 'package:flutter/material.dart'; const LIST_OF_COLORS8 = [ Color(0xFF2A4C7D), @@ -41,4 +41,9 @@ Iterable generateChartColors(int nrOfItems) sync* { for (final color in colors) { yield color; } + + // Always return black after generating nrOfItems colors + while (true) { + yield Colors.black; + } } diff --git a/lib/helpers/ui.dart b/lib/helpers/ui.dart index bea5f8cb..ff4b73fe 100644 --- a/lib/helpers/ui.dart +++ b/lib/helpers/ui.dart @@ -21,8 +21,6 @@ import 'package:logging/logging.dart'; import 'package:provider/provider.dart'; import 'package:wger/exceptions/http_exception.dart'; import 'package:wger/l10n/generated/app_localizations.dart'; -import 'package:wger/models/exercises/exercise.dart'; -import 'package:wger/models/exercises/translation.dart'; import 'package:wger/models/workouts/log.dart'; import 'package:wger/providers/routines.dart'; @@ -108,13 +106,7 @@ void showHttpExceptionErrorDialog( showDialog(context: context, builder: (context) => Container()); } -dynamic showDeleteDialog( - BuildContext context, - String confirmDeleteName, - Log log, - Translation exercise, - Map> exerciseData, -) async { +void showDeleteDialog(BuildContext context, String confirmDeleteName, Log log) async { final res = await showDialog( context: context, builder: (BuildContext contextDialog) { @@ -124,19 +116,18 @@ dynamic showDeleteDialog( ), actions: [ TextButton( + key: const ValueKey('cancel-button'), child: Text(MaterialLocalizations.of(context).cancelButtonLabel), onPressed: () => Navigator.of(contextDialog).pop(), ), TextButton( + key: const ValueKey('delete-button'), child: Text( AppLocalizations.of(context).delete, style: TextStyle(color: Theme.of(context).colorScheme.error), ), onPressed: () { - exerciseData[exercise]!.removeWhere((el) => el.id == log.id); - Provider.of(context, listen: false).deleteLog( - log, - ); + context.read().deleteLog(log.id!, log.routineId); Navigator.of(contextDialog).pop(); diff --git a/lib/models/workouts/routine.dart b/lib/models/workouts/routine.dart index 15ef154b..d0f55dbb 100644 --- a/lib/models/workouts/routine.dart +++ b/lib/models/workouts/routine.dart @@ -19,7 +19,6 @@ import 'package:json_annotation/json_annotation.dart'; import 'package:wger/helpers/json.dart'; import 'package:wger/helpers/misc.dart'; -import 'package:wger/models/exercises/exercise.dart'; import 'package:wger/models/workouts/day.dart'; import 'package:wger/models/workouts/day_data.dart'; import 'package:wger/models/workouts/log.dart'; @@ -177,28 +176,4 @@ class Routine { return groupedLogs; } - - /// Massages the log data to more easily present on the log overview - /// - Map> get logData { - final out = >{}; - for (final sessionData in sessions) { - final date = sessionData.session.date; - if (!out.containsKey(date)) { - out[date] = { - 'session': sessionData.session, - 'exercises': >{}, - }; - } - - for (final log in sessionData.logs) { - final exercise = log.exercise; - if (!out[date]!['exercises']!.containsKey(exercise)) { - out[date]!['exercises']![exercise] = []; - } - out[date]!['exercises']![exercise].add(log); - } - } - return out; - } } diff --git a/lib/models/workouts/session_api.dart b/lib/models/workouts/session_api.dart index 619823db..ca0e2bd3 100644 --- a/lib/models/workouts/session_api.dart +++ b/lib/models/workouts/session_api.dart @@ -17,6 +17,7 @@ */ import 'package:json_annotation/json_annotation.dart'; +import 'package:wger/models/exercises/exercise.dart'; import 'package:wger/models/workouts/log.dart'; import 'package:wger/models/workouts/session.dart'; @@ -32,6 +33,17 @@ class WorkoutSessionApi { WorkoutSessionApi({required this.session, this.logs = const []}); + /// Returns all different exercises in the session + /// + /// This is used to display the exercises in the session + List get exercises { + final Set exerciseSet = {}; + for (final log in logs) { + exerciseSet.add(log.exercise); + } + return exerciseSet.toList(); + } + // Boilerplate factory WorkoutSessionApi.fromJson(Map json) => _$WorkoutSessionApiFromJson(json); diff --git a/lib/providers/routines.dart b/lib/providers/routines.dart index e232dc23..d61310df 100644 --- a/lib/providers/routines.dart +++ b/lib/providers/routines.dart @@ -673,13 +673,9 @@ class RoutinesProvider with ChangeNotifier { notifyListeners(); }*/ - Future deleteLog(Log log) async { - await baseProvider.deleteRequest(_logsUrlPath, log.id!); - for (final workout in _routines) { - for (final sessionData in workout.sessions) { - sessionData.session.logs.removeWhere((element) => element.id == log.id); - } - } - notifyListeners(); + Future deleteLog(int logId, int routineId) async { + _logger.fine('Deleting log ${logId}'); + await baseProvider.deleteRequest(_logsUrlPath, logId); + await fetchAndSetRoutineFull(routineId); } } diff --git a/lib/screens/routine_logs_screen.dart b/lib/screens/routine_logs_screen.dart index 76379493..ab97ebe4 100644 --- a/lib/screens/routine_logs_screen.dart +++ b/lib/screens/routine_logs_screen.dart @@ -18,9 +18,8 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import 'package:wger/models/workouts/routine.dart'; import 'package:wger/providers/routines.dart'; -import 'package:wger/widgets/routines/app_bar.dart'; +import 'package:wger/widgets/core/app_bar.dart'; import 'package:wger/widgets/routines/workout_logs.dart'; class WorkoutLogsScreen extends StatelessWidget { @@ -30,13 +29,12 @@ class WorkoutLogsScreen extends StatelessWidget { @override Widget build(BuildContext context) { - final routine = ModalRoute.of(context)!.settings.arguments as Routine; + final routineId = ModalRoute.of(context)!.settings.arguments as int; + final routine = Provider.of(context).findById(routineId); return Scaffold( - appBar: RoutineDetailAppBar(routine), - body: Consumer( - builder: (context, value, child) => WorkoutLogs(routine), - ), + appBar: EmptyAppBar(routine.name), + body: WorkoutLogs(routine), ); } } diff --git a/lib/widgets/nutrition/meal.dart b/lib/widgets/nutrition/meal.dart index d1b6349a..00296109 100644 --- a/lib/widgets/nutrition/meal.dart +++ b/lib/widgets/nutrition/meal.dart @@ -238,15 +238,10 @@ class MealItemEditableFullTile extends StatelessWidget { return NutritionTile( leading: IngredientAvatar(ingredient: _item.ingredient), - title: Row( - mainAxisSize: MainAxisSize.max, - children: [ - Text( - '${_item.amount.toStringAsFixed(0)}$unit ${_item.ingredient.name}', - overflow: TextOverflow.ellipsis, - textAlign: TextAlign.left, - ), - ], + title: Text( + '${_item.amount.toStringAsFixed(0)}$unit ${_item.ingredient.name}', + overflow: TextOverflow.ellipsis, + textAlign: TextAlign.left, ), subtitle: (_viewMode != viewMode.withAllDetails && !_editing) ? null diff --git a/lib/widgets/routines/app_bar.dart b/lib/widgets/routines/app_bar.dart index d10d9557..6c431742 100644 --- a/lib/widgets/routines/app_bar.dart +++ b/lib/widgets/routines/app_bar.dart @@ -122,7 +122,7 @@ class RoutineDetailAppBar extends StatelessWidget implements PreferredSizeWidget Navigator.pushNamed( context, WorkoutLogsScreen.routeName, - arguments: routine, + arguments: routine.id, ); case _RoutineDetailBarOptions.delete: diff --git a/lib/widgets/routines/log.dart b/lib/widgets/routines/log.dart index 14a86618..52bc3863 100644 --- a/lib/widgets/routines/log.dart +++ b/lib/widgets/routines/log.dart @@ -22,7 +22,6 @@ import 'package:wger/helpers/colors.dart'; import 'package:wger/helpers/misc.dart'; import 'package:wger/helpers/ui.dart'; import 'package:wger/l10n/generated/app_localizations.dart'; -import 'package:wger/models/exercises/exercise.dart'; import 'package:wger/models/workouts/log.dart'; import 'package:wger/models/workouts/routine.dart'; import 'package:wger/models/workouts/session.dart'; @@ -116,18 +115,22 @@ class ExerciseLogChart extends StatelessWidget { mainAxisSize: MainAxisSize.max, children: [ LogChartWidgetFl(_logs, _selectedDate), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - ..._logs.keys.map((reps) { - colors.moveNext(); - return Indicator( - color: colors.current, - text: formatNum(reps).toString(), - isSquare: false, - ); - }), - ], + SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ..._logs.keys.map((reps) { + colors.moveNext(); + + return Indicator( + color: colors.current, + text: formatNum(reps).toString(), + isSquare: false, + ); + }), + ], + ), ), const SizedBox(height: 15), ], @@ -139,49 +142,42 @@ class DayLogWidget extends StatelessWidget { final DateTime _date; final Routine _routine; - final WorkoutSession _session; - final Map> _exerciseMap; - - const DayLogWidget(this._date, this._exerciseMap, this._session, this._routine); + const DayLogWidget(this._date, this._routine); @override Widget build(BuildContext context) { + final sessionApi = + _routine.sessions.firstWhere((sessionApi) => sessionApi.session.date.isSameDayAs(_date)); + final exercises = sessionApi.exercises; + return Card( child: Column( children: [ - SessionInfo(_session), - ..._exerciseMap.keys.map((exercise) { + SessionInfo(sessionApi.session), + ...exercises.map((exercise) { final translation = exercise.getTranslation(Localizations.localeOf(context).languageCode); return Column( children: [ - if (_exerciseMap[exercise]!.isNotEmpty) - Text( - translation.name, - style: Theme.of(context).textTheme.headlineSmall, - ) - else - Container(), - ..._exerciseMap[exercise]!.map( - (log) => Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text(log.singleLogRepTextNoNl), - IconButton( - icon: const Icon(Icons.delete), - onPressed: () { - showDeleteDialog( - context, - translation.name, - log, - translation, - _exerciseMap, - ); - }, - ), - ], - ), + Text( + translation.name, + style: Theme.of(context).textTheme.titleMedium, ), + ...sessionApi.logs.where((l) => l.exerciseId == exercise.id).map( + (log) => Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(log.singleLogRepTextNoNl), + IconButton( + icon: const Icon(Icons.delete), + key: ValueKey('delete-log-${log.id}'), + onPressed: () { + showDeleteDialog(context, translation.name, log); + }, + ), + ], + ), + ), Padding( padding: const EdgeInsets.symmetric(horizontal: 15), child: ExerciseLogChart( diff --git a/lib/widgets/routines/workout_logs.dart b/lib/widgets/routines/workout_logs.dart index 921ce9ab..55f3ce70 100644 --- a/lib/widgets/routines/workout_logs.dart +++ b/lib/widgets/routines/workout_logs.dart @@ -21,10 +21,7 @@ import 'package:flutter/material.dart'; import 'package:table_calendar/table_calendar.dart'; import 'package:wger/helpers/consts.dart'; import 'package:wger/l10n/generated/app_localizations.dart'; -import 'package:wger/models/exercises/exercise.dart'; -import 'package:wger/models/workouts/log.dart'; import 'package:wger/models/workouts/routine.dart'; -import 'package:wger/models/workouts/session.dart'; import 'package:wger/theme/theme.dart'; import 'package:wger/widgets/routines/log.dart'; @@ -49,27 +46,19 @@ class _WorkoutLogsState extends State { @override Widget build(BuildContext context) { return ListView( + padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 8), children: [ - Padding( - padding: const EdgeInsets.symmetric(vertical: 10), - child: Text( - AppLocalizations.of(context).labelWorkoutLogs, - style: Theme.of(context).textTheme.headlineSmall, - ), + Text( + AppLocalizations.of(context).labelWorkoutLogs, + style: Theme.of(context).textTheme.headlineSmall, ), - Padding( - padding: const EdgeInsets.all(8.0), - child: Text( - AppLocalizations.of(context).logHelpEntries, - textAlign: TextAlign.justify, - ), + Text( + AppLocalizations.of(context).logHelpEntries, + textAlign: TextAlign.justify, ), - Padding( - padding: const EdgeInsets.all(8.0), - child: Text( - AppLocalizations.of(context).logHelpEntriesUnits, - textAlign: TextAlign.justify, - ), + Text( + AppLocalizations.of(context).logHelpEntriesUnits, + textAlign: TextAlign.justify, ), SizedBox( width: double.infinity, @@ -83,10 +72,8 @@ class _WorkoutLogsState extends State { /// An event in the workout log calendar class WorkoutLogEvent { final DateTime dateTime; - final WorkoutSession session; - final Map> exercises; - const WorkoutLogEvent(this.dateTime, this.session, this.exercises); + const WorkoutLogEvent(this.dateTime); } class WorkoutLogCalendar extends StatefulWidget { @@ -101,8 +88,8 @@ class WorkoutLogCalendar extends StatefulWidget { class _WorkoutLogCalendarState extends State { DateTime _focusedDay = clock.now(); DateTime? _selectedDay; - late final ValueNotifier> _selectedEvents; - late Map> _events; + late final ValueNotifier> _selectedEvents; + late Map> _events; @override void initState() { @@ -121,18 +108,16 @@ class _WorkoutLogCalendarState extends State { } void loadEvents() { - for (final date in widget._routine.logData.keys) { - final entry = widget._routine.logData[date]!; - _events[DateFormatLists.format(date)] = [ - WorkoutLogEvent(date, entry['session'], entry['exercises']), + for (final sessionApi in widget._routine.sessions) { + _events[DateFormatLists.format(sessionApi.session.date)] = [ + sessionApi.session.date, ]; } - // Add initial selected day to events list _selectedEvents.value = _getEventsForDay(_selectedDay!); } - List _getEventsForDay(DateTime day) { + List _getEventsForDay(DateTime day) { return _events[DateFormatLists.format(day)] ?? []; } @@ -165,23 +150,17 @@ class _WorkoutLogCalendarState extends State { availableCalendarFormats: const {CalendarFormat.month: ''}, onDaySelected: _onDaySelected, onPageChanged: (focusedDay) { - // No need to call `setState()` here _focusedDay = focusedDay; }, ), const SizedBox(height: 8.0), SizedBox( - child: ValueListenableBuilder>( + child: ValueListenableBuilder>( valueListenable: _selectedEvents, builder: (context, logEvents, _) { // At the moment there is only one "event" per day return logEvents.isNotEmpty - ? DayLogWidget( - logEvents.first.dateTime, - logEvents.first.exercises, - logEvents.first.session, - widget._routine, - ) + ? DayLogWidget(logEvents.first, widget._routine) : Container(); }, ), diff --git a/pubspec.lock b/pubspec.lock index 79922851..f5a58c44 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -398,14 +398,6 @@ packages: description: flutter source: sdk version: "0.0.0" - flutter_calendar_carousel: - dependency: "direct main" - description: - name: flutter_calendar_carousel - sha256: "2fd1b58cefbefe0504ca7d0080a9e63be96041acc93b0c7f340ef0a55b9808fe" - url: "https://pub.dev" - source: hosted - version: "2.5.3" flutter_driver: dependency: transitive description: flutter diff --git a/pubspec.yaml b/pubspec.yaml index 07ed2621..fcaf6027 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -41,7 +41,6 @@ dependencies: fl_chart: ^0.69.2 flex_color_scheme: ^8.1.1 flex_seed_scheme: ^3.5.1 - flutter_calendar_carousel: ^2.4.4 flutter_html: ^3.0.0 flutter_staggered_grid_view: ^0.7.0 flutter_svg: ^2.0.17 diff --git a/test/helpers/colors_test.dart b/test/helpers/colors_test.dart index 12803e45..da451a78 100644 --- a/test/helpers/colors_test.dart +++ b/test/helpers/colors_test.dart @@ -1,78 +1,88 @@ +import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:wger/helpers/colors.dart'; void main() { group('generateChartColors', () { - test('should generate 3 colors for 3 items using iterator', () { + test('should generate 3 colors for 3 items', () { final iterator = generateChartColors(3).iterator; final expectedColors = LIST_OF_COLORS3.iterator; - while (iterator.moveNext() && expectedColors.moveNext()) { + while (expectedColors.moveNext()) { + expect(iterator.moveNext(), isTrue); expect(iterator.current, equals(expectedColors.current)); } - expect(iterator.moveNext(), isFalse); - expect(expectedColors.moveNext(), isFalse); + expect(iterator.moveNext(), isTrue); + expect(iterator.current, equals(Colors.black)); }); - test('should generate 5 colors for 5 items using iterator', () { + test('should generate 5 colors for 5 items', () { final iterator = generateChartColors(5).iterator; final expectedColors = LIST_OF_COLORS5.iterator; - while (iterator.moveNext() && expectedColors.moveNext()) { + while (expectedColors.moveNext()) { + expect(iterator.moveNext(), isTrue); expect(iterator.current, equals(expectedColors.current)); } - expect(iterator.moveNext(), isFalse); - expect(expectedColors.moveNext(), isFalse); + expect(iterator.moveNext(), isTrue); + expect(iterator.current, equals(Colors.black)); }); - test('should generate 8 colors for 8 items using iterator', () { + test('should generate 8 colors for 8 items', () { final iterator = generateChartColors(8).iterator; final expectedColors = LIST_OF_COLORS8.iterator; - while (iterator.moveNext() && expectedColors.moveNext()) { + while (expectedColors.moveNext()) { + expect(iterator.moveNext(), isTrue); expect(iterator.current, equals(expectedColors.current)); } - expect(iterator.moveNext(), isFalse); - expect(expectedColors.moveNext(), isFalse); + expect(iterator.moveNext(), isTrue); + expect(iterator.current, equals(Colors.black)); }); - test('should generate 8 colors for more than 8 items using iterator', () { + test('should generate surplus color for more than 8 items', () { final iterator = generateChartColors(10).iterator; final expectedColors = LIST_OF_COLORS8.iterator; - while (iterator.moveNext() && expectedColors.moveNext()) { + while (expectedColors.moveNext()) { + expect(iterator.moveNext(), isTrue); expect(iterator.current, equals(expectedColors.current)); } - expect(iterator.moveNext(), isFalse); - expect(expectedColors.moveNext(), isFalse); + // After exhausting the 8 colors, it should always return black + for (int i = 0; i < 12; i++) { + expect(iterator.moveNext(), isTrue); + expect(iterator.current, equals(Colors.black)); + } }); - test('should generate 8 colors for 0 items using iterator', () { + test('should generate 3 colors for 0 items', () { final iterator = generateChartColors(0).iterator; final expectedColors = LIST_OF_COLORS3.iterator; - while (iterator.moveNext() && expectedColors.moveNext()) { + while (expectedColors.moveNext()) { + expect(iterator.moveNext(), isTrue); expect(iterator.current, equals(expectedColors.current)); } - expect(iterator.moveNext(), isFalse); - expect(expectedColors.moveNext(), isFalse); + expect(iterator.moveNext(), isTrue); + expect(iterator.current, equals(Colors.black)); }); - test('should generate 8 colors for negative items using iterator', () { + test('should generate 3 colors for negative number of items', () { final iterator = generateChartColors(-5).iterator; final expectedColors = LIST_OF_COLORS3.iterator; - while (iterator.moveNext() && expectedColors.moveNext()) { + while (expectedColors.moveNext()) { + expect(iterator.moveNext(), isTrue); expect(iterator.current, equals(expectedColors.current)); } - expect(iterator.moveNext(), isFalse); - expect(expectedColors.moveNext(), isFalse); + expect(iterator.moveNext(), isTrue); + expect(iterator.current, equals(Colors.black)); }); }); } diff --git a/test/nutrition/goldens/nutritional_plan_2_one_meal_with_ingredients.png b/test/nutrition/goldens/nutritional_plan_2_one_meal_with_ingredients.png index bcfe2d3b..b437c159 100644 Binary files a/test/nutrition/goldens/nutritional_plan_2_one_meal_with_ingredients.png and b/test/nutrition/goldens/nutritional_plan_2_one_meal_with_ingredients.png differ diff --git a/test/nutrition/goldens/nutritional_plan_3_both_meals_with_ingredients.png b/test/nutrition/goldens/nutritional_plan_3_both_meals_with_ingredients.png index 8abe53b4..02b148cd 100644 Binary files a/test/nutrition/goldens/nutritional_plan_3_both_meals_with_ingredients.png and b/test/nutrition/goldens/nutritional_plan_3_both_meals_with_ingredients.png differ diff --git a/test/workout/day_form_test.mocks.dart b/test/workout/day_form_test.mocks.dart index 82f93fc1..ccd1bd6f 100644 --- a/test/workout/day_form_test.mocks.dart +++ b/test/workout/day_form_test.mocks.dart @@ -478,8 +478,8 @@ class MockRoutinesProvider extends _i1.Mock implements _i12.RoutinesProvider { ) as _i13.Future<_i11.Log>); @override - _i13.Future deleteLog(_i11.Log? log) => (super.noSuchMethod( - Invocation.method(#deleteLog, [log]), + _i13.Future deleteLog(int? logId, int? routineId) => (super.noSuchMethod( + Invocation.method(#deleteLog, [logId, routineId]), returnValue: _i13.Future.value(), returnValueForMissingStub: _i13.Future.value(), ) as _i13.Future); diff --git a/test/workout/goldens/routine_logs_screen_detail.png b/test/workout/goldens/routine_logs_screen_detail.png index 2becb554..df048921 100644 Binary files a/test/workout/goldens/routine_logs_screen_detail.png and b/test/workout/goldens/routine_logs_screen_detail.png differ diff --git a/test/workout/repetition_unit_form_widget_test.mocks.dart b/test/workout/repetition_unit_form_widget_test.mocks.dart index 0350975c..7764f336 100644 --- a/test/workout/repetition_unit_form_widget_test.mocks.dart +++ b/test/workout/repetition_unit_form_widget_test.mocks.dart @@ -478,8 +478,8 @@ class MockRoutinesProvider extends _i1.Mock implements _i12.RoutinesProvider { ) as _i13.Future<_i11.Log>); @override - _i13.Future deleteLog(_i11.Log? log) => (super.noSuchMethod( - Invocation.method(#deleteLog, [log]), + _i13.Future deleteLog(int? logId, int? routineId) => (super.noSuchMethod( + Invocation.method(#deleteLog, [logId, routineId]), returnValue: _i13.Future.value(), returnValueForMissingStub: _i13.Future.value(), ) as _i13.Future); diff --git a/test/workout/routine_edit_screen_test.mocks.dart b/test/workout/routine_edit_screen_test.mocks.dart index 925ff6ae..8fbd8e1e 100644 --- a/test/workout/routine_edit_screen_test.mocks.dart +++ b/test/workout/routine_edit_screen_test.mocks.dart @@ -478,8 +478,8 @@ class MockRoutinesProvider extends _i1.Mock implements _i12.RoutinesProvider { ) as _i13.Future<_i11.Log>); @override - _i13.Future deleteLog(_i11.Log? log) => (super.noSuchMethod( - Invocation.method(#deleteLog, [log]), + _i13.Future deleteLog(int? logId, int? routineId) => (super.noSuchMethod( + Invocation.method(#deleteLog, [logId, routineId]), returnValue: _i13.Future.value(), returnValueForMissingStub: _i13.Future.value(), ) as _i13.Future); diff --git a/test/workout/routine_edit_test.mocks.dart b/test/workout/routine_edit_test.mocks.dart index 4ac118a1..e0874cb3 100644 --- a/test/workout/routine_edit_test.mocks.dart +++ b/test/workout/routine_edit_test.mocks.dart @@ -478,8 +478,8 @@ class MockRoutinesProvider extends _i1.Mock implements _i12.RoutinesProvider { ) as _i13.Future<_i11.Log>); @override - _i13.Future deleteLog(_i11.Log? log) => (super.noSuchMethod( - Invocation.method(#deleteLog, [log]), + _i13.Future deleteLog(int? logId, int? routineId) => (super.noSuchMethod( + Invocation.method(#deleteLog, [logId, routineId]), returnValue: _i13.Future.value(), returnValueForMissingStub: _i13.Future.value(), ) as _i13.Future); diff --git a/test/workout/routine_form_test.mocks.dart b/test/workout/routine_form_test.mocks.dart index cefb2f64..af723622 100644 --- a/test/workout/routine_form_test.mocks.dart +++ b/test/workout/routine_form_test.mocks.dart @@ -478,8 +478,8 @@ class MockRoutinesProvider extends _i1.Mock implements _i12.RoutinesProvider { ) as _i13.Future<_i11.Log>); @override - _i13.Future deleteLog(_i11.Log? log) => (super.noSuchMethod( - Invocation.method(#deleteLog, [log]), + _i13.Future deleteLog(int? logId, int? routineId) => (super.noSuchMethod( + Invocation.method(#deleteLog, [logId, routineId]), returnValue: _i13.Future.value(), returnValueForMissingStub: _i13.Future.value(), ) as _i13.Future); diff --git a/test/workout/routine_logs_screen_test.dart b/test/workout/routine_logs_screen_test.dart index 507e6910..8d57b942 100644 --- a/test/workout/routine_logs_screen_test.dart +++ b/test/workout/routine_logs_screen_test.dart @@ -22,6 +22,7 @@ import 'package:clock/clock.dart'; import 'package:flutter/material.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/l10n/generated/app_localizations.dart'; import 'package:wger/models/workouts/routine.dart'; @@ -40,7 +41,9 @@ void main() { setUp(() { routine = getTestRoutine(); - routine.logs[0].date = DateTime.now(); + routine.sessions[0].session.date = DateTime(2025, 3, 29); + + when(mockRoutinesProvider.findById(any)).thenAnswer((_) => routine); }); Widget renderWidget({locale = 'en'}) { @@ -56,7 +59,7 @@ void main() { home: TextButton( onPressed: () => key.currentState!.push( MaterialPageRoute( - settings: RouteSettings(arguments: routine), + settings: RouteSettings(arguments: routine.id), builder: (_) => const WorkoutLogsScreen(), ), ), @@ -89,8 +92,29 @@ void main() { expect(find.text('Training logs'), findsOneWidget); expect(find.byType(WorkoutLogCalendar), findsOneWidget); + expect(find.text('Bench press'), findsOneWidget); + expect(find.byKey(const ValueKey('delete-log-1')), findsOneWidget); + expect(find.byKey(const ValueKey('delete-log-2')), findsOneWidget); + expect(find.byKey(const ValueKey('delete-log-3')), findsNothing); }); }, tags: ['golden'], ); + + testWidgets('Test deleting log entries', (WidgetTester tester) async { + await withClock(Clock.fixed(DateTime(2025, 3, 29)), () async { + await tester.pumpWidget(renderWidget()); + await tester.tap(find.byType(TextButton)); + await tester.pumpAndSettle(); + await tester.drag(find.byType(ListView), const Offset(0, -500)); + await tester.pumpAndSettle(); + await tester.tap(find.byKey(const ValueKey('delete-log-1'))); + await tester.pumpAndSettle(); + + expect(find.byKey(const ValueKey('delete-button')), findsOneWidget); + expect(find.byKey(const ValueKey('cancel-button')), findsOneWidget); + await tester.tap(find.byKey(const ValueKey('delete-button'))); + verify(mockRoutinesProvider.deleteLog(1, 1)).called(1); + }); + }); } diff --git a/test/workout/routine_logs_screen_test.mocks.dart b/test/workout/routine_logs_screen_test.mocks.dart index bea7d1bc..c5b9828f 100644 --- a/test/workout/routine_logs_screen_test.mocks.dart +++ b/test/workout/routine_logs_screen_test.mocks.dart @@ -478,8 +478,8 @@ class MockRoutinesProvider extends _i1.Mock implements _i12.RoutinesProvider { ) as _i13.Future<_i11.Log>); @override - _i13.Future deleteLog(_i11.Log? log) => (super.noSuchMethod( - Invocation.method(#deleteLog, [log]), + _i13.Future deleteLog(int? logId, int? routineId) => (super.noSuchMethod( + Invocation.method(#deleteLog, [logId, routineId]), returnValue: _i13.Future.value(), returnValueForMissingStub: _i13.Future.value(), ) as _i13.Future); diff --git a/test/workout/slot_entry_form_test.mocks.dart b/test/workout/slot_entry_form_test.mocks.dart index 11877e80..3eb6ed3b 100644 --- a/test/workout/slot_entry_form_test.mocks.dart +++ b/test/workout/slot_entry_form_test.mocks.dart @@ -478,8 +478,8 @@ class MockRoutinesProvider extends _i1.Mock implements _i12.RoutinesProvider { ) as _i13.Future<_i11.Log>); @override - _i13.Future deleteLog(_i11.Log? log) => (super.noSuchMethod( - Invocation.method(#deleteLog, [log]), + _i13.Future deleteLog(int? logId, int? routineId) => (super.noSuchMethod( + Invocation.method(#deleteLog, [logId, routineId]), returnValue: _i13.Future.value(), returnValueForMissingStub: _i13.Future.value(), ) as _i13.Future); diff --git a/test/workout/weight_unit_form_widget_test.mocks.dart b/test/workout/weight_unit_form_widget_test.mocks.dart index 2b566151..b84bfb49 100644 --- a/test/workout/weight_unit_form_widget_test.mocks.dart +++ b/test/workout/weight_unit_form_widget_test.mocks.dart @@ -478,8 +478,8 @@ class MockRoutinesProvider extends _i1.Mock implements _i12.RoutinesProvider { ) as _i13.Future<_i11.Log>); @override - _i13.Future deleteLog(_i11.Log? log) => (super.noSuchMethod( - Invocation.method(#deleteLog, [log]), + _i13.Future deleteLog(int? logId, int? routineId) => (super.noSuchMethod( + Invocation.method(#deleteLog, [logId, routineId]), returnValue: _i13.Future.value(), returnValueForMissingStub: _i13.Future.value(), ) as _i13.Future);