diff --git a/.gitea/workflows/test.yml b/.gitea/workflows/test.yml index 3ecff8b0..a26ff075 100644 --- a/.gitea/workflows/test.yml +++ b/.gitea/workflows/test.yml @@ -139,70 +139,70 @@ jobs: - name: Native Analyze run: flutter analyze - # - name: Test - # run: flutter test --coverage + - name: Test + run: flutter test --coverage - # - name: Test coverage - # run: | - # echo "📊 Analyzing test coverage..." - # echo "" - # awk -F: ' - # /^SF:/ { - # file = $0 - # sub(/^SF:/, "", file) - # sub(/^[[:space:]]+/, "", file) - # is_generated = 0 - # if (file ~ /\.(g|freezed|gr|mocks)\.dart$/) { - # is_generated = 1 - # generated_files++ - # } else if (file ~ /\/(build|\.dart_tool)\//) { - # is_generated = 1 - # generated_files++ - # } else { - # source_files++ - # } - # } - # /^LF:/ { - # if (!is_generated) { - # total += $2 - # } else { - # generated_lines += $2 - # } - # } - # /^LH:/ { - # if (!is_generated) { - # covered += $2 - # } else { - # generated_covered += $2 - # } - # } - # END { - # print "📁 Files:" - # printf " Source files: %d\n", source_files - # printf " Generated files: %d (excluded)\n", generated_files - # print "" - # print "📝 Lines:" - # printf " Total lines: %d\n", total - # printf " Covered lines: %d\n", covered - # printf " Uncovered lines: %d\n", total - covered - # print "" - # if (total == 0) { - # print "⚠️ Coverage: 0%" - # exit 1 - # } else { - # percentage = (covered / total) * 100 - # printf "✅ Coverage: %.2f%%\n", percentage - # print "" - # if (percentage < 60) { - # printf "⚠️ Warning: Coverage is below 60%%\n" - # } else if (percentage < 80) { - # printf "💛 Good: Coverage is above 60%%\n" - # } else { - # printf "💚 Excellent: Coverage is above 80%%\n" - # } - # } - # } - # ' coverage/lcov.info + - name: Test coverage + run: | + echo "📊 Analyzing test coverage..." + echo "" + awk -F: ' + /^SF:/ { + file = $0 + sub(/^SF:/, "", file) + sub(/^[[:space:]]+/, "", file) + is_generated = 0 + if (file ~ /\.(g|freezed|gr|mocks)\.dart$/) { + is_generated = 1 + generated_files++ + } else if (file ~ /\/(build|\.dart_tool)\//) { + is_generated = 1 + generated_files++ + } else { + source_files++ + } + } + /^LF:/ { + if (!is_generated) { + total += $2 + } else { + generated_lines += $2 + } + } + /^LH:/ { + if (!is_generated) { + covered += $2 + } else { + generated_covered += $2 + } + } + END { + print "📁 Files:" + printf " Source files: %d\n", source_files + printf " Generated files: %d (excluded)\n", generated_files + print "" + print "📝 Lines:" + printf " Total lines: %d\n", total + printf " Covered lines: %d\n", covered + printf " Uncovered lines: %d\n", total - covered + print "" + if (total == 0) { + print "⚠️ Coverage: 0%" + exit 1 + } else { + percentage = (covered / total) * 100 + printf "✅ Coverage: %.2f%%\n", percentage + print "" + if (percentage < 60) { + printf "⚠️ Warning: Coverage is below 60%%\n" + } else if (percentage < 80) { + printf "💛 Good: Coverage is above 60%%\n" + } else { + printf "💚 Excellent: Coverage is above 80%%\n" + } + } + } + ' coverage/lcov.info - name: Build apk run: | diff --git a/lib/features/create_diet/presentation/cubit/create_diet_cubit.dart b/lib/features/create_diet/presentation/cubit/create_diet_cubit.dart index 22803a76..59be7492 100644 --- a/lib/features/create_diet/presentation/cubit/create_diet_cubit.dart +++ b/lib/features/create_diet/presentation/cubit/create_diet_cubit.dart @@ -201,6 +201,7 @@ class CreateDietCubit extends Cubit { } Future getDietForPreview() async { + if (!_isFormValidForDraft()) return null; final authorProfile = await profileRepository.getMyProfile(); final imageBytes = (await state.dietData.image?.readAsBytes()) ?? []; String image = base64.encode(imageBytes); @@ -317,6 +318,11 @@ class CreateDietCubit extends Cubit { return 'validation'; } try { + final dietState = state.dietData.state; + if (dietState == null) { + emit(state.copyWith(isDietPublishing: false, dietStateError: 'Статус диеты не выбран')); + return 'validation'; + } File? file; if (state.dietData.imageUrl != null) { file = await downloadImageAsFile(state.dietData.imageUrl!); @@ -325,7 +331,6 @@ class CreateDietCubit extends Cubit { if (state.dietData.image?.path != null) { image = await ImageCompressUtility.compressFile(File(state.dietData.image!.path)); } - final dietState = state.dietData.state!; final dailySchedule = state.dietData.dailySchedule; await createDietRepository.createDiet( name: state.dietData.name ?? '', diff --git a/lib/features/create_diet/repositories/create_diet_repository.dart b/lib/features/create_diet/repositories/create_diet_repository.dart index 546d975d..825e9424 100644 --- a/lib/features/create_diet/repositories/create_diet_repository.dart +++ b/lib/features/create_diet/repositories/create_diet_repository.dart @@ -82,7 +82,12 @@ class CreateDietRepository { return MapEntry( int.parse(key), (value as Map).map((key, value) { - return MapEntry(int.parse(key), DailySchedule.fromJson(value as Map)); + final portionMap = Map.from(value as Map); + final pnvid = portionMap['product_nutritional_value_id']; + if (pnvid is String) { + portionMap['product_nutritional_value_id'] = int.tryParse(pnvid); + } + return MapEntry(int.parse(key), DailySchedule.fromJson(portionMap)); }), ); }), diff --git a/test/features/blog_post/presentation/pages/blog_post_screen_smoke_test.dart b/test/features/blog_post/presentation/pages/blog_post_screen_smoke_test.dart index 0758ab9b..027ae523 100644 --- a/test/features/blog_post/presentation/pages/blog_post_screen_smoke_test.dart +++ b/test/features/blog_post/presentation/pages/blog_post_screen_smoke_test.dart @@ -7,6 +7,8 @@ import 'package:eatvio_app/core/theme/theme_helper.dart'; import 'package:eatvio_app/features/blog_post/presentation/cubit/blog_post_cubit.dart'; import 'package:eatvio_app/features/blog_post/presentation/cubit/blog_post_state.dart'; import 'package:eatvio_app/features/blog_post/presentation/pages/blog_post_screen.dart'; +import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart'; +import 'package:eatvio_app/features/feed/models/feed_blog_post_model.dart'; import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_cubit.dart'; import 'package:eatvio_app/generated/l10n.dart'; import 'package:flutter/material.dart'; @@ -159,5 +161,49 @@ void main() { expect(find.byType(BlogPostScreen), findsOneWidget); }); }); + + testWidgets('renders loaded state with blog post model', (tester) async { + await mockNetworkImagesFor(() async { + final blogPostCubit = MockBlogPostCubit(); + final feedCubit = MockFeedCubit(); + final commentsCubit = MockCommentsCubit(); + final blogPostModel = FeedBlogPostModel( + id: 1, + title: 'Test Post Title', + description: 'Test post description', + image: 'https://example.com/image.jpg', + state: 'published', + author: const Author(authorSlug: 'author-1', authorName: 'Author Name', authorProfileIcon: 'https://example.com/icon.jpg', authorIsMe: false), + videoUrl: '', + createdAt: DateTime(2024, 1, 15), + updatedAt: DateTime(2024, 1, 15), + totalLikes: 10, + totalViews: 100, + totalComments: 5, + wasLikedByMe: false, + ); + final state = BlogPostState(isLoading: false, blogPostModel: blogPostModel, error: null); + + when(blogPostCubit.state).thenReturn(state); + when(blogPostCubit.stream).thenAnswer((_) => Stream.value(state)); + when(blogPostCubit.close()).thenAnswer((_) async => Future.value()); + when(feedCubit.viewPost(any)).thenAnswer((_) async => Future.value()); + when(feedCubit.stream).thenAnswer((_) => const Stream.empty()); + when(feedCubit.close()).thenAnswer((_) async => Future.value()); + when(commentsCubit.state).thenReturn(const CommentsState()); + when(commentsCubit.stream).thenAnswer((_) => const Stream.empty()); + when(commentsCubit.close()).thenAnswer((_) async => Future.value()); + when(commentsCubit.getComments(any, any, any)).thenAnswer((_) async => Future.value()); + + registerMocks(blogPostCubit, commentsCubit); + + await tester.pumpWidget(wrap(blogPostCubit, feedCubit, commentsCubit, const BlogPostScreen(id: 1))); + await tester.pump(); + await tester.pumpAndSettle(); + + expect(find.byType(BlogPostScreen), findsOneWidget); + expect(find.text('Test Post Title'), findsOneWidget); + }); + }); }); } diff --git a/test/features/create_diet/presentation/create_diet_screen_test.dart b/test/features/create_diet/presentation/create_diet_screen_test.dart index a9d5f44a..6f3de99c 100644 --- a/test/features/create_diet/presentation/create_diet_screen_test.dart +++ b/test/features/create_diet/presentation/create_diet_screen_test.dart @@ -67,5 +67,39 @@ void main() { expect(find.byType(CreateDietScreenContent), findsOneWidget); }); + + testWidgets('renders create diet screen with loading state', (tester) async { + final cubit = MockCreateDietCubit(); + final state = CreateDietState.initial().copyWith(isLoading: true); + + when(cubit.state).thenReturn(state); + when(cubit.stream).thenAnswer((_) => Stream.value(state)); + when(cubit.close()).thenAnswer((_) async => Future.value()); + when(cubit.setInitialData(any)).thenAnswer((_) async => Future.value()); + + sl.registerLazySingleton(() => cubit); + + await tester.pumpWidget(wrap(const CreateDietScreenContent(), cubit)); + await tester.pump(); + + expect(find.byType(CreateDietScreenContent), findsOneWidget); + }); + + testWidgets('renders create diet screen with diet publishing state', (tester) async { + final cubit = MockCreateDietCubit(); + final state = CreateDietState.initial().copyWith(isDietPublishing: true); + + when(cubit.state).thenReturn(state); + when(cubit.stream).thenAnswer((_) => Stream.value(state)); + when(cubit.close()).thenAnswer((_) async => Future.value()); + when(cubit.setInitialData(any)).thenAnswer((_) async => Future.value()); + + sl.registerLazySingleton(() => cubit); + + await tester.pumpWidget(wrap(const CreateDietScreenContent(), cubit)); + await tester.pump(); + + expect(find.byType(CreateDietScreenContent), findsOneWidget); + }); }); } diff --git a/test/features/create_diet/presentation/cubit/create_diet_cubit_test.dart b/test/features/create_diet/presentation/cubit/create_diet_cubit_test.dart index c5266123..8cba4984 100644 --- a/test/features/create_diet/presentation/cubit/create_diet_cubit_test.dart +++ b/test/features/create_diet/presentation/cubit/create_diet_cubit_test.dart @@ -6,6 +6,7 @@ import 'package:eatvio_app/features/create_diet/repositories/create_diet_reposit import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart'; import 'package:eatvio_app/features/glossary/models/glossary_model.dart'; import 'package:eatvio_app/features/glossary/repositories/glossary_repository.dart'; +import 'package:eatvio_app/features/profile/models/profile_model.dart'; import 'package:eatvio_app/features/profile/repositories/profile_repository.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:flutter_image_compress/flutter_image_compress.dart'; @@ -236,6 +237,51 @@ void main() { expect(result, null); }); + test('getDietForPreview returns Diet when form is valid', () async { + const profile = ProfileModel( + id: 1, + name: 'Test User', + email: 'test@example.com', + phoneConfirmationState: 'confirmed', + slug: 'test-user', + isPublicProfile: true, + isBlockedByMe: false, + isBlockedByAuthor: false, + isBlockedByUser: false, + isEntitySubscribedByMe: false, + profilePhotoUrl: 'https://example.com/photo.jpg', + roles: [], + badges: [], + subscriptions: [], + subscribers: [], + ); + when(profileRepository.getMyProfile()).thenAnswer((_) async => profile); + + cubit.setName('Test Diet'); + cubit.setDetails('Test Description'); + cubit.setDuration(0); + cubit.setDietState(DietState.active); + const portion = DailySchedule( + name: 'Apple', + portion: Portion.grams, + quantity: 100, + dailyNutrition: DailyNutrition(energy: 52.0, proteins: 0.3, fats: 0.2, carbohydrates: 14.0), + productNutritionalValueId: 1, + ); + cubit.addPortion(dayIndex: 0, mealIndex: 0, portion: portion); + cubit.setImage(null, imageUrl: 'https://example.com/cover.jpg'); + + final result = await cubit.getDietForPreview(); + + expect(result, isNotNull); + expect(result!.name, 'Test Diet'); + expect(result.details, 'Test Description'); + expect(result.duration, 7); + expect(result.author.authorName, 'Test User'); + expect(result.author.authorSlug, 'test-user'); + verify(profileRepository.getMyProfile()).called(1); + }); + test('createDiet returns validation error when form is invalid', () async { final result = await cubit.createDiet(); expect(result, 'validation'); @@ -257,7 +303,7 @@ void main() { portion: Portion.grams, quantity: 100, dailyNutrition: DailyNutrition(energy: 52.0, proteins: 0.3, fats: 0.2, carbohydrates: 14.0), - productNutritionalValueId: '1', + productNutritionalValueId: 1, ); cubit.addPortion(dayIndex: 0, mealIndex: 0, portion: portion); // Set image with non-existent file to trigger error in compression @@ -280,7 +326,7 @@ void main() { portion: Portion.grams, quantity: 100, dailyNutrition: DailyNutrition(energy: 52.0, proteins: 0.3, fats: 0.2, carbohydrates: 14.0), - productNutritionalValueId: '1', + productNutritionalValueId: 1, ); cubit.addPortion(dayIndex: 0, mealIndex: 0, portion: portion); final image = XFile('test_image.jpg'); @@ -302,7 +348,7 @@ void main() { portion: Portion.grams, quantity: 100, dailyNutrition: DailyNutrition(energy: 52.0, proteins: 0.3, fats: 0.2, carbohydrates: 14.0), - productNutritionalValueId: '1', + productNutritionalValueId: 1, ); cubit.addPortion(dayIndex: 0, mealIndex: 0, portion: portion); final image = XFile('test_image.jpg'); @@ -321,7 +367,7 @@ void main() { portion: Portion.grams, quantity: 100, dailyNutrition: DailyNutrition(energy: 52.0, proteins: 0.3, fats: 0.2, carbohydrates: 14.0), - productNutritionalValueId: '1', + productNutritionalValueId: 1, ); cubit.addPortion(dayIndex: 0, mealIndex: 0, portion: portion); @@ -336,7 +382,7 @@ void main() { portion: Portion.grams, quantity: 100, dailyNutrition: DailyNutrition(energy: 52.0, proteins: 0.3, fats: 0.2, carbohydrates: 14.0), - productNutritionalValueId: '1', + productNutritionalValueId: 1, ); cubit.addPortion(dayIndex: 0, mealIndex: 0, portion: portion); @@ -405,7 +451,7 @@ void main() { portion: Portion.grams, quantity: 100, dailyNutrition: DailyNutrition(energy: 52.0, proteins: 0.3, fats: 0.2, carbohydrates: 14.0), - productNutritionalValueId: '1', + productNutritionalValueId: 1, ); cubit.addPortion(dayIndex: 0, mealIndex: 0, portion: portion); final image = XFile('test_image.jpg'); @@ -427,7 +473,7 @@ void main() { portion: Portion.grams, quantity: 100, dailyNutrition: DailyNutrition(energy: 52.0, proteins: 0.3, fats: 0.2, carbohydrates: 14.0), - productNutritionalValueId: '1', + productNutritionalValueId: 1, ); cubit.addPortion(dayIndex: 0, mealIndex: 0, portion: portion); final image = XFile('test_image.jpg'); @@ -448,7 +494,7 @@ void main() { portion: Portion.grams, quantity: 100, dailyNutrition: DailyNutrition(energy: 52.0, proteins: 0.3, fats: 0.2, carbohydrates: 14.0), - productNutritionalValueId: '1', + productNutritionalValueId: 1, ); cubit.addPortion(dayIndex: 0, mealIndex: 0, portion: portion); final image = XFile('test_image.jpg'); @@ -469,7 +515,7 @@ void main() { portion: Portion.grams, quantity: 100, dailyNutrition: DailyNutrition(energy: 52.0, proteins: 0.3, fats: 0.2, carbohydrates: 14.0), - productNutritionalValueId: '1', + productNutritionalValueId: 1, ); cubit.addPortion(dayIndex: 0, mealIndex: 0, portion: portion); final image = XFile('test_image.jpg'); @@ -491,7 +537,7 @@ void main() { portion: Portion.grams, quantity: 100, dailyNutrition: DailyNutrition(energy: 52.0, proteins: 0.3, fats: 0.2, carbohydrates: 14.0), - productNutritionalValueId: '1', + productNutritionalValueId: 1, ); cubit.addPortion(dayIndex: 0, mealIndex: 0, portion: portion); @@ -522,7 +568,7 @@ void main() { portion: Portion.grams, quantity: 100, dailyNutrition: DailyNutrition(energy: 52.0, proteins: 0.3, fats: 0.2, carbohydrates: 14.0), - productNutritionalValueId: '1', + productNutritionalValueId: 1, ); cubit.addPortion(dayIndex: 0, mealIndex: 0, portion: portion); @@ -546,7 +592,7 @@ void main() { portion: Portion.grams, quantity: 100, dailyNutrition: DailyNutrition(energy: 52.0, proteins: 0.3, fats: 0.2, carbohydrates: 14.0), - productNutritionalValueId: '1', + productNutritionalValueId: 1, ); cubit.addPortion(dayIndex: 0, mealIndex: 0, portion: portion); diff --git a/test/features/create_diet/presentation/diet_preview_screen_smoke_test.dart b/test/features/create_diet/presentation/diet_preview_screen_smoke_test.dart index 823b556c..391143c6 100644 --- a/test/features/create_diet/presentation/diet_preview_screen_smoke_test.dart +++ b/test/features/create_diet/presentation/diet_preview_screen_smoke_test.dart @@ -63,7 +63,7 @@ void main() { dailyNutrition: [], ); - await tester.pumpWidget(wrap(DietPreviewScreen(diet: testDiet))); + await tester.pumpWidget(wrap(DietPreviewScreen(diet: testDiet, isImageInBase64: true))); await tester.pumpAndSettle(); expect(find.byType(DietPreviewScreen), findsOneWidget); @@ -103,7 +103,7 @@ void main() { dailyNutrition: [const DailyNutrition(energy: 2000.0, proteins: 100.0, fats: 50.0, carbohydrates: 200.0)], ); - await tester.pumpWidget(wrap(DietPreviewScreen(diet: testDiet))); + await tester.pumpWidget(wrap(DietPreviewScreen(diet: testDiet, isImageInBase64: true))); await tester.pumpAndSettle(); expect(find.byType(DietPreviewScreen), findsOneWidget); @@ -131,7 +131,7 @@ void main() { dailyNutrition: [], ); - await tester.pumpWidget(wrap(DietPreviewScreen(diet: testDiet))); + await tester.pumpWidget(wrap(DietPreviewScreen(diet: testDiet, isImageInBase64: true))); await tester.pumpAndSettle(); expect(find.text('Test Diet Name'), findsOneWidget); diff --git a/test/features/create_diet/repositories/create_diet_repository_test.dart b/test/features/create_diet/repositories/create_diet_repository_test.dart index 1b881380..602097ad 100644 --- a/test/features/create_diet/repositories/create_diet_repository_test.dart +++ b/test/features/create_diet/repositories/create_diet_repository_test.dart @@ -196,7 +196,7 @@ void main() { portion: Portion.grams, quantity: 100, dailyNutrition: null, - productNutritionalValueId: '1', + productNutritionalValueId: 1, ), ], ], @@ -236,7 +236,7 @@ void main() { portion: Portion.grams, quantity: 100, dailyNutrition: null, - productNutritionalValueId: '1', + productNutritionalValueId: 1, ), ], ], @@ -299,7 +299,7 @@ void main() { portion: Portion.grams, quantity: 100, dailyNutrition: null, - productNutritionalValueId: '1', + productNutritionalValueId: 1, ), ], ], diff --git a/test/features/my_diets/repositories/my_diets_repository_test.dart b/test/features/my_diets/repositories/my_diets_repository_test.dart index c31bb6aa..b5a23bb5 100644 --- a/test/features/my_diets/repositories/my_diets_repository_test.dart +++ b/test/features/my_diets/repositories/my_diets_repository_test.dart @@ -103,7 +103,7 @@ void main() { dietData.first['name'] = 'Another Diet'; dietData.first['image'] = 'https://someurl.com/img2.jpg'; dietData.first['duration'] = duration; - dietData.first['state'] = 'draft'; + dietData.first['state'] = 'drafted'; dietData.first['type'] = {'light': false, 'veg': true, 'carb': false, 'meat': false}; dietData.first['description_md'] = ''; dietData.first['author'] = {'author_slug': 'author-slug', 'author_name': 'Author Name', 'author_profile_icon': '', 'author_is_me': true, 'is_entity_subscribed_by_me': null, 'author_place_in_rating': null}; diff --git a/test/features/plans/presentation/pages/about_plan_screen_smoke_test.dart b/test/features/plans/presentation/pages/about_plan_screen_smoke_test.dart index e57b7601..32f67e95 100644 --- a/test/features/plans/presentation/pages/about_plan_screen_smoke_test.dart +++ b/test/features/plans/presentation/pages/about_plan_screen_smoke_test.dart @@ -182,5 +182,34 @@ void main() { expect(find.byType(AboutPlanScreen), findsOneWidget); }); + + testWidgets('renders with navigateToComments true', (tester) async { + final planCubit = MockAboutPlanCubit(); + final feedCubit = MockFeedCubit(); + final commentsCubit = MockCommentsCubit(); + const state = AboutPlanState(isLoading: false, planModel: testPlan, chosenOptionIndex: 0, error: null); + + when(planCubit.state).thenReturn(state); + when(planCubit.stream).thenAnswer((_) => const Stream.empty()); + when(planCubit.close()).thenAnswer((_) async => Future.value()); + when(feedCubit.viewPlan(any)).thenAnswer((_) async => Future.value()); + when(feedCubit.stream).thenAnswer((_) => const Stream.empty()); + when(feedCubit.close()).thenAnswer((_) async => Future.value()); + when(commentsCubit.state).thenReturn(const CommentsState()); + when(commentsCubit.stream).thenAnswer((_) => const Stream.empty()); + when(commentsCubit.close()).thenAnswer((_) async => Future.value()); + when(commentsCubit.getComments(any, any, any)).thenAnswer((_) async => Future.value()); + + registerMocks(planCubit, commentsCubit); + + await mockNetworkImagesFor(() async { + await tester.pumpWidget(wrap(planCubit, feedCubit, commentsCubit, const AboutPlanScreen(slug: 'test', navigateToComments: true, index: 1))); + await tester.pump(); + await tester.pump(); + }); + + expect(find.byType(AboutPlanScreen), findsOneWidget); + expect(find.text('Test Plan'), findsOneWidget); + }); }); } diff --git a/test/features/recipe/presentation/pages/about_recipe_screen_smoke_test.dart b/test/features/recipe/presentation/pages/about_recipe_screen_smoke_test.dart index 3008d3b3..9bc22016 100644 --- a/test/features/recipe/presentation/pages/about_recipe_screen_smoke_test.dart +++ b/test/features/recipe/presentation/pages/about_recipe_screen_smoke_test.dart @@ -193,5 +193,36 @@ void main() { expect(find.byType(AboutRecipeScreen), findsOneWidget); }); + + testWidgets('renders with navigateToComments true', (tester) async { + final recipeCubit = MockAboutRecipeCubit(); + final feedCubit = MockFeedCubit(); + final commentsCubit = MockCommentsCubit(); + final state = AboutRecipeState.loaded(recipe: testRecipe); + + when(recipeCubit.state).thenReturn(state); + when(recipeCubit.stream).thenAnswer((_) => const Stream.empty()); + when(recipeCubit.close()).thenAnswer((_) async => Future.value()); + when(feedCubit.viewRecipe(any)).thenAnswer((_) async => Future.value()); + when(feedCubit.stream).thenAnswer((_) => const Stream.empty()); + when(feedCubit.close()).thenAnswer((_) async => Future.value()); + when(commentsCubit.state).thenReturn(const CommentsState()); + when(commentsCubit.stream).thenAnswer((_) => const Stream.empty()); + when(commentsCubit.close()).thenAnswer((_) async => Future.value()); + when(commentsCubit.getComments(any, any, any)).thenAnswer((_) async => Future.value()); + when(commentsCubit.setReplyId(any)).thenReturn(null); + + registerMocks(recipeCubit, commentsCubit); + + await mockNetworkImagesFor(() async { + await tester.pumpWidget( + wrap(recipeCubit, feedCubit, commentsCubit, const AboutRecipeScreen(recipeSlug: 'test', index: 2, navigateToComments: true)), + ); + await tester.pump(); + await tester.pump(const Duration(milliseconds: 600)); + }); + + expect(find.byType(AboutRecipeScreen), findsOneWidget); + }); }); } diff --git a/test/fixtures/create_diet/nutrition_response.json b/test/fixtures/create_diet/nutrition_response.json index 6dd2119e..61bf7807 100644 --- a/test/fixtures/create_diet/nutrition_response.json +++ b/test/fixtures/create_diet/nutrition_response.json @@ -3,11 +3,58 @@ "1": { "0": { "name": "Авокадо", - "portion": "grams", + "portion": "gr", "quantity": 100, - "product_nutritional_value_id": "501" + "product_nutritional_value_id": 501, + "nutrition": { + "energy": 160.0, + "proteins": 2.0, + "fats": 14.0, + "carbohydrates": 9.0 + } + }, + "1": { + "name": "Лосось", + "portion": "gr", + "quantity": 150, + "product_nutritional_value_id": 502, + "nutrition": { + "energy": 312.0, + "proteins": 30.0, + "fats": 19.5, + "carbohydrates": 0.0 + } + } + }, + "2": { + "0": { + "name": "Овсянка", + "portion": "gr", + "quantity": 150, + "product_nutritional_value_id": 503, + "nutrition": { + "energy": 195.0, + "proteins": 6.75, + "fats": 3.0, + "carbohydrates": 33.0 + } + } + } + }, + "2": { + "1": { + "0": { + "name": "Творог", + "portion": "gr", + "quantity": 200, + "product_nutritional_value_id": 504, + "nutrition": { + "energy": 226.0, + "proteins": 31.0, + "fats": 9.0, + "carbohydrates": 3.4 + } } } } } - diff --git a/test/fixtures/diets/diet_response.json b/test/fixtures/diets/diet_response.json index d4d770a9..41b52101 100644 --- a/test/fixtures/diets/diet_response.json +++ b/test/fixtures/diets/diet_response.json @@ -12,7 +12,66 @@ "meat": false }, "description_md": "Test details", - "daily_schedule": {}, + "daily_schedule": { + "1": { + "1": { + "0": { + "name": "Овсянка", + "portion": "gr", + "quantity": 150, + "product_nutritional_value_id": 101, + "nutrition": { + "energy": 195.0, + "proteins": 6.75, + "fats": 3.0, + "carbohydrates": 33.0 + } + }, + "1": { + "name": "Бананы", + "portion": "gr", + "quantity": 120, + "product_nutritional_value_id": 102, + "nutrition": { + "energy": 107.0, + "proteins": 1.3, + "fats": 0.4, + "carbohydrates": 27.0 + } + } + }, + "2": { + "0": { + "name": "Куриная грудка", + "portion": "gr", + "quantity": 200, + "product_nutritional_value_id": 201, + "nutrition": { + "energy": 330.0, + "proteins": 62.0, + "fats": 7.2, + "carbohydrates": 0.0 + } + } + } + }, + "2": { + "1": { + "0": { + "name": "Творог", + "portion": "gr", + "quantity": 200, + "product_nutritional_value_id": 301, + "nutrition": { + "energy": 226.0, + "proteins": 31.0, + "fats": 9.0, + "carbohydrates": 3.4 + } + } + } + } + }, "author": { "author_slug": "author", "author_name": "Author Name", @@ -26,6 +85,18 @@ "total_comments": 5, "was_liked_by_me": false, "is_favorite": false, - "daily_nutrition": [] + "daily_nutrition": [ + { + "energy": 864.0, + "proteins": 101.05, + "fats": 19.6, + "carbohydrates": 63.4 + }, + { + "energy": 532.0, + "proteins": 93.3, + "fats": 16.2, + "carbohydrates": 30.4 + } + ] } - diff --git a/test/fixtures/doubles/create_diet_doubles.dart b/test/fixtures/doubles/create_diet_doubles.dart index 7cf7b3b9..08b16861 100644 --- a/test/fixtures/doubles/create_diet_doubles.dart +++ b/test/fixtures/doubles/create_diet_doubles.dart @@ -53,11 +53,15 @@ class FixtureCreateDietRepository extends CreateDietRepository { portion = Portion.grams; } + final pnvid = portionData['product_nutritional_value_id']; + final nutritionJson = portionData['nutrition'] as Map?; + final dailyNutrition = nutritionJson != null ? DailyNutrition.fromJson(nutritionJson) : null; mealMap[portionIndex] = DailySchedule( name: portionData['name'] as String, portion: portion, - quantity: portionData['quantity'] as int, - productNutritionalValueId: portionData['product_nutritional_value_id'] as String?, + quantity: portionData['quantity'] is int ? portionData['quantity'] as int : int.parse(portionData['quantity'].toString()), + productNutritionalValueId: pnvid == null ? null : (pnvid is int ? pnvid : int.tryParse(pnvid.toString())), + dailyNutrition: dailyNutrition, ); } @@ -75,13 +79,13 @@ class FixtureCreateDietRepository extends CreateDietRepository { required String name, required int duration, required DietState state, - required String meatType, - required String carbType, - required String vegType, - required String lightType, - required String details, - required File image, - required List>> dailySchedule, + required String? meatType, + required String? carbType, + required String? vegType, + required String? lightType, + required String? details, + required File? image, + required List>>? dailySchedule, }) async { createRequests.add({ 'name': name, diff --git a/test/fixtures/feed/diets_pagination_response.json b/test/fixtures/feed/diets_pagination_response.json index a15de8fb..8dd97a36 100644 --- a/test/fixtures/feed/diets_pagination_response.json +++ b/test/fixtures/feed/diets_pagination_response.json @@ -14,7 +14,54 @@ "meat": true }, "description_md": "Keto diet description", - "daily_schedule": {}, + "daily_schedule": { + "1": { + "1": { + "0": { + "name": "Яйца вареные", + "portion": "pcs", + "quantity": 2, + "product_nutritional_value_id": 401, + "nutrition": { + "energy": 155.0, + "proteins": 12.6, + "fats": 10.6, + "carbohydrates": 1.1 + } + } + }, + "2": { + "0": { + "name": "Авокадо", + "portion": "gr", + "quantity": 100, + "product_nutritional_value_id": 501, + "nutrition": { + "energy": 160.0, + "proteins": 2.0, + "fats": 14.0, + "carbohydrates": 9.0 + } + } + } + }, + "2": { + "1": { + "0": { + "name": "Лосось", + "portion": "gr", + "quantity": 150, + "product_nutritional_value_id": 502, + "nutrition": { + "energy": 312.0, + "proteins": 30.0, + "fats": 19.5, + "carbohydrates": 0.0 + } + } + } + } + }, "author": { "author_slug": "nutritionist-1", "author_name": "Nutritionist Name", @@ -28,7 +75,20 @@ "total_comments": 25, "was_liked_by_me": false, "is_favorite": false, - "daily_nutrition": [] + "daily_nutrition": [ + { + "energy": 627.0, + "proteins": 44.6, + "fats": 44.1, + "carbohydrates": 10.1 + }, + { + "energy": 312.0, + "proteins": 30.0, + "fats": 19.5, + "carbohydrates": 0.0 + } + ] } ], "links": { @@ -50,4 +110,3 @@ "total_max_price": null } } - diff --git a/test/fixtures/my_diets/my_diets_pagination_response.json b/test/fixtures/my_diets/my_diets_pagination_response.json new file mode 100644 index 00000000..a23af562 --- /dev/null +++ b/test/fixtures/my_diets/my_diets_pagination_response.json @@ -0,0 +1,112 @@ +{ + "data": [ + { + "id": 1, + "slug": "my-keto-diet", + "name": "My Keto Diet", + "image": "https://example.com/my-keto.jpg", + "duration": 14, + "state": "drafted", + "type": { + "light": true, + "veg": true, + "carb": false, + "meat": true + }, + "description_md": "My drafted keto diet for two weeks", + "daily_schedule": { + "1": { + "1": { + "0": { + "name": "Омлет", + "portion": "gr", + "quantity": 200, + "product_nutritional_value_id": 601, + "nutrition": { + "energy": 318.0, + "proteins": 21.2, + "fats": 24.2, + "carbohydrates": 2.0 + } + } + }, + "2": { + "0": { + "name": "Салат с курицей", + "portion": "gr", + "quantity": 300, + "product_nutritional_value_id": 602, + "nutrition": { + "energy": 285.0, + "proteins": 35.0, + "fats": 12.0, + "carbohydrates": 8.0 + } + } + } + }, + "2": { + "1": { + "0": { + "name": "Творог 5%", + "portion": "gr", + "quantity": 200, + "product_nutritional_value_id": 603, + "nutrition": { + "energy": 226.0, + "proteins": 31.0, + "fats": 9.0, + "carbohydrates": 3.4 + } + } + } + } + }, + "author": { + "author_slug": "me", + "author_name": "My Name", + "author_profile_icon": "https://example.com/me.jpg", + "author_is_me": true, + "is_entity_subscribed_by_me": null, + "author_place_in_rating": null + }, + "total_likes": 0, + "total_views": 0, + "total_comments": 0, + "was_liked_by_me": false, + "is_favorite": false, + "daily_nutrition": [ + { + "energy": 603.0, + "proteins": 56.2, + "fats": 36.2, + "carbohydrates": 10.0 + }, + { + "energy": 226.0, + "proteins": 31.0, + "fats": 9.0, + "carbohydrates": 3.4 + } + ] + } + ], + "links": { + "first": "http://localhost/api/my-diets?page=1", + "last": "http://localhost/api/my-diets?page=1", + "prev": null, + "next": null + }, + "meta": { + "current_page": 1, + "from": 1, + "last_page": 1, + "links": [], + "path": "http://localhost/api/my-diets", + "per_page": 10, + "to": 1, + "total": 1, + "total_min_price": null, + "total_max_price": null + } +}