diff --git a/ios/Flutter/ephemeral/flutter_lldb_helper.py b/ios/Flutter/ephemeral/flutter_lldb_helper.py new file mode 100644 index 000000000..a88caf99d --- /dev/null +++ b/ios/Flutter/ephemeral/flutter_lldb_helper.py @@ -0,0 +1,32 @@ +# +# Generated file, do not edit. +# + +import lldb + +def handle_new_rx_page(frame: lldb.SBFrame, bp_loc, extra_args, intern_dict): + """Intercept NOTIFY_DEBUGGER_ABOUT_RX_PAGES and touch the pages.""" + base = frame.register["x0"].GetValueAsAddress() + page_len = frame.register["x1"].GetValueAsUnsigned() + + # Note: NOTIFY_DEBUGGER_ABOUT_RX_PAGES will check contents of the + # first page to see if handled it correctly. This makes diagnosing + # misconfiguration (e.g. missing breakpoint) easier. + data = bytearray(page_len) + data[0:8] = b'IHELPED!' + + error = lldb.SBError() + frame.GetThread().GetProcess().WriteMemory(base, data, error) + if not error.Success(): + print(f'Failed to write into {base}[+{page_len}]', error) + return + +def __lldb_init_module(debugger: lldb.SBDebugger, _): + target = debugger.GetDummyTarget() + # Caveat: must use BreakpointCreateByRegEx here and not + # BreakpointCreateByName. For some reasons callback function does not + # get carried over from dummy target for the later. + bp = target.BreakpointCreateByRegex("^NOTIFY_DEBUGGER_ABOUT_RX_PAGES$") + bp.SetScriptCallbackFunction('{}.handle_new_rx_page'.format(__name__)) + bp.SetAutoContinue(True) + print("-- LLDB integration loaded --") diff --git a/ios/Flutter/ephemeral/flutter_lldbinit b/ios/Flutter/ephemeral/flutter_lldbinit new file mode 100644 index 000000000..e3ba6fbed --- /dev/null +++ b/ios/Flutter/ephemeral/flutter_lldbinit @@ -0,0 +1,5 @@ +# +# Generated file, do not edit. +# + +command script import --relative-to-command-file flutter_lldb_helper.py diff --git a/lib/screens/add_exercise_screen.dart b/lib/screens/add_exercise_screen.dart index 83dd447ed..52b9382a8 100644 --- a/lib/screens/add_exercise_screen.dart +++ b/lib/screens/add_exercise_screen.dart @@ -11,8 +11,8 @@ import 'package:wger/providers/user.dart'; import 'package:wger/screens/exercise_screen.dart'; import 'package:wger/widgets/add_exercise/steps/step_1_basics.dart'; import 'package:wger/widgets/add_exercise/steps/step_2_variations.dart'; -import 'package:wger/widgets/add_exercise/steps/step_3_description.dart'; -import 'package:wger/widgets/add_exercise/steps/step_4_translations.dart'; +import 'package:wger/widgets/add_exercise/steps/step_3_description.dart' as step3; +import 'package:wger/widgets/add_exercise/steps/step_4_translations.dart' as step4; import 'package:wger/widgets/add_exercise/steps/step_5_images.dart'; import 'package:wger/widgets/add_exercise/steps/step_6_overview.dart'; import 'package:wger/widgets/core/app_bar.dart'; @@ -46,7 +46,9 @@ class _AddExerciseStepperState extends State { int _currentStep = 0; int lastStepIndex = AddExerciseStepper.STEPS_IN_FORM - 1; bool _isLoading = false; + bool _isValidating = false; Widget errorWidget = const SizedBox.shrink(); + WgerHttpException? _validationError; final List> _keys = [ GlobalKey(), @@ -57,16 +59,63 @@ class _AddExerciseStepperState extends State { GlobalKey(), ]; + Future _validateLanguageOnServer(BuildContext context) async { + final addExerciseProvider = context.read(); + + try { + if (_currentStep == 2) { + await addExerciseProvider.validateLanguage( + addExerciseProvider.descriptionEn ?? '', + 'en', + ); + } + + if (_currentStep == 3 && addExerciseProvider.descriptionTrans != null) { + final languageCode = addExerciseProvider.languageTranslation?.shortName ?? ''; + if (languageCode.isNotEmpty) { + await addExerciseProvider.validateLanguage( + addExerciseProvider.descriptionTrans ?? '', + languageCode, + ); + } + } + + return true; + } on WgerHttpException catch (error) { + if (mounted) { + setState(() { + _validationError = error; + }); + } + return false; + } catch (error) { + if (mounted) { + setState(() { + _validationError = WgerHttpException({'error': [error.toString()]}); + }); + } + return false; + } + } + Widget _controlsBuilder(BuildContext context, ControlsDetails details) { return Column( children: [ const SizedBox(height: 10), + + if (_validationError != null && _currentStep != lastStepIndex) + Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: FormHttpErrorsWidget(_validationError!), + ), + if (_currentStep == lastStepIndex) errorWidget, + Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ OutlinedButton( - onPressed: details.onStepCancel, + onPressed: _isValidating ? null : details.onStepCancel, child: Text(AppLocalizations.of(context).previous), ), @@ -76,70 +125,112 @@ class _AddExerciseStepperState extends State { onPressed: _isLoading ? null : () async { - setState(() { - _isLoading = true; - errorWidget = const SizedBox.shrink(); - }); - final addExerciseProvider = context.read(); - final exerciseProvider = context.read(); - - Exercise? exercise; - try { - final exerciseId = await addExerciseProvider.postExerciseToServer(); - exercise = await exerciseProvider.fetchAndSetExercise(exerciseId); - } on WgerHttpException catch (error) { - if (context.mounted) { - setState(() { - errorWidget = FormHttpErrorsWidget(error); - }); - } - } finally { - if (mounted) { - setState(() { - _isLoading = false; - }); - } - } - - if (exercise == null || !context.mounted) { - return; - } - - final name = exercise - .getTranslation(Localizations.localeOf(context).languageCode) - .name; - - return showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - title: Text(AppLocalizations.of(context).success), - content: Text(AppLocalizations.of(context).cacheWarning), - actions: [ - TextButton( - child: Text(name), - onPressed: () { - Navigator.of(context).pop(); - Navigator.pushReplacementNamed( - context, - ExerciseDetailScreen.routeName, - arguments: exercise, - ); - }, - ), - ], - ); - }, - ); - }, + setState(() { + _isLoading = true; + errorWidget = const SizedBox.shrink(); + }); + final addExerciseProvider = context.read(); + final exerciseProvider = context.read(); + + Exercise? exercise; + try { + final exerciseId = await addExerciseProvider.postExerciseToServer(); + exercise = await exerciseProvider.fetchAndSetExercise(exerciseId); + } on WgerHttpException catch (error) { + if (context.mounted) { + setState(() { + errorWidget = FormHttpErrorsWidget(error); + }); + } + } finally { + if (mounted) { + setState(() { + _isLoading = false; + }); + } + } + + if (exercise == null || !context.mounted) { + return; + } + + final name = exercise + .getTranslation(Localizations.localeOf(context).languageCode) + .name; + + return showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text(AppLocalizations.of(context).success), + content: Text(AppLocalizations.of(context).cacheWarning), + actions: [ + TextButton( + child: Text(name), + onPressed: () { + Navigator.of(context).pop(); + Navigator.pushReplacementNamed( + context, + ExerciseDetailScreen.routeName, + arguments: exercise, + ); + }, + ), + ], + ); + }, + ); + }, child: _isLoading ? const SizedBox(height: 20, width: 20, child: CircularProgressIndicator()) : Text(AppLocalizations.of(context).save), ) else ElevatedButton( - onPressed: details.onStepContinue, - child: Text(AppLocalizations.of(context).next), + onPressed: _isValidating + ? null + : () async { + setState(() { + _validationError = null; + }); + + if (!(_keys[_currentStep].currentState?.validate() ?? false)) { + return; + } + + _keys[_currentStep].currentState?.save(); + + if (_currentStep == 2 || _currentStep == 3) { + setState(() { + _isValidating = true; + }); + + final isValid = await _validateLanguageOnServer(context); + + if (mounted) { + setState(() { + _isValidating = false; + }); + } + + if (!isValid) { + return; + } + } + + if (_currentStep != lastStepIndex) { + setState(() { + _currentStep += 1; + }); + } + }, + child: _isValidating + ? const SizedBox( + height: 20, + width: 20, + child: CircularProgressIndicator(strokeWidth: 2), + ) + : Text(AppLocalizations.of(context).next), ), ], ), @@ -164,42 +255,29 @@ class _AddExerciseStepperState extends State { ), Step( title: Text(AppLocalizations.of(context).description), - content: Step3Description(formkey: _keys[2]), + content: step3.Step3Description(formkey: _keys[2]), ), Step( title: Text(AppLocalizations.of(context).translation), - content: Step4Translation(formkey: _keys[3]), + content: step4.Step4Translation(formkey: _keys[3]), ), Step( title: Text(AppLocalizations.of(context).images), content: Step5Images(formkey: _keys[4]), ), - Step(title: Text(AppLocalizations.of(context).overview), content: Step6Overview()), + Step( + title: Text(AppLocalizations.of(context).overview), + content: Step6Overview(), + ), ], currentStep: _currentStep, - onStepContinue: () { - if (_keys[_currentStep].currentState?.validate() ?? false) { - _keys[_currentStep].currentState?.save(); - - if (_currentStep != lastStepIndex) { - setState(() { - _currentStep += 1; - }); - } - } - }, + onStepContinue: null, // Použijeme vlastnú logiku v _controlsBuilder onStepCancel: () => setState(() { if (_currentStep != 0) { _currentStep -= 1; + _validationError = null; // Resetovať chybu pri návrate } }), - /* - onStepTapped: (int index) { - setState(() { - _currentStep = index; - }); - }, - */ ), ); } @@ -256,4 +334,4 @@ class EmailNotVerified extends StatelessWidget { ), ); } -} +} \ No newline at end of file diff --git a/lib/widgets/add_exercise/steps/step_3_description.dart b/lib/widgets/add_exercise/steps/step_3_description.dart index 2905df173..6d2f2b2f2 100644 --- a/lib/widgets/add_exercise/steps/step_3_description.dart +++ b/lib/widgets/add_exercise/steps/step_3_description.dart @@ -20,7 +20,9 @@ class Step3Description extends StatelessWidget { child: Column( children: [ AddExerciseTextArea( - onChange: (value) => {}, + onChange: (value) => { + addExerciseProvider.descriptionEn = value + }, title: '${i18n.description}*', helperText: i18n.enterTextInLanguage, isMultiline: true, @@ -31,4 +33,4 @@ class Step3Description extends StatelessWidget { ), ); } -} +} \ No newline at end of file diff --git a/lib/widgets/add_exercise/steps/step_4_translations.dart b/lib/widgets/add_exercise/steps/step_4_translations.dart index d8f4a778f..01d8b30c8 100644 --- a/lib/widgets/add_exercise/steps/step_4_translations.dart +++ b/lib/widgets/add_exercise/steps/step_4_translations.dart @@ -83,17 +83,19 @@ class _Step4TranslationState extends State { return null; }, onSaved: (String? alternateName) => - addExerciseProvider.alternateNamesTrans = alternateName!.split('\n'), + addExerciseProvider.alternateNamesTrans = alternateName!.split('\n'), ), Consumer( builder: (ctx, provider, __) => AddExerciseTextArea( - onChange: (value) => {}, + onChange: (value) { + addExerciseProvider.descriptionTrans = value; + }, title: '${i18n.description}*', helperText: i18n.enterTextInLanguage, isMultiline: true, validator: (name) => validateExerciseDescription(name, context), onSaved: (String? description) => - addExerciseProvider.descriptionTrans = description!, + addExerciseProvider.descriptionTrans = description!, ), ), ], @@ -102,4 +104,4 @@ class _Step4TranslationState extends State { ), ); } -} +} \ No newline at end of file diff --git a/test/widgets/add_exercise/add_exercise_screen_test.dart b/test/widgets/add_exercise/add_exercise_screen_test.dart new file mode 100644 index 000000000..a2d1f14b3 --- /dev/null +++ b/test/widgets/add_exercise/add_exercise_screen_test.dart @@ -0,0 +1,183 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; +import 'package:mockito/annotations.dart'; +import 'package:wger/exceptions/http_exception.dart'; +import 'package:wger/providers/add_exercise.dart'; + +@GenerateMocks([AddExerciseProvider]) +import 'add_exercise_screen_test.mocks.dart'; + +void main() { + late MockAddExerciseProvider mockProvider; + + setUp(() { + mockProvider = MockAddExerciseProvider(); + }); + + group('validateLanguage - English description (step 2)', () { + test('calls validateLanguage with English text and "en" code', () async { + + const descriptionText = 'Push-ups are a basic exercise for upper body strength.'; + when(mockProvider.validateLanguage(descriptionText, 'en')) + .thenAnswer((_) async => true); + + await mockProvider.validateLanguage(descriptionText, 'en'); + + verify(mockProvider.validateLanguage(descriptionText, 'en')).called(1); + }); + + test('throws WgerHttpException when validation fails', () async { + const descriptionText = 'Deutscher Text in English field'; + final exception = WgerHttpException({ + 'text': ['The detected language does not match the expected language.'] + }); + + when(mockProvider.validateLanguage(descriptionText, 'en')) + .thenThrow(exception); + + expect( + () => mockProvider.validateLanguage(descriptionText, 'en'), + throwsA(isA()), + ); + }); + + test('validates with correct language code "en"', () async { + const text = 'This is an English description.'; + when(mockProvider.validateLanguage(text, 'en')) + .thenAnswer((_) async => true); + + final result = await mockProvider.validateLanguage(text, 'en'); + + expect(result, isTrue); + verify(mockProvider.validateLanguage(text, 'en')).called(1); + }); + }); + + group('validateLanguage - Translation (step 3)', () { + test('calls validateLanguage with German text and "de" code', () async { + const translationText = 'Liegestütze sind eine Grundübung für den Oberkörper.'; + when(mockProvider.validateLanguage(translationText, 'de')) + .thenAnswer((_) async => true); + + await mockProvider.validateLanguage(translationText, 'de'); + + verify(mockProvider.validateLanguage(translationText, 'de')).called(1); + }); + + test('calls validateLanguage with Slovak text and "sk" code', () async { + const translationText = 'Kliky sú základné cvičenie pre hornú časť tela.'; + when(mockProvider.validateLanguage(translationText, 'sk')) + .thenAnswer((_) async => true); + + await mockProvider.validateLanguage(translationText, 'sk'); + + verify(mockProvider.validateLanguage(translationText, 'sk')).called(1); + }); + + test('uses correct language code from Language model', () async { + const text = 'Deutscher Text'; + const languageCode = 'de'; + + when(mockProvider.validateLanguage(text, languageCode)) + .thenAnswer((_) async => true); + + await mockProvider.validateLanguage(text, languageCode); + + verify(mockProvider.validateLanguage(text, languageCode)).called(1); + verifyNever(mockProvider.validateLanguage(text, 'en')); + }); + }); + + group('validateLanguage - Edge cases', () { + test('handles empty text', () async { + when(mockProvider.validateLanguage('', 'en')) + .thenAnswer((_) async => true); + + await mockProvider.validateLanguage('', 'en'); + + verify(mockProvider.validateLanguage('', 'en')).called(1); + }); + + test('handles text with special characters', () async { + const text = 'Übung für Körper & Geist (100% Erfolg)'; + when(mockProvider.validateLanguage(text, 'de')) + .thenAnswer((_) async => true); + + await mockProvider.validateLanguage(text, 'de'); + + verify(mockProvider.validateLanguage(text, 'de')).called(1); + }); + + test('handles long text', () async { + const longText = ''' +Push-ups are a fundamental bodyweight exercise that strengthens the upper body, +especially the chest, shoulders, and triceps. The movement involves supporting +the body on hands and feet while keeping it straight. It improves muscular +strength, core stability, endurance, and overall physical fitness. +'''; + when(mockProvider.validateLanguage(longText, 'en')) + .thenAnswer((_) async => true); + + await mockProvider.validateLanguage(longText, 'en'); + + verify(mockProvider.validateLanguage(longText, 'en')).called(1); + }); + }); + + group('validateLanguage - Error responses', () { + test('WgerHttpException is thrown for invalid language', () { + final exception = WgerHttpException({ + 'text': ['The detected language does not match the expected language.'] + }); + + expect(exception, isA()); + }); + + test('throws WgerHttpException on validation failure', () async { + const text = 'Wrong language text'; + final exception = WgerHttpException({ + 'text': ['Language mismatch error'] + }); + + when(mockProvider.validateLanguage(text, 'en')).thenThrow(exception); + + expect( + () => mockProvider.validateLanguage(text, 'en'), + throwsA(isA()), + ); + }); + }); + + group('validateLanguage - Multiple language codes', () { + test('validates different language codes correctly', () async { + when(mockProvider.validateLanguage('English text', 'en')) + .thenAnswer((_) async => true); + await mockProvider.validateLanguage('English text', 'en'); + verify(mockProvider.validateLanguage('English text', 'en')).called(1); + + when(mockProvider.validateLanguage('Deutscher Text', 'de')) + .thenAnswer((_) async => true); + await mockProvider.validateLanguage('Deutscher Text', 'de'); + verify(mockProvider.validateLanguage('Deutscher Text', 'de')).called(1); + + when(mockProvider.validateLanguage('Slovenský text', 'sk')) + .thenAnswer((_) async => true); + await mockProvider.validateLanguage('Slovenský text', 'sk'); + verify(mockProvider.validateLanguage('Slovenský text', 'sk')).called(1); + }); + + test('does not cross-validate wrong language combinations', () async { + when(mockProvider.validateLanguage('Deutscher Text', 'de')) + .thenAnswer((_) async => true); + + await mockProvider.validateLanguage('Deutscher Text', 'de'); + + verify(mockProvider.validateLanguage('Deutscher Text', 'de')).called(1); + + verifyNever(mockProvider.validateLanguage('Deutscher Text', 'en')); + verifyNever(mockProvider.validateLanguage('Deutscher Text', 'sk')); + }); + }); + + +} \ No newline at end of file diff --git a/test/widgets/add_exercise/add_exercise_screen_test.mocks.dart b/test/widgets/add_exercise/add_exercise_screen_test.mocks.dart new file mode 100644 index 000000000..9bb98461b --- /dev/null +++ b/test/widgets/add_exercise/add_exercise_screen_test.mocks.dart @@ -0,0 +1,320 @@ +// Mocks generated by Mockito 5.4.6 from annotations +// in wger/test/widgets/add_exercise/add_exercise_screen_test.dart. +// Do not manually edit this file. + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'dart:async' as _i12; +import 'dart:ui' as _i13; + +import 'package:mockito/mockito.dart' as _i1; +import 'package:mockito/src/dummies.dart' as _i6; +import 'package:wger/models/exercises/category.dart' as _i11; +import 'package:wger/models/exercises/equipment.dart' as _i7; +import 'package:wger/models/exercises/exercise_submission.dart' as _i9; +import 'package:wger/models/exercises/exercise_submission_images.dart' as _i5; +import 'package:wger/models/exercises/language.dart' as _i10; +import 'package:wger/models/exercises/muscle.dart' as _i8; +import 'package:wger/models/exercises/variation.dart' as _i3; +import 'package:wger/providers/add_exercise.dart' as _i4; +import 'package:wger/providers/base_provider.dart' as _i2; + +// 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 +// ignore_for_file: invalid_use_of_internal_member + +class _FakeWgerBaseProvider_0 extends _i1.SmartFake + implements _i2.WgerBaseProvider { + _FakeWgerBaseProvider_0(Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); +} + +class _FakeVariation_1 extends _i1.SmartFake implements _i3.Variation { + _FakeVariation_1(Object parent, Invocation parentInvocation) + : super(parent, parentInvocation); +} + +/// A class which mocks [AddExerciseProvider]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockAddExerciseProvider extends _i1.Mock + implements _i4.AddExerciseProvider { + MockAddExerciseProvider() { + _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.ExerciseSubmissionImage> get exerciseImages => + (super.noSuchMethod( + Invocation.getter(#exerciseImages), + returnValue: <_i5.ExerciseSubmissionImage>[], + ) + as List<_i5.ExerciseSubmissionImage>); + + @override + String get author => + (super.noSuchMethod( + Invocation.getter(#author), + returnValue: _i6.dummyValue( + this, + Invocation.getter(#author), + ), + ) + as String); + + @override + List get alternateNamesEn => + (super.noSuchMethod( + Invocation.getter(#alternateNamesEn), + returnValue: [], + ) + as List); + + @override + List get alternateNamesTrans => + (super.noSuchMethod( + Invocation.getter(#alternateNamesTrans), + returnValue: [], + ) + as List); + + @override + List<_i7.Equipment> get equipment => + (super.noSuchMethod( + Invocation.getter(#equipment), + returnValue: <_i7.Equipment>[], + ) + as List<_i7.Equipment>); + + @override + bool get newVariation => + (super.noSuchMethod(Invocation.getter(#newVariation), returnValue: false) + as bool); + + @override + _i3.Variation get variation => + (super.noSuchMethod( + Invocation.getter(#variation), + returnValue: _FakeVariation_1(this, Invocation.getter(#variation)), + ) + as _i3.Variation); + + @override + List<_i8.Muscle> get primaryMuscles => + (super.noSuchMethod( + Invocation.getter(#primaryMuscles), + returnValue: <_i8.Muscle>[], + ) + as List<_i8.Muscle>); + + @override + List<_i8.Muscle> get secondaryMuscles => + (super.noSuchMethod( + Invocation.getter(#secondaryMuscles), + returnValue: <_i8.Muscle>[], + ) + as List<_i8.Muscle>); + + @override + _i9.ExerciseSubmissionApi get exerciseApiObject => + (super.noSuchMethod( + Invocation.getter(#exerciseApiObject), + returnValue: _i6.dummyValue<_i9.ExerciseSubmissionApi>( + this, + Invocation.getter(#exerciseApiObject), + ), + ) + as _i9.ExerciseSubmissionApi); + + @override + set author(String? value) => super.noSuchMethod( + Invocation.setter(#author, value), + returnValueForMissingStub: null, + ); + + @override + set exerciseNameEn(String? value) => super.noSuchMethod( + Invocation.setter(#exerciseNameEn, value), + returnValueForMissingStub: null, + ); + + @override + set exerciseNameTrans(String? value) => super.noSuchMethod( + Invocation.setter(#exerciseNameTrans, value), + returnValueForMissingStub: null, + ); + + @override + set descriptionEn(String? value) => super.noSuchMethod( + Invocation.setter(#descriptionEn, value), + returnValueForMissingStub: null, + ); + + @override + set descriptionTrans(String? value) => super.noSuchMethod( + Invocation.setter(#descriptionTrans, value), + returnValueForMissingStub: null, + ); + + @override + set languageEn(_i10.Language? value) => super.noSuchMethod( + Invocation.setter(#languageEn, value), + returnValueForMissingStub: null, + ); + + @override + set languageTranslation(_i10.Language? value) => super.noSuchMethod( + Invocation.setter(#languageTranslation, value), + returnValueForMissingStub: null, + ); + + @override + set alternateNamesEn(List? value) => super.noSuchMethod( + Invocation.setter(#alternateNamesEn, value), + returnValueForMissingStub: null, + ); + + @override + set alternateNamesTrans(List? value) => super.noSuchMethod( + Invocation.setter(#alternateNamesTrans, value), + returnValueForMissingStub: null, + ); + + @override + set category(_i11.ExerciseCategory? value) => super.noSuchMethod( + Invocation.setter(#category, value), + returnValueForMissingStub: null, + ); + + @override + set equipment(List<_i7.Equipment>? equipment) => super.noSuchMethod( + Invocation.setter(#equipment, equipment), + returnValueForMissingStub: null, + ); + + @override + set variationConnectToExercise(int? value) => super.noSuchMethod( + Invocation.setter(#variationConnectToExercise, value), + returnValueForMissingStub: null, + ); + + @override + set variationId(int? variation) => super.noSuchMethod( + Invocation.setter(#variationId, variation), + returnValueForMissingStub: null, + ); + + @override + set primaryMuscles(List<_i8.Muscle>? muscles) => super.noSuchMethod( + Invocation.setter(#primaryMuscles, muscles), + returnValueForMissingStub: null, + ); + + @override + set secondaryMuscles(List<_i8.Muscle>? muscles) => super.noSuchMethod( + Invocation.setter(#secondaryMuscles, muscles), + 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 + void addExerciseImages(List<_i5.ExerciseSubmissionImage>? images) => + super.noSuchMethod( + Invocation.method(#addExerciseImages, [images]), + returnValueForMissingStub: null, + ); + + @override + void removeImage(String? path) => super.noSuchMethod( + Invocation.method(#removeImage, [path]), + returnValueForMissingStub: null, + ); + + @override + _i12.Future postExerciseToServer() => + (super.noSuchMethod( + Invocation.method(#postExerciseToServer, []), + returnValue: _i12.Future.value(0), + ) + as _i12.Future); + + @override + _i12.Future addExerciseSubmission() => + (super.noSuchMethod( + Invocation.method(#addExerciseSubmission, []), + returnValue: _i12.Future.value(0), + ) + as _i12.Future); + + @override + _i12.Future addImages(int? exerciseId) => + (super.noSuchMethod( + Invocation.method(#addImages, [exerciseId]), + returnValue: _i12.Future.value(), + returnValueForMissingStub: _i12.Future.value(), + ) + as _i12.Future); + + @override + _i12.Future validateLanguage(String? input, String? languageCode) => + (super.noSuchMethod( + Invocation.method(#validateLanguage, [input, languageCode]), + returnValue: _i12.Future.value(false), + ) + as _i12.Future); + + @override + void addListener(_i13.VoidCallback? listener) => super.noSuchMethod( + Invocation.method(#addListener, [listener]), + returnValueForMissingStub: null, + ); + + @override + void removeListener(_i13.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, + ); +}