feat(tests): added new tests and updated fixtures

This commit is contained in:
misha
2026-02-10 08:02:50 +03:00
parent c91e83adaf
commit f81aebe9d8
16 changed files with 591 additions and 102 deletions

View File

@@ -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: |

View File

@@ -201,6 +201,7 @@ class CreateDietCubit extends Cubit<CreateDietState> {
}
Future<Diet?> getDietForPreview() async {
if (!_isFormValidForDraft()) return null;
final authorProfile = await profileRepository.getMyProfile();
final imageBytes = (await state.dietData.image?.readAsBytes()) ?? <int>[];
String image = base64.encode(imageBytes);
@@ -317,6 +318,11 @@ class CreateDietCubit extends Cubit<CreateDietState> {
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<CreateDietState> {
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 ?? '',

View File

@@ -82,7 +82,12 @@ class CreateDietRepository {
return MapEntry(
int.parse(key),
(value as Map<String, dynamic>).map((key, value) {
return MapEntry(int.parse(key), DailySchedule.fromJson(value as Map<String, dynamic>));
final portionMap = Map<String, dynamic>.from(value as Map<String, dynamic>);
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));
}),
);
}),

View File

@@ -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<void>.value());
when(feedCubit.viewPost(any)).thenAnswer((_) async => Future<void>.value());
when(feedCubit.stream).thenAnswer((_) => const Stream.empty());
when(feedCubit.close()).thenAnswer((_) async => Future<void>.value());
when(commentsCubit.state).thenReturn(const CommentsState());
when(commentsCubit.stream).thenAnswer((_) => const Stream<CommentsState>.empty());
when(commentsCubit.close()).thenAnswer((_) async => Future<void>.value());
when(commentsCubit.getComments(any, any, any)).thenAnswer((_) async => Future<void>.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);
});
});
});
}

View File

@@ -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<void>.value());
when(cubit.setInitialData(any)).thenAnswer((_) async => Future<void>.value());
sl.registerLazySingleton<CreateDietCubit>(() => 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<void>.value());
when(cubit.setInitialData(any)).thenAnswer((_) async => Future<void>.value());
sl.registerLazySingleton<CreateDietCubit>(() => cubit);
await tester.pumpWidget(wrap(const CreateDietScreenContent(), cubit));
await tester.pump();
expect(find.byType(CreateDietScreenContent), findsOneWidget);
});
});
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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,
),
],
],

View File

@@ -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};

View File

@@ -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<AboutPlanState>.empty());
when(planCubit.close()).thenAnswer((_) async => Future<void>.value());
when(feedCubit.viewPlan(any)).thenAnswer((_) async => Future<void>.value());
when(feedCubit.stream).thenAnswer((_) => const Stream.empty());
when(feedCubit.close()).thenAnswer((_) async => Future<void>.value());
when(commentsCubit.state).thenReturn(const CommentsState());
when(commentsCubit.stream).thenAnswer((_) => const Stream<CommentsState>.empty());
when(commentsCubit.close()).thenAnswer((_) async => Future<void>.value());
when(commentsCubit.getComments(any, any, any)).thenAnswer((_) async => Future<void>.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);
});
});
}

View File

@@ -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<AboutRecipeState>.empty());
when(recipeCubit.close()).thenAnswer((_) async => Future<void>.value());
when(feedCubit.viewRecipe(any)).thenAnswer((_) async => Future<void>.value());
when(feedCubit.stream).thenAnswer((_) => const Stream.empty());
when(feedCubit.close()).thenAnswer((_) async => Future<void>.value());
when(commentsCubit.state).thenReturn(const CommentsState());
when(commentsCubit.stream).thenAnswer((_) => const Stream<CommentsState>.empty());
when(commentsCubit.close()).thenAnswer((_) async => Future<void>.value());
when(commentsCubit.getComments(any, any, any)).thenAnswer((_) async => Future<void>.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);
});
});
}

View File

@@ -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
}
}
}
}
}

View File

@@ -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
}
]
}

View File

@@ -53,11 +53,15 @@ class FixtureCreateDietRepository extends CreateDietRepository {
portion = Portion.grams;
}
final pnvid = portionData['product_nutritional_value_id'];
final nutritionJson = portionData['nutrition'] as Map<String, dynamic>?;
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<List<List<DailySchedule>>> dailySchedule,
required String? meatType,
required String? carbType,
required String? vegType,
required String? lightType,
required String? details,
required File? image,
required List<List<List<DailySchedule>>>? dailySchedule,
}) async {
createRequests.add(<String, dynamic>{
'name': name,

View File

@@ -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
}
}

View File

@@ -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
}
}