From 67e770f5d027decd2fd9dedfa102a04c68fcfb7b Mon Sep 17 00:00:00 2001 From: misha Date: Fri, 20 Feb 2026 13:20:17 +0300 Subject: [PATCH] review: fixed review issues --- .DS_Store | Bin 12292 -> 0 bytes lib/core/widgets/info_row.dart | 2 +- .../presentation/cubit/create_diet_cubit.dart | 8 +- .../presentation/diet_preview_screen.dart | 6 ++ .../presentation/widgets/my_diet_widget.dart | 2 + .../cubit/create_diet_cubit_test.dart | 85 +++++++++++++++++- .../diet_preview_screen_smoke_test.dart | 29 ++++++ .../cubit/my_diets_cubit_test.dart | 4 +- 8 files changed, 131 insertions(+), 5 deletions(-) delete mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 617af8b4ff1f0ec1b05979f5f68b483ee0b2446b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12292 zcmeHNZEO@p7=EW8^vdTIDDolZw9%l{TFQqQL%5bUf=0m}LJ{!TyIacAz1?%~dbJ?f z_=5yYOb8f5h=4IB`ok!i2nJ0+5%^(@A5jy9m}pFl2~nfQ;1BVc+3nrT-d*`Hkpy=x zvokyMzBA80v-8f(yGKNhl#SJ zQ;xFBOgmX3U0P6uBCcP9IEplAOZo}ZPM{o(((8yV7ezo$BL{uNkqbh=K)^u2K)^u2 zK)}G2Gr-qggphB&cT0n7z(Byj|C#}AA0mi6je6;jn+nr`3*W-?dcK7h0eQY<=VcKu zje6;jo1zARxvEHARpuQrn5&NRh$k2I(jhl>)rpCo(T>+M^G+yC^p5g~5GNLO)8HB~ z5HL{6z+4n5XKy2!&$wrA_I@H`8rfWH>$l-BtE#T49aR^qA3bL5xbYLh4HG9dOm1jw zY@BjKr^UmQ5?Zpa*RlJp)DqjYoer~KU)$>#saQr!8dh&lPJbd7Gq&jQ@I;?z zx1M^eShR951DVOi0@2)kj;>pxz2zG>X7yZ;lwT7MPsk=6+ca0$StDoI)|z$MP4>XD zoSt50ZM4y2-p~h@XLwb1mF{Gbmgw}X(Huv!di9RM^`@54&E-~yoy*zj9wTX6F7gSY zS=_F0blzms;$K`&o1za4XSgo~Axuqf20N_D!#OWyD>*UsP0Zj`vnox1%V~%CoRxipq z_F76%KN)0FN=Eq5Q(wHP*2?%!rQZGUz&?Rb(ofq(bmb*vY|BGp1VWS~oAU5pIq)y_ zk}jj5501?PiV_(a%QvkH_kFfUU@0S4L^P6|jElaDpi63`6fouC@3QeT;U9}5&EVcZ zYT>?x27x$DCil4&eP-+#Xdfu}%!tOL9SwSUp&3U$gI^2oN_8*zbigU9hc&*FtV`;67iv203Wi`gxpoDKc`IxAvgj8Z6>sf0$I&7`K zWS*_A3yle_kHHSca$2rG+o3sY`Q1v}HWR~nEwKt_4CZ!;ZRIq>(jB2-q;!a^TWwe= zd!U2c^sLa0xfP-MP<=c+b?er)1q+)aZFAbTHAlAI)7IJ?nKyUAwr%yH#-_XO?T+`E zY0KWlsm3%Pwo&8U`@)sKsopco98+ut*YIj&j^`gpeKfe&<5;jDb6i?0b4*T{ckH$5 zvqB-*Uk&zGr;Qi^lXP_qs<^e1=Trk`kOs@qhbwFb-3c9A+_%tX%uUQtwFOvsV&kP* zIo8)gB|Uh+OZ56-Gqcdw(PPVS&uV{c<|XwiS`Veadg`Vwpzj1jUFa=MDPK=`KNXES zU`xVU7(j?G0?Noz-Ci*1laNRF>*gnK-(`ytE|DtE{>oCyh`R@hI*MV1;Wp3a-w#wg zRp$jBMMB22d)g1@&YV4k(F1CqR1r^QsWNzKhHa_06?{XjXgUZ10|5g80|5iqIRm%i`9@C*$&boi)JK3q;DUf}O7F0iHsgjAPe}uWUW&o?NB+;u>A9UhcQ* zP!vt$MD6v9J)ov?5nbx9m;1l%s{#*78!`u#WMmvf8#sX9jD@5xBPcJS^zS->>(PH75V*T{!{ouuflIy@Va{b(-^Iz$7zV3r3=Rrvp(D-~P=1_B1IO9og?XRNaWkH#uI z9^Ym~5H}!lnd68&ATLyK5j!!Y;&VJw@i`u8t1Br4qn^%?o1zxsKmQ|u { } } + + static const int _maxImageBytesForBase64 = 5 * 1024 * 1024; + Future getDietForPreview() async { if (!_isFormValidForDraft()) return null; final authorProfile = await profileRepository.getMyProfile(); final imageBytes = (await state.dietData.image?.readAsBytes()) ?? []; - String image = base64.encode(imageBytes); + String image = ''; + if (imageBytes.isNotEmpty && imageBytes.length <= _maxImageBytesForBase64) { + image = base64.encode(imageBytes); + } if (image.isEmpty && state.dietData.imageUrl != null) { image = state.dietData.imageUrl ?? ''; } diff --git a/lib/features/create_diet/presentation/diet_preview_screen.dart b/lib/features/create_diet/presentation/diet_preview_screen.dart index c7482cd4..fd5a2f3b 100644 --- a/lib/features/create_diet/presentation/diet_preview_screen.dart +++ b/lib/features/create_diet/presentation/diet_preview_screen.dart @@ -44,6 +44,12 @@ class _DietPreviewScreenState extends State { } } + @override + void dispose() { + scrollController.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { final s = S.of(context); diff --git a/lib/features/my_diets/presentation/widgets/my_diet_widget.dart b/lib/features/my_diets/presentation/widgets/my_diet_widget.dart index 019cc487..0799fc77 100644 --- a/lib/features/my_diets/presentation/widgets/my_diet_widget.dart +++ b/lib/features/my_diets/presentation/widgets/my_diet_widget.dart @@ -61,8 +61,10 @@ class _MyDietWidgetState extends State { child: SuccessPublishedDietOrPlanBottomSheet(title: widget.diet.name), ); await context.read().loadAllDiets(); + break; case 422: showErrorToast('Пожалуйста, заполните все обязательные поля для публикации'); + break; default: showErrorToast('Произошла ошибка. Попробуйте ещё раз через некоторое время'); } 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 5feac64f..bd8a2d34 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 @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:eatvio_app/core/app_config.dart'; import 'package:eatvio_app/core/app_initializer.dart'; import 'package:eatvio_app/core/locator_service.dart'; @@ -13,7 +15,6 @@ import 'package:flutter_image_compress/flutter_image_compress.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; - import 'create_diet_cubit_test.mocks.dart'; @GenerateNiceMocks([MockSpec(), MockSpec(), MockSpec()]) @@ -316,6 +317,88 @@ void main() { expect(result, 'error'); }); + test('updateDiet returns error and resets isDietBeingEdited when repository throws', () async { + 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); + // No image so we reach repository.updateDiet without compression + + when(dietRepository.updateDiet( + dietId: 1, + state: anyNamed('state'), + name: anyNamed('name'), + duration: anyNamed('duration'), + meatType: anyNamed('meatType'), + carbType: anyNamed('carbType'), + vegType: anyNamed('vegType'), + lightType: anyNamed('lightType'), + details: anyNamed('details'), + image: anyNamed('image'), + dailySchedule: anyNamed('dailySchedule'), + )).thenThrow(Exception('network error')); + + final result = await cubit.updateDiet(1); + + expect(result, 'error'); + expect(cubit.state.isDietBeingEdited, false); + }); + + test('createDiet returns error when repository createDiet throws', () async { + 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, + ); + for (var day = 0; day < 7; day++) { + cubit.addPortion(dayIndex: day, mealIndex: 0, portion: portion); + } + final tempDir = Directory.systemTemp; + final tempFile = File('${tempDir.path}/create_diet_test_image_${DateTime.now().millisecondsSinceEpoch}.png'); + final pngBytes = [ + 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, + 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x08, 0x02, 0x00, 0x00, 0x00, 0x90, 0x77, 0x53, + 0xDE, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, + 0x44, 0xAE, 0x42, 0x60, 0x82, + ]; + await tempFile.writeAsBytes(pngBytes); + cubit.setImage(XFile(tempFile.path)); + when(dietRepository.createDiet( + name: anyNamed('name'), + duration: anyNamed('duration'), + state: anyNamed('state'), + meatType: anyNamed('meatType'), + carbType: anyNamed('carbType'), + vegType: anyNamed('vegType'), + lightType: anyNamed('lightType'), + details: anyNamed('details'), + image: anyNamed('image'), + dailySchedule: anyNamed('dailySchedule'), + )).thenThrow(Exception('server error')); + + final result = await cubit.createDiet(); + + expect(result, 'error'); + expect(cubit.state.isDietPublishing, false); + if (await tempFile.exists()) await tempFile.delete(); + }); + test('createDiet validates name length', () async { cubit.setName('Te'); // Less than 3 characters cubit.setDetails('Test Description'); 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 391143c6..b9dbe004 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 @@ -139,5 +139,34 @@ void main() { expect(find.text('10'), findsOneWidget); // totalLikes }); }); + + testWidgets('renders diet preview with isImageInBase64 false (URL image)', (tester) async { + await mockNetworkImagesFor(() async { + final testDiet = Diet( + id: 1, + slug: 'test-diet', + name: 'Test Diet URL Image', + image: 'https://example.com/diet-cover.jpg', + duration: 7, + state: DietState.active, + type: const DietType(veg: true, light: false, carb: false, meat: false), + details: 'Test diet details', + dailySchedule: {}, + author: const Author(authorSlug: 'test-author', authorName: 'Test Author', authorProfileIcon: 'https://example.com/photo.jpg', authorIsMe: false), + totalLikes: 10, + totalViews: 100, + totalComments: 5, + wasLikedByMe: false, + isFavorite: false, + dailyNutrition: [], + ); + + await tester.pumpWidget(wrap(DietPreviewScreen(diet: testDiet, isImageInBase64: false))); + await tester.pumpAndSettle(); + + expect(find.byType(DietPreviewScreen), findsOneWidget); + expect(find.text('Test Diet URL Image'), findsOneWidget); + }); + }); }); } diff --git a/test/features/my_diets/presentation/cubit/my_diets_cubit_test.dart b/test/features/my_diets/presentation/cubit/my_diets_cubit_test.dart index bb990eec..ef4abc80 100644 --- a/test/features/my_diets/presentation/cubit/my_diets_cubit_test.dart +++ b/test/features/my_diets/presentation/cubit/my_diets_cubit_test.dart @@ -269,11 +269,11 @@ void main() { when(mockCreateDietRepository.updateDiet( dietId: 1, state: DietState.active, - )).thenAnswer((_) async => 200); + )).thenAnswer((_) async => 201); final result = await myDietsCubit!.publishDiet(1); - expect(result, 200); + expect(result, 201); verify(mockCreateDietRepository.updateDiet(dietId: 1, state: DietState.active)).called(1); });