Move common logic to SessionForm

Refactor and polish a bit the form in the log overview.
This commit is contained in:
Roland Geider
2025-05-09 21:12:49 +02:00
parent e8ff3458e0
commit d663bcd88a
8 changed files with 1288 additions and 367 deletions

View File

@@ -0,0 +1,130 @@
// test/widgets/routines/forms/session_form_test.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/session.dart';
import 'package:wger/providers/routines.dart';
import 'package:wger/widgets/routines/forms/session.dart';
import 'session_form_test.mocks.dart';
@GenerateMocks([RoutinesProvider])
void main() {
late MockRoutinesProvider mockRoutinesProvider;
setUp(() {
mockRoutinesProvider = MockRoutinesProvider();
});
Future<void> pumpSessionForm(
WidgetTester tester, {
WorkoutSession? session,
int routineId = 1,
Function()? onSaved,
}) async {
await tester.pumpWidget(
MultiProvider(
providers: [
ChangeNotifierProvider<RoutinesProvider>.value(
value: mockRoutinesProvider,
),
],
child: MaterialApp(
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
home: Scaffold(
body: SessionForm(
routineId,
session: session,
onSaved: onSaved,
),
),
),
),
);
await tester.pumpAndSettle();
}
group('SessionForm', () {
testWidgets('renders correctly for an existing session', (WidgetTester tester) async {
//Arrange
final existingSession = WorkoutSession(
id: 1,
routineId: 1,
notes: 'Existing notes',
impression: 1,
date: DateTime.now(),
timeStart: const TimeOfDay(hour: 10, minute: 0),
timeEnd: const TimeOfDay(hour: 11, minute: 0),
);
//Act
await pumpSessionForm(tester, session: existingSession);
//Assert
expect(find.widgetWithText(TextFormField, 'Existing notes'), findsOneWidget);
final toggleButtons = tester.widget<ToggleButtons>(find.byType(ToggleButtons));
expect(toggleButtons.isSelected, [true, false, false]); // Bad impression
expect(find.widgetWithText(TextFormField, '10:00'), findsOneWidget);
expect(find.widgetWithText(TextFormField, '11:00'), findsOneWidget);
});
testWidgets('saves a new session', (WidgetTester tester) async {
// Arrange
bool onSavedCalled = false;
await pumpSessionForm(tester, onSaved: () => onSavedCalled = true);
when(mockRoutinesProvider.addSession(any, any)).thenAnswer(
(_) async => WorkoutSession(id: 1, routineId: 1, date: DateTime.now()),
);
// Act
await tester.enterText(find.widgetWithText(TextFormField, 'Notes'), 'New session notes');
await tester.tap(find.byKey(const ValueKey('save-button')));
await tester.pumpAndSettle();
// Assert
verify(mockRoutinesProvider.addSession(any, 1)).called(1);
expect(onSavedCalled, isTrue);
});
testWidgets('saves an existing session', (WidgetTester tester) async {
// Arrange
bool onSavedCalled = false;
final existingSession = WorkoutSession(
id: 1,
routineId: 1,
notes: 'Old notes',
impression: 2,
date: DateTime.now(),
);
when(mockRoutinesProvider.editSession(any)).thenAnswer(
(_) async => WorkoutSession(
id: 1,
routineId: 1,
date: DateTime.now(),
),
);
// Act
await pumpSessionForm(
tester,
session: existingSession,
onSaved: () => onSavedCalled = true,
);
await tester.enterText(find.widgetWithText(TextFormField, 'Old notes'), 'Updated notes');
await tester.tap(find.byKey(const ValueKey('save-button')));
await tester.pumpAndSettle();
// Assert
final captured =
verify(mockRoutinesProvider.editSession(captureAny)).captured.single as WorkoutSession;
expect(captured.notes, 'Updated notes');
expect(onSavedCalled, isTrue);
});
});
}

View File

