diff --git a/test/exercises/contribute_exercise_test.dart b/test/exercises/contribute_exercise_test.dart index ee3380e6..de9edea5 100644 --- a/test/exercises/contribute_exercise_test.dart +++ b/test/exercises/contribute_exercise_test.dart @@ -123,114 +123,114 @@ void main() { // ============================================================================ group('Form Field Validation Tests', () { - testWidgets('Exercise name field is required and displays validation error', - (WidgetTester tester) async { - // Setup: Create verified user with required data - setupVerifiedUser(); + testWidgets('Exercise name field is required and displays validation error', ( + WidgetTester tester, + ) async { + // Setup: Create verified user with required data + setupVerifiedUser(); - // Build the exercise contribution screen - await tester.pumpWidget(createExerciseScreen()); - await tester.pumpAndSettle(); + // Build the exercise contribution screen + await tester.pumpWidget(createExerciseScreen()); + await tester.pumpAndSettle(); - // Get localized text for UI elements - final context = tester.element(find.byType(Stepper)); - final l10n = AppLocalizations.of(context); + // Get localized text for UI elements + final context = tester.element(find.byType(Stepper)); + final l10n = AppLocalizations.of(context); - // Find the Next button (use .first since there are 6 steps with 6 Next buttons) - final nextButton = find.widgetWithText(ElevatedButton, l10n.next).first; - expect(nextButton, findsOneWidget); + // Find the Next button (use .first since there are 6 steps with 6 Next buttons) + final nextButton = find.widgetWithText(ElevatedButton, l10n.next).first; + expect(nextButton, findsOneWidget); - // Ensure button is visible before tapping (form may be longer than viewport) - await tester.ensureVisible(nextButton); - await tester.pumpAndSettle(); + // Ensure button is visible before tapping (form may be longer than viewport) + await tester.ensureVisible(nextButton); + await tester.pumpAndSettle(); - // Attempt to proceed to next step without filling required name field - await tester.tap(nextButton); - await tester.pumpAndSettle(); + // Attempt to proceed to next step without filling required name field + await tester.tap(nextButton); + await tester.pumpAndSettle(); - // Verify that validation prevented navigation (still on step 0) - final stepper = tester.widget(find.byType(Stepper)); - expect(stepper.currentStep, equals(0)); - }); + // Verify that validation prevented navigation (still on step 0) + final stepper = tester.widget(find.byType(Stepper)); + expect(stepper.currentStep, equals(0)); + }); - testWidgets('User can enter exercise name in text field', - (WidgetTester tester) async { - // Setup: Create verified user - setupVerifiedUser(); + testWidgets('User can enter exercise name in text field', (WidgetTester tester) async { + // Setup: Create verified user + setupVerifiedUser(); - // Build the exercise contribution screen - await tester.pumpWidget(createExerciseScreen()); - await tester.pumpAndSettle(); + // Build the exercise contribution screen + await tester.pumpWidget(createExerciseScreen()); + await tester.pumpAndSettle(); - // Find the first text field (exercise name field) - final nameField = find.byType(TextFormField).first; - expect(nameField, findsOneWidget); + // Find the first text field (exercise name field) + final nameField = find.byType(TextFormField).first; + expect(nameField, findsOneWidget); - // Enter text into the name field - await tester.enterText(nameField, 'Bench Press'); - await tester.pumpAndSettle(); + // Enter text into the name field + await tester.enterText(nameField, 'Bench Press'); + await tester.pumpAndSettle(); - // Verify that the entered text is displayed - expect(find.text('Bench Press'), findsOneWidget); - }); + // Verify that the entered text is displayed + expect(find.text('Bench Press'), findsOneWidget); + }); - testWidgets('Alternative names field accepts multiple lines of text', - (WidgetTester tester) async { - // Setup: Create verified user - setupVerifiedUser(); + testWidgets('Alternative names field accepts multiple lines of text', ( + WidgetTester tester, + ) async { + // Setup: Create verified user + setupVerifiedUser(); - // Build the exercise contribution screen - await tester.pumpWidget(createExerciseScreen()); - await tester.pumpAndSettle(); + // Build the exercise contribution screen + await tester.pumpWidget(createExerciseScreen()); + await tester.pumpAndSettle(); - // Find all text fields - final textFields = find.byType(TextFormField); - expect(textFields, findsWidgets); + // Find all text fields + final textFields = find.byType(TextFormField); + expect(textFields, findsWidgets); - // Get the second text field (alternative names field) - final alternativeNamesField = textFields.at(1); + // Get the second text field (alternative names field) + final alternativeNamesField = textFields.at(1); - // Enter multi-line text with newline character - await tester.enterText(alternativeNamesField, 'Chest Press\nFlat Bench Press'); - await tester.pumpAndSettle(); + // Enter multi-line text with newline character + await tester.enterText(alternativeNamesField, 'Chest Press\nFlat Bench Press'); + await tester.pumpAndSettle(); - // Verify that multi-line text was accepted and is displayed - expect(find.text('Chest Press\nFlat Bench Press'), findsOneWidget); - }); + // Verify that multi-line text was accepted and is displayed + expect(find.text('Chest Press\nFlat Bench Press'), findsOneWidget); + }); - testWidgets('Category dropdown is required for form submission', - (WidgetTester tester) async { - // Setup: Create verified user - setupVerifiedUser(); + testWidgets('Category dropdown is required for form submission', (WidgetTester tester) async { + // Setup: Create verified user + setupVerifiedUser(); - // Build the exercise contribution screen - await tester.pumpWidget(createExerciseScreen()); - await tester.pumpAndSettle(); + // Build the exercise contribution screen + await tester.pumpWidget(createExerciseScreen()); + await tester.pumpAndSettle(); - // Fill the name field (to isolate category validation) - final nameField = find.byType(TextFormField).first; - await tester.enterText(nameField, 'Test Exercise'); - await tester.pumpAndSettle(); + // Fill the name field (to isolate category validation) + final nameField = find.byType(TextFormField).first; + await tester.enterText(nameField, 'Test Exercise'); + await tester.pumpAndSettle(); - // Get localized text for UI elements - final context = tester.element(find.byType(Stepper)); - final l10n = AppLocalizations.of(context); + // Get localized text for UI elements + final context = tester.element(find.byType(Stepper)); + final l10n = AppLocalizations.of(context); - // Find the Next button - final nextButton = find.widgetWithText(ElevatedButton, l10n.next).first; + // Find the Next button + final nextButton = find.widgetWithText(ElevatedButton, l10n.next).first; - // Ensure button is visible before tapping - await tester.ensureVisible(nextButton); - await tester.pumpAndSettle(); + // Ensure button is visible before tapping + await tester.ensureVisible(nextButton); + await tester.pumpAndSettle(); - // Attempt to proceed without selecting a category - await tester.tap(nextButton); - await tester.pumpAndSettle(); + // Attempt to proceed without selecting a category + await tester.tap(nextButton); + await tester.pumpAndSettle(); - // Verify that validation prevented navigation (still on step 0) - final stepper = tester.widget(find.byType(Stepper)); - expect(stepper.currentStep, equals(0)); - }); + // Verify that validation prevented navigation (still on step 0) + final stepper = tester.widget(find.byType(Stepper)); + expect(stepper.currentStep, equals(0)); + }); }); // ============================================================================ @@ -241,50 +241,47 @@ void main() { // ============================================================================ group('Form Navigation and Data Persistence Tests', () { - testWidgets('Form data persists when navigating between steps', - (WidgetTester tester) async { - // Setup: Create verified user - setupVerifiedUser(); + testWidgets('Form data persists when navigating between steps', (WidgetTester tester) async { + // Setup: Create verified user + setupVerifiedUser(); - // Build the exercise contribution screen - await tester.pumpWidget(createExerciseScreen()); - await tester.pumpAndSettle(); + // Build the exercise contribution screen + await tester.pumpWidget(createExerciseScreen()); + await tester.pumpAndSettle(); - // Enter text in the name field - final nameField = find.byType(TextFormField).first; - await tester.enterText(nameField, 'Test Exercise'); - await tester.pumpAndSettle(); + // Enter text in the name field + final nameField = find.byType(TextFormField).first; + await tester.enterText(nameField, 'Test Exercise'); + await tester.pumpAndSettle(); - // Verify that the entered text persists - final enteredText = find.text('Test Exercise'); - expect(enteredText, findsOneWidget); - }); + // Verify that the entered text persists + final enteredText = find.text('Test Exercise'); + expect(enteredText, findsOneWidget); + }); - testWidgets('Previous button navigates back to previous step', - (WidgetTester tester) async { - // Setup: Create verified user - setupVerifiedUser(); + testWidgets('Previous button navigates back to previous step', (WidgetTester tester) async { + // Setup: Create verified user + setupVerifiedUser(); - // Build the exercise contribution screen - await tester.pumpWidget(createExerciseScreen()); - await tester.pumpAndSettle(); + // Build the exercise contribution screen + await tester.pumpWidget(createExerciseScreen()); + await tester.pumpAndSettle(); - // Verify initial step is 0 - var stepper = tester.widget(find.byType(Stepper)); - expect(stepper.currentStep, equals(0)); + // Verify initial step is 0 + var stepper = tester.widget(find.byType(Stepper)); + expect(stepper.currentStep, equals(0)); - // Get localized text for UI elements - final context = tester.element(find.byType(Stepper)); - final l10n = AppLocalizations.of(context); + // Get localized text for UI elements + final context = tester.element(find.byType(Stepper)); + final l10n = AppLocalizations.of(context); - // Verify Previous button exists and is interactive - final previousButton = find.widgetWithText(OutlinedButton, l10n.previous); - expect(previousButton, findsOneWidget); - - final button = tester.widget(previousButton); - expect(button.onPressed, isNotNull); - }); + // Verify Previous button exists and is interactive + final previousButton = find.widgetWithText(OutlinedButton, l10n.previous); + expect(previousButton, findsOneWidget); + final button = tester.widget(previousButton); + expect(button.onPressed, isNotNull); + }); }); // ============================================================================ @@ -295,44 +292,42 @@ void main() { // ============================================================================ group('Dropdown Selection Tests', () { - testWidgets('Category selection widgets exist in form', - (WidgetTester tester) async { - // Setup: Create verified user with categories data - setupVerifiedUser(); + testWidgets('Category selection widgets exist in form', (WidgetTester tester) async { + // Setup: Create verified user with categories data + setupVerifiedUser(); - // Build the exercise contribution screen - await tester.pumpWidget(createExerciseScreen()); - await tester.pumpAndSettle(); + // Build the exercise contribution screen + await tester.pumpWidget(createExerciseScreen()); + await tester.pumpAndSettle(); - // Verify that the stepper structure is present - expect(find.byType(AddExerciseStepper), findsOneWidget); - expect(find.byType(Stepper), findsOneWidget); + // Verify that the stepper structure is present + expect(find.byType(AddExerciseStepper), findsOneWidget); + expect(find.byType(Stepper), findsOneWidget); - // Verify that Step1Basics is loaded (contains category selection) - final stepper = tester.widget(find.byType(Stepper)); - expect(stepper.steps.length, equals(6)); - expect(stepper.steps[0].content.runtimeType.toString(), contains('Step1Basics')); - }); + // Verify that Step1Basics is loaded (contains category selection) + final stepper = tester.widget(find.byType(Stepper)); + expect(stepper.steps.length, equals(6)); + expect(stepper.steps[0].content.runtimeType.toString(), contains('Step1Basics')); + }); - testWidgets('Form contains multiple selection fields', - (WidgetTester tester) async { - // Setup: Create verified user with all required data - setupVerifiedUser(); + testWidgets('Form contains multiple selection fields', (WidgetTester tester) async { + // Setup: Create verified user with all required data + setupVerifiedUser(); - // Build the exercise contribution screen - await tester.pumpWidget(createExerciseScreen()); - await tester.pumpAndSettle(); + // Build the exercise contribution screen + await tester.pumpWidget(createExerciseScreen()); + await tester.pumpAndSettle(); - // Verify that the stepper structure exists - expect(find.byType(Stepper), findsOneWidget); + // Verify that the stepper structure exists + expect(find.byType(Stepper), findsOneWidget); - // Verify all 6 steps are present - final stepper = tester.widget(find.byType(Stepper)); - expect(stepper.steps.length, equals(6)); + // Verify all 6 steps are present + final stepper = tester.widget(find.byType(Stepper)); + expect(stepper.steps.length, equals(6)); - // Verify text form fields exist (for name, description, etc.) - expect(find.byType(TextFormField), findsWidgets); - }); + // Verify text form fields exist (for name, description, etc.) + expect(find.byType(TextFormField), findsWidgets); + }); }); // ============================================================================ @@ -343,47 +338,44 @@ void main() { // ============================================================================ group('Provider Integration Tests', () { - testWidgets('Selecting category updates provider state', - (WidgetTester tester) async { - // Setup: Create verified user - setupVerifiedUser(); + testWidgets('Selecting category updates provider state', (WidgetTester tester) async { + // Setup: Create verified user + setupVerifiedUser(); - // Build the exercise contribution screen - await tester.pumpWidget(createExerciseScreen()); - await tester.pumpAndSettle(); + // Build the exercise contribution screen + await tester.pumpWidget(createExerciseScreen()); + await tester.pumpAndSettle(); - // Verify that categories were loaded from provider - verify(mockExerciseProvider.categories).called(greaterThan(0)); - }); + // Verify that categories were loaded from provider + verify(mockExerciseProvider.categories).called(greaterThan(0)); + }); - testWidgets('Selecting muscles updates provider state', - (WidgetTester tester) async { - // Setup: Create verified user - setupVerifiedUser(); + testWidgets('Selecting muscles updates provider state', (WidgetTester tester) async { + // Setup: Create verified user + setupVerifiedUser(); - // Build the exercise contribution screen - await tester.pumpWidget(createExerciseScreen()); - await tester.pumpAndSettle(); + // Build the exercise contribution screen + await tester.pumpWidget(createExerciseScreen()); + await tester.pumpAndSettle(); - // Verify that muscle data was loaded from providers - verify(mockExerciseProvider.muscles).called(greaterThan(0)); - verify(mockAddExerciseProvider.primaryMuscles).called(greaterThan(0)); - verify(mockAddExerciseProvider.secondaryMuscles).called(greaterThan(0)); - }); + // Verify that muscle data was loaded from providers + verify(mockExerciseProvider.muscles).called(greaterThan(0)); + verify(mockAddExerciseProvider.primaryMuscles).called(greaterThan(0)); + verify(mockAddExerciseProvider.secondaryMuscles).called(greaterThan(0)); + }); - testWidgets('Equipment list is retrieved from provider', - (WidgetTester tester) async { - // Setup: Create verified user - setupVerifiedUser(); + testWidgets('Equipment list is retrieved from provider', (WidgetTester tester) async { + // Setup: Create verified user + setupVerifiedUser(); - // Build the exercise contribution screen - await tester.pumpWidget(createExerciseScreen()); - await tester.pumpAndSettle(); + // Build the exercise contribution screen + await tester.pumpWidget(createExerciseScreen()); + await tester.pumpAndSettle(); - // Verify that equipment data was loaded from providers - verify(mockExerciseProvider.equipment).called(greaterThan(0)); - verify(mockAddExerciseProvider.equipment).called(greaterThan(0)); - }); + // Verify that equipment data was loaded from providers + verify(mockExerciseProvider.equipment).called(greaterThan(0)); + verify(mockAddExerciseProvider.equipment).called(greaterThan(0)); + }); }); // ============================================================================ @@ -394,59 +386,58 @@ void main() { // ============================================================================ group('Exercise Submission Tests', () { - testWidgets('Successful submission shows success dialog', - (WidgetTester tester) async { - // Setup: Create verified user and mock successful submission - setupVerifiedUser(); - when(mockAddExerciseProvider.addExercise()).thenAnswer((_) async => 1); - when(mockAddExerciseProvider.addImages(any)).thenAnswer((_) async => {}); - when(mockExerciseProvider.fetchAndSetExercise(any)) - .thenAnswer((_) async => testBenchPress); - when(mockAddExerciseProvider.clear()).thenReturn(null); + testWidgets('Successful submission shows success dialog', (WidgetTester tester) async { + // Setup: Create verified user and mock successful submission + setupVerifiedUser(); + when(mockAddExerciseProvider.addExercise()).thenAnswer((_) async => 1); + when(mockAddExerciseProvider.addImages(any)).thenAnswer((_) async => {}); + when(mockExerciseProvider.fetchAndSetExercise(any)).thenAnswer((_) async => testBenchPress); + when(mockAddExerciseProvider.clear()).thenReturn(null); - // Build the exercise contribution screen - await tester.pumpWidget(createExerciseScreen()); - await tester.pumpAndSettle(); + // Build the exercise contribution screen + await tester.pumpWidget(createExerciseScreen()); + await tester.pumpAndSettle(); - // Verify that the stepper is ready for submission (all 6 steps exist) - final stepper = tester.widget(find.byType(Stepper)); - expect(stepper.steps.length, equals(6)); - }); + // Verify that the stepper is ready for submission (all 6 steps exist) + final stepper = tester.widget(find.byType(Stepper)); + expect(stepper.steps.length, equals(6)); + }); - testWidgets('Failed submission displays error message', - (WidgetTester tester) async { - // Setup: Create verified user and mock failed submission - setupVerifiedUser(); - final httpException = WgerHttpException({'name': ['This field is required']}); - when(mockAddExerciseProvider.addExercise()).thenThrow(httpException); + testWidgets('Failed submission displays error message', (WidgetTester tester) async { + // Setup: Create verified user and mock failed submission + setupVerifiedUser(); + final httpException = WgerHttpException({ + 'name': ['This field is required'], + }); + when(mockAddExerciseProvider.addExercise()).thenThrow(httpException); - // Build the exercise contribution screen - await tester.pumpWidget(createExerciseScreen()); - await tester.pumpAndSettle(); + // Build the exercise contribution screen + await tester.pumpWidget(createExerciseScreen()); + await tester.pumpAndSettle(); - // Verify that error handling structure is in place - final stepper = tester.widget(find.byType(Stepper)); - expect(stepper.steps.length, equals(6)); - }); + // Verify that error handling structure is in place + final stepper = tester.widget(find.byType(Stepper)); + expect(stepper.steps.length, equals(6)); + }); - testWidgets('Provider clear method is called after successful submission', - (WidgetTester tester) async { - // Setup: Mock successful submission flow - setupVerifiedUser(); - when(mockAddExerciseProvider.addExercise()).thenAnswer((_) async => 1); - when(mockAddExerciseProvider.addImages(any)).thenAnswer((_) async => {}); - when(mockExerciseProvider.fetchAndSetExercise(any)) - .thenAnswer((_) async => testBenchPress); - when(mockAddExerciseProvider.clear()).thenReturn(null); + testWidgets('Provider clear method is called after successful submission', ( + WidgetTester tester, + ) async { + // Setup: Mock successful submission flow + setupVerifiedUser(); + when(mockAddExerciseProvider.addExercise()).thenAnswer((_) async => 1); + when(mockAddExerciseProvider.addImages(any)).thenAnswer((_) async => {}); + when(mockExerciseProvider.fetchAndSetExercise(any)).thenAnswer((_) async => testBenchPress); + when(mockAddExerciseProvider.clear()).thenReturn(null); - // Build the exercise contribution screen - await tester.pumpWidget(createExerciseScreen()); - await tester.pumpAndSettle(); + // Build the exercise contribution screen + await tester.pumpWidget(createExerciseScreen()); + await tester.pumpAndSettle(); - // Verify that the form structure is ready for submission - expect(find.byType(Stepper), findsOneWidget); - expect(find.byType(AddExerciseStepper), findsOneWidget); - }); + // Verify that the form structure is ready for submission + expect(find.byType(Stepper), findsOneWidget); + expect(find.byType(AddExerciseStepper), findsOneWidget); + }); }); // ============================================================================ @@ -457,60 +448,57 @@ void main() { // ============================================================================ group('Access Control Tests', () { - testWidgets('Unverified users cannot access exercise form', - (WidgetTester tester) async { - // Setup: Create unverified user (isTrustworthy = false) - tProfile1.isTrustworthy = false; - when(mockUserProvider.profile).thenReturn(tProfile1); + testWidgets('Unverified users cannot access exercise form', (WidgetTester tester) async { + // Setup: Create unverified user (isTrustworthy = false) + tProfile1.isTrustworthy = false; + when(mockUserProvider.profile).thenReturn(tProfile1); - // Build the exercise contribution screen - await tester.pumpWidget(createExerciseScreen()); - await tester.pumpAndSettle(); + // Build the exercise contribution screen + await tester.pumpWidget(createExerciseScreen()); + await tester.pumpAndSettle(); - // Verify that EmailNotVerified widget is shown instead of the form - expect(find.byType(EmailNotVerified), findsOneWidget); - expect(find.byType(AddExerciseStepper), findsNothing); - expect(find.byType(Stepper), findsNothing); - }); + // Verify that EmailNotVerified widget is shown instead of the form + expect(find.byType(EmailNotVerified), findsOneWidget); + expect(find.byType(AddExerciseStepper), findsNothing); + expect(find.byType(Stepper), findsNothing); + }); - testWidgets('Verified users can access all form fields', - (WidgetTester tester) async { - // Setup: Create verified user - setupVerifiedUser(); + testWidgets('Verified users can access all form fields', (WidgetTester tester) async { + // Setup: Create verified user + setupVerifiedUser(); - // Build the exercise contribution screen - await tester.pumpWidget(createExerciseScreen()); - await tester.pumpAndSettle(); + // Build the exercise contribution screen + await tester.pumpWidget(createExerciseScreen()); + await tester.pumpAndSettle(); - // Verify that form elements are accessible - expect(find.byType(AddExerciseStepper), findsOneWidget); - expect(find.byType(Stepper), findsOneWidget); - expect(find.byType(TextFormField), findsWidgets); + // Verify that form elements are accessible + expect(find.byType(AddExerciseStepper), findsOneWidget); + expect(find.byType(Stepper), findsOneWidget); + expect(find.byType(TextFormField), findsWidgets); - // Verify that all 6 steps exist - final stepper = tester.widget(find.byType(Stepper)); - expect(stepper.steps.length, equals(6)); - }); + // Verify that all 6 steps exist + final stepper = tester.widget(find.byType(Stepper)); + expect(stepper.steps.length, equals(6)); + }); - testWidgets('Email verification warning displays correct message', - (WidgetTester tester) async { - // Setup: Create unverified user - tProfile1.isTrustworthy = false; - when(mockUserProvider.profile).thenReturn(tProfile1); + testWidgets('Email verification warning displays correct message', (WidgetTester tester) async { + // Setup: Create unverified user + tProfile1.isTrustworthy = false; + when(mockUserProvider.profile).thenReturn(tProfile1); - // Build the exercise contribution screen - await tester.pumpWidget(createExerciseScreen()); - await tester.pumpAndSettle(); + // Build the exercise contribution screen + await tester.pumpWidget(createExerciseScreen()); + await tester.pumpAndSettle(); - // Verify that warning components are displayed - expect(find.byIcon(Icons.warning), findsOneWidget); - expect(find.byType(ListTile), findsOneWidget); + // Verify that warning components are displayed + expect(find.byIcon(Icons.warning), findsOneWidget); + expect(find.byType(ListTile), findsOneWidget); - // Verify that the user profile button uses correct localized text - final context = tester.element(find.byType(EmailNotVerified)); - final expectedText = AppLocalizations.of(context).userProfile; - final profileButton = find.widgetWithText(TextButton, expectedText); - expect(profileButton, findsOneWidget); - }); + // Verify that the user profile button uses correct localized text + final context = tester.element(find.byType(EmailNotVerified)); + final expectedText = AppLocalizations.of(context).userProfile; + final profileButton = find.widgetWithText(TextButton, expectedText); + expect(profileButton, findsOneWidget); + }); }); }