@@ -0,0 +1,827 @@
// Mocks generated by Mockito 5.4.6 from annotations
// in wger/test/workout/forms/session_form_test.dart.
// Do not manually edit this file.
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'dart:async' as _i13;
import 'dart:ui' as _i16;
import 'package:mockito/mockito.dart' as _i1;
import 'package:mockito/src/dummies.dart' as _i15;
import 'package:wger/models/workouts/base_config.dart' as _i9;
import 'package:wger/models/workouts/day.dart' as _i6;
import 'package:wger/models/workouts/day_data.dart' as _i14;
import 'package:wger/models/workouts/log.dart' as _i11;
import 'package:wger/models/workouts/repetition_unit.dart' as _i4;
import 'package:wger/models/workouts/routine.dart' as _i5;
import 'package:wger/models/workouts/session.dart' as _i10;
import 'package:wger/models/workouts/slot.dart' as _i7;
import 'package:wger/models/workouts/slot_entry.dart' as _i8;
import 'package:wger/models/workouts/weight_unit.dart' as _i3;
import 'package:wger/providers/base_provider.dart' as _i2;
import 'package:wger/providers/routines.dart' as _i12;
// ignore_for_file: type=lint
// ignore_for_file: avoid_redundant_argument_values
// ignore_for_file: avoid_setters_without_getters
// ignore_for_file: comment_references
// ignore_for_file: deprecated_member_use
// ignore_for_file: deprecated_member_use_from_same_package
// ignore_for_file: implementation_imports
// ignore_for_file: invalid_use_of_visible_for_testing_member
// ignore_for_file: must_be_immutable
// ignore_for_file: prefer_const_constructors
// ignore_for_file: unnecessary_parenthesis
// ignore_for_file: camel_case_types
// ignore_for_file: subtype_of_sealed_class
class _FakeWgerBaseProvider_0 extends _i1.SmartFake implements _i2.WgerBaseProvider {
_FakeWgerBaseProvider_0(
Object parent,
Invocation parentInvocation,
) : super(
parent,
parentInvocation,
);
}
class _FakeWeightUnit_1 extends _i1.SmartFake implements _i3.WeightUnit {
_FakeWeightUnit_1(
Object parent,
Invocation parentInvocation,
) : super(
parent,
parentInvocation,
);
}
class _FakeRepetitionUnit_2 extends _i1.SmartFake implements _i4.RepetitionUnit {
_FakeRepetitionUnit_2(
Object parent,
Invocation parentInvocation,
) : super(
parent,
parentInvocation,
);
}
class _FakeRoutine_3 extends _i1.SmartFake implements _i5.Routine {
_FakeRoutine_3(
Object parent,
Invocation parentInvocation,
) : super(
parent,
parentInvocation,
);
}
class _FakeDay_4 extends _i1.SmartFake implements _i6.Day {
_FakeDay_4(
Object parent,
Invocation parentInvocation,
) : super(
parent,
parentInvocation,
);
}
class _FakeSlot_5 extends _i1.SmartFake implements _i7.Slot {
_FakeSlot_5(
Object parent,
Invocation parentInvocation,
) : super(
parent,
parentInvocation,
);
}
class _FakeSlotEntry_6 extends _i1.SmartFake implements _i8.SlotEntry {
_FakeSlotEntry_6(
Object parent,
Invocation parentInvocation,
) : super(
parent,
parentInvocation,
);
}
class _FakeBaseConfig_7 extends _i1.SmartFake implements _i9.BaseConfig {
_FakeBaseConfig_7(
Object parent,
Invocation parentInvocation,
) : super(
parent,
parentInvocation,
);
}
class _FakeWorkoutSession_8 extends _i1.SmartFake implements _i10.WorkoutSession {
_FakeWorkoutSession_8(
Object parent,
Invocation parentInvocation,
) : super(
parent,
parentInvocation,
);
}
class _FakeLog_9 extends _i1.SmartFake implements _i11.Log {
_FakeLog_9(
Object parent,
Invocation parentInvocation,
) : super(
parent,
parentInvocation,
);
}
/// A class which mocks [RoutinesProvider].
///
/// See the documentation for Mockito's code generation for more information.
class MockRoutinesProvider extends _i1.Mock implements _i12.RoutinesProvider {
MockRoutinesProvider() {
_i1.throwOnMissingStub(this);
}
@override
_i2.WgerBaseProvider get baseProvider => (super.noSuchMethod(
Invocation.getter(#baseProvider),
returnValue: _FakeWgerBaseProvider_0(
this,
Invocation.getter(#baseProvider),
),
) as _i2.WgerBaseProvider);
@override
List<_i5.Routine> get items => (super.noSuchMethod(
Invocation.getter(#items),
returnValue: <_i5.Routine>[],
) as List<_i5.Routine>);
@override
List<_i3.WeightUnit> get weightUnits => (super.noSuchMethod(
Invocation.getter(#weightUnits),
returnValue: <_i3.WeightUnit>[],
) as List<_i3.WeightUnit>);
@override
_i3.WeightUnit get defaultWeightUnit => (super.noSuchMethod(
Invocation.getter(#defaultWeightUnit),
returnValue: _FakeWeightUnit_1(
this,
Invocation.getter(#defaultWeightUnit),
),
) as _i3.WeightUnit);
@override
List<_i4.RepetitionUnit> get repetitionUnits => (super.noSuchMethod(
Invocation.getter(#repetitionUnits),
returnValue: <_i4.RepetitionUnit>[],
) as List<_i4.RepetitionUnit>);
@override
_i4.RepetitionUnit get defaultRepetitionUnit => (super.noSuchMethod(
Invocation.getter(#defaultRepetitionUnit),
returnValue: _FakeRepetitionUnit_2(
this,
Invocation.getter(#defaultRepetitionUnit),
),
) as _i4.RepetitionUnit);
@override
set activeRoutine(_i5.Routine? _activeRoutine) => super.noSuchMethod(
Invocation.setter(
#activeRoutine,
_activeRoutine,
),
returnValueForMissingStub: null,
);
@override
set weightUnits(List<_i3.WeightUnit>? weightUnits) => super.noSuchMethod(
Invocation.setter(
#weightUnits,
weightUnits,
),
returnValueForMissingStub: null,
);
@override
set repetitionUnits(List<_i4.RepetitionUnit>? repetitionUnits) => super.noSuchMethod(
Invocation.setter(
#repetitionUnits,
repetitionUnits,
),
returnValueForMissingStub: null,
);
@override
bool get hasListeners => (super.noSuchMethod(
Invocation.getter(#hasListeners),
returnValue: false,
) as bool);
@override
void clear() => super.noSuchMethod(
Invocation.method(
#clear,
[],
),
returnValueForMissingStub: null,
);
@override
_i3.WeightUnit findWeightUnitById(int? id) => (super.noSuchMethod(
Invocation.method(
#findWeightUnitById,
[id],
),
returnValue: _FakeWeightUnit_1(
this,
Invocation.method(
#findWeightUnitById,
[id],
),
),
) as _i3.WeightUnit);
@override
_i4.RepetitionUnit findRepetitionUnitById(int? id) => (super.noSuchMethod(
Invocation.method(
#findRepetitionUnitById,
[id],
),
returnValue: _FakeRepetitionUnit_2(
this,
Invocation.method(
#findRepetitionUnitById,
[id],
),
),
) as _i4.RepetitionUnit);
@override
List<_i5.Routine> getPlans() => (super.noSuchMethod(
Invocation.method(
#getPlans,
[],
),
returnValue: <_i5.Routine>[],
) as List<_i5.Routine>);
@override
_i5.Routine findById(int? id) => (super.noSuchMethod(
Invocation.method(
#findById,
[id],
),
returnValue: _FakeRoutine_3(
this,
Invocation.method(
#findById,
[id],
),
),
) as _i5.Routine);
@override
int findIndexById(int? id) => (super.noSuchMethod(
Invocation.method(
#findIndexById,
[id],
),
returnValue: 0,
) as int);
@override
void setActiveRoutine() => super.noSuchMethod(
Invocation.method(
#setActiveRoutine,
[],
),
returnValueForMissingStub: null,
);
@override
_i13.Future<void> fetchAndSetAllRoutinesFull() => (super.noSuchMethod(
Invocation.method(
#fetchAndSetAllRoutinesFull,
[],
),
returnValue: _i13.Future<void>.value(),
returnValueForMissingStub: _i13.Future<void>.value(),
) as _i13.Future<void>);
@override
_i13.Future<void> fetchAndSetAllRoutinesSparse() => (super.noSuchMethod(
Invocation.method(
#fetchAndSetAllRoutinesSparse,
[],
),
returnValue: _i13.Future<void>.value(),
returnValueForMissingStub: _i13.Future<void>.value(),
) as _i13.Future<void>);
@override
_i13.Future<void> setExercisesAndUnits(List<_i14.DayData>? entries) => (super.noSuchMethod(
Invocation.method(
#setExercisesAndUnits,
[entries],
),
returnValue: _i13.Future<void>.value(),
returnValueForMissingStub: _i13.Future<void>.value(),
) as _i13.Future<void>);
@override
_i13.Future<_i5.Routine> fetchAndSetRoutineSparse(int? planId) => (super.noSuchMethod(
Invocation.method(
#fetchAndSetRoutineSparse,
[planId],
),
returnValue: _i13.Future<_i5.Routine>.value(_FakeRoutine_3(
this,
Invocation.method(
#fetchAndSetRoutineSparse,
[planId],
),
)),
) as _i13.Future<_i5.Routine>);
@override
_i13.Future<_i5.Routine> fetchAndSetRoutineFull(int? routineId) => (super.noSuchMethod(
Invocation.method(
#fetchAndSetRoutineFull,
[routineId],
),
returnValue: _i13.Future<_i5.Routine>.value(_FakeRoutine_3(
this,
Invocation.method(
#fetchAndSetRoutineFull,
[routineId],
),
)),
) as _i13.Future<_i5.Routine>);
@override
_i13.Future<_i5.Routine> addRoutine(_i5.Routine? routine) => (super.noSuchMethod(
Invocation.method(
#addRoutine,
[routine],
),
returnValue: _i13.Future<_i5.Routine>.value(_FakeRoutine_3(
this,
Invocation.method(
#addRoutine,
[routine],
),
)),
) as _i13.Future<_i5.Routine>);
@override
_i13.Future<void> editRoutine(_i5.Routine? routine) => (super.noSuchMethod(
Invocation.method(
#editRoutine,
[routine],
),
returnValue: _i13.Future<void>.value(),
returnValueForMissingStub: _i13.Future<void>.value(),
) as _i13.Future<void>);
@override
_i13.Future<void> deleteRoutine(int? id) => (super.noSuchMethod(
Invocation.method(
#deleteRoutine,
[id],
),
returnValue: _i13.Future<void>.value(),
returnValueForMissingStub: _i13.Future<void>.value(),
) as _i13.Future<void>);
@override
_i13.Future<void> fetchAndSetRepetitionUnits() => (super.noSuchMethod(
Invocation.method(
#fetchAndSetRepetitionUnits,
[],
),
returnValue: _i13.Future<void>.value(),
returnValueForMissingStub: _i13.Future<void>.value(),
) as _i13.Future<void>);
@override
_i13.Future<void> fetchAndSetWeightUnits() => (super.noSuchMethod(
Invocation.method(
#fetchAndSetWeightUnits,
[],
),
returnValue: _i13.Future<void>.value(),
returnValueForMissingStub: _i13.Future<void>.value(),
) as _i13.Future<void>);
@override
_i13.Future<void> fetchAndSetUnits() => (super.noSuchMethod(
Invocation.method(
#fetchAndSetUnits,
[],
),
returnValue: _i13.Future<void>.value(),
returnValueForMissingStub: _i13.Future<void>.value(),
) as _i13.Future<void>);
@override
_i13.Future<_i6.Day> addDay(_i6.Day? day) => (super.noSuchMethod(
Invocation.method(
#addDay,
[day],
),
returnValue: _i13.Future<_i6.Day>.value(_FakeDay_4(
this,
Invocation.method(
#addDay,
[day],
),
)),
) as _i13.Future<_i6.Day>);
@override
_i13.Future<void> editDay(_i6.Day? day) => (super.noSuchMethod(
Invocation.method(
#editDay,
[day],
),
returnValue: _i13.Future<void>.value(),
returnValueForMissingStub: _i13.Future<void>.value(),
) as _i13.Future<void>);
@override
_i13.Future<void> editDays(List<_i6.Day>? days) => (super.noSuchMethod(
Invocation.method(
#editDays,
[days],
),
returnValue: _i13.Future<void>.value(),
returnValueForMissingStub: _i13.Future<void>.value(),
) as _i13.Future<void>);
@override
_i13.Future<void> deleteDay(int? dayId) => (super.noSuchMethod(
Invocation.method(
#deleteDay,
[dayId],
),
returnValue: _i13.Future<void>.value(),
returnValueForMissingStub: _i13.Future<void>.value(),
) as _i13.Future<void>);
@override
_i13.Future<_i7.Slot> addSlot(
_i7.Slot? slot,
int? routineId,
) =>
(super.noSuchMethod(
Invocation.method(
#addSlot,
[
slot,
routineId,
],
),
returnValue: _i13.Future<_i7.Slot>.value(_FakeSlot_5(
this,
Invocation.method(
#addSlot,
[
slot,
routineId,
],
),
)),
) as _i13.Future<_i7.Slot>);
@override
_i13.Future<void> deleteSlot(
int? slotId,
int? routineId,
) =>
(super.noSuchMethod(
Invocation.method(
#deleteSlot,
[
slotId,
routineId,
],
),
returnValue: _i13.Future<void>.value(),
returnValueForMissingStub: _i13.Future<void>.value(),
) as _i13.Future<void>);
@override
_i13.Future<void> editSlot(
_i7.Slot? slot,
int? routineId,
) =>
(super.noSuchMethod(
Invocation.method(
#editSlot,
[
slot,
routineId,
],
),
returnValue: _i13.Future<void>.value(),
returnValueForMissingStub: _i13.Future<void>.value(),
) as _i13.Future<void>);
@override
_i13.Future<void> editSlots(
List<_i7.Slot>? slots,
int? routineId,
) =>
(super.noSuchMethod(
Invocation.method(
#editSlots,
[
slots,
routineId,
],
),
returnValue: _i13.Future<void>.value(),
returnValueForMissingStub: _i13.Future<void>.value(),
) as _i13.Future<void>);
@override
_i13.Future<_i8.SlotEntry> addSlotEntry(
_i8.SlotEntry? entry,
int? routineId,
) =>
(super.noSuchMethod(
Invocation.method(
#addSlotEntry,
[
entry,
routineId,
],
),
returnValue: _i13.Future<_i8.SlotEntry>.value(_FakeSlotEntry_6(
this,
Invocation.method(
#addSlotEntry,
[
entry,
routineId,
],
),
)),
) as _i13.Future<_i8.SlotEntry>);
@override
_i13.Future<void> deleteSlotEntry(
int? id,
int? routineId,
) =>
(super.noSuchMethod(
Invocation.method(
#deleteSlotEntry,
[
id,
routineId,
],
),
returnValue: _i13.Future<void>.value(),
returnValueForMissingStub: _i13.Future<void>.value(),
) as _i13.Future<void>);
@override
_i13.Future<void> editSlotEntry(
_i8.SlotEntry? entry,
int? routineId,
) =>
(super.noSuchMethod(
Invocation.method(
#editSlotEntry,
[
entry,
routineId,
],
),
returnValue: _i13.Future<void>.value(),
returnValueForMissingStub: _i13.Future<void>.value(),
) as _i13.Future<void>);
@override
String getConfigUrl(_i8.ConfigType? type) => (super.noSuchMethod(
Invocation.method(
#getConfigUrl,
[type],
),
returnValue: _i15.dummyValue<String>(
this,
Invocation.method(
#getConfigUrl,
[type],
),
),
) as String);
@override
_i13.Future<_i9.BaseConfig> editConfig(
_i9.BaseConfig? config,
_i8.ConfigType? type,
) =>
(super.noSuchMethod(
Invocation.method(
#editConfig,
[
config,
type,
],
),
returnValue: _i13.Future<_i9.BaseConfig>.value(_FakeBaseConfig_7(
this,
Invocation.method(
#editConfig,
[
config,
type,
],
),
)),
) as _i13.Future<_i9.BaseConfig>);
@override
_i13.Future<_i9.BaseConfig> addConfig(
_i9.BaseConfig? config,
_i8.ConfigType? type,
) =>
(super.noSuchMethod(
Invocation.method(
#addConfig,
[
config,
type,
],
),
returnValue: _i13.Future<_i9.BaseConfig>.value(_FakeBaseConfig_7(
this,
Invocation.method(
#addConfig,
[
config,
type,
],
),
)),
) as _i13.Future<_i9.BaseConfig>);
@override
_i13.Future<void> deleteConfig(
int? id,
_i8.ConfigType? type,
) =>
(super.noSuchMethod(
Invocation.method(
#deleteConfig,
[
id,
type,
],
),
returnValue: _i13.Future<void>.value(),
returnValueForMissingStub: _i13.Future<void>.value(),
) as _i13.Future<void>);
@override
_i13.Future<void> handleConfig(
_i8.SlotEntry? entry,
String? input,
_i8.ConfigType? type,
) =>
(super.noSuchMethod(
Invocation.method(
#handleConfig,
[
entry,
input,
type,
],
),
returnValue: _i13.Future<void>.value(),
returnValueForMissingStub: _i13.Future<void>.value(),
) as _i13.Future<void>);
@override
_i13.Future<List<_i10.WorkoutSession>> fetchSessionData() => (super.noSuchMethod(
Invocation.method(
#fetchSessionData,
[],
),
returnValue: _i13.Future<List<_i10.WorkoutSession>>.value(<_i10.WorkoutSession>[]),
) as _i13.Future<List<_i10.WorkoutSession>>);
@override
_i13.Future<_i10.WorkoutSession> addSession(
_i10.WorkoutSession? session,
int? routineId,
) =>
(super.noSuchMethod(
Invocation.method(
#addSession,
[
session,
routineId,
],
),
returnValue: _i13.Future<_i10.WorkoutSession>.value(_FakeWorkoutSession_8(
this,
Invocation.method(
#addSession,
[
session,
routineId,
],
),
)),
) as _i13.Future<_i10.WorkoutSession>);
@override
_i13.Future<_i10.WorkoutSession> editSession(_i10.WorkoutSession? session) => (super.noSuchMethod(
Invocation.method(
#editSession,
[session],
),
returnValue: _i13.Future<_i10.WorkoutSession>.value(_FakeWorkoutSession_8(
this,
Invocation.method(
#editSession,
[session],
),
)),
) as _i13.Future<_i10.WorkoutSession>);
@override
_i13.Future<_i11.Log> addLog(_i11.Log? log) => (super.noSuchMethod(
Invocation.method(
#addLog,
[log],
),
returnValue: _i13.Future<_i11.Log>.value(_FakeLog_9(
this,
Invocation.method(
#addLog,
[log],
),
)),
) as _i13.Future<_i11.Log>);
@override
_i13.Future<void> deleteLog(
int? logId,
int? routineId,
) =>
(super.noSuchMethod(
Invocation.method(
#deleteLog,
[
logId,
routineId,
],
),
returnValue: _i13.Future<void>.value(),
returnValueForMissingStub: _i13.Future<void>.value(),
) as _i13.Future<void>);
@override
void addListener(_i16.VoidCallback? listener) => super.noSuchMethod(
Invocation.method(
#addListener,
[listener],
),
returnValueForMissingStub: null,
);
@override
void removeListener(_i16.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,
);
}