diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index b4071fe3..4ad594f6 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -3,7 +3,7 @@ name: Eatvio App Android Test on: [ pull_request ] env: - flutter_version: "3.38.6" + flutter_version: "3.38.9" java_version: "21.x" flutter_channel: "stable" diff --git a/.gitea/workflows/test.yml b/.gitea/workflows/test.yml index a26ff075..f07c8dd3 100644 --- a/.gitea/workflows/test.yml +++ b/.gitea/workflows/test.yml @@ -3,7 +3,7 @@ name: Eatvio App Android Test on: [ pull_request ] env: - flutter_version: "3.38.6" + flutter_version: "3.38.9" java_version: "21.x" flutter_channel: "stable" my_local_cache_dir: "/opt/hostedtoolcache/" diff --git a/android/app/agconnect-services.json b/android/app/agconnect-services.json index 8ca899db..e2f8dec1 100644 --- a/android/app/agconnect-services.json +++ b/android/app/agconnect-services.json @@ -1 +1,94 @@ -{"agcgw":{"url":"connect-drru.hispace.dbankcloud.ru","backurl":"connect-drru.hispace.dbankcloud.cn","websocketurl":"connect-ws-drru.hispace.dbankcloud.ru","websocketbackurl":"connect-ws-drru.hispace.dbankcloud.cn"},"agcgw_all":{"SG":"connect-dra.dbankcloud.cn","SG_back":"connect-dra.hispace.hicloud.com","CN":"connect-drcn.dbankcloud.cn","CN_back":"connect-drcn.hispace.hicloud.com","RU":"connect-drru.hispace.dbankcloud.ru","RU_back":"connect-drru.hispace.dbankcloud.cn","DE":"connect-dre.dbankcloud.cn","DE_back":"connect-dre.hispace.hicloud.com"},"websocketgw_all":{"SG":"connect-ws-dra.hispace.dbankcloud.cn","SG_back":"connect-ws-dra.hispace.dbankcloud.com","CN":"connect-ws-drcn.hispace.dbankcloud.cn","CN_back":"connect-ws-drcn.hispace.dbankcloud.com","RU":"connect-ws-drru.hispace.dbankcloud.ru","RU_back":"connect-ws-drru.hispace.dbankcloud.cn","DE":"connect-ws-dre.hispace.dbankcloud.cn","DE_back":"connect-ws-dre.hispace.dbankcloud.com"},"client":{"cp_id":"20007000036693451","product_id":"461323198429446894","client_id":"1595824543740483264","client_secret":"2A1727B0D31AA9BE1F5D9E8A5CDA1A67B9EF084AC9FB68D6C633A3CCB08ECDEE","project_id":"461323198429446894","app_id":"113238569","api_key":"DQEDAJNRITp9M3uc1rPHyT8PzLc7bnVcKJAeavZZk0TQNMx9ifvBebr+JOmZe9tEvjnLwv7c1hAHXmPGR/gWG86iABDrCVotq+NGnw==","package_name":"com.eatvioapp.app"},"oauth_client":{"client_id":"113238569","client_type":1},"app_info":{"app_id":"113238569","package_name":"com.eatvioapp.app"},"service":{"analytics":{"collector_url":"datacollector-drru.dt.dbankcloud.ru,datacollector-drru.dt.hicloud.com","collector_url_cn":"datacollector-drcn.dt.hicloud.com,datacollector-drcn.dt.dbankcloud.cn","collector_url_de":"datacollector-dre.dt.hicloud.com,datacollector-dre.dt.dbankcloud.cn","collector_url_ru":"datacollector-drru.dt.dbankcloud.ru,datacollector-drru.dt.hicloud.com","collector_url_sg":"datacollector-dra.dt.hicloud.com,datacollector-dra.dt.dbankcloud.cn","resource_id":"p1","channel_id":""},"ml":{"mlservice_url":"ml-api-drru.ai.dbankcloud.ru,ml-api-drru.ai.dbankcloud.cn"},"cloudstorage":{"storage_url":"https://agc-storage-drcn.platform.dbankcloud.cn","storage_url_ru":"https://agc-storage-drru.cloud.huawei.ru","storage_url_sg":"https://ops-dra.agcstorage.link","storage_url_de":"https://ops-dre.agcstorage.link","storage_url_cn":"https://agc-storage-drcn.platform.dbankcloud.cn","storage_url_ru_back":"https://agc-storage-drru.cloud.huawei.ru","storage_url_sg_back":"https://agc-storage-dra.cloud.huawei.asia","storage_url_de_back":"https://agc-storage-dre.cloud.huawei.eu","storage_url_cn_back":"https://agc-storage-drcn.cloud.huawei.com.cn"},"search":{"url":"https://search-drru.cloud.huawei.ru"},"edukit":{"edu_url":"edukit.cloud.huawei.com.cn","dh_url":"edukit.cloud.huawei.com.cn"}},"region":"RU","configuration_version":"3.0","appInfos":[{"package_name":"com.eatvioapp.app","client":{"app_id":"113238569"},"app_info":{"package_name":"com.eatvioapp.app","app_id":"113238569"},"oauth_client":{"client_type":1,"client_id":"113238569"}}]} \ No newline at end of file +{ + "agcgw" : { + "url" : "connect-drru.hispace.dbankcloud.ru", + "backurl" : "connect-drru.hispace.dbankcloud.cn", + "websocketurl" : "connect-ws-drru.hispace.dbankcloud.ru", + "websocketbackurl" : "connect-ws-drru.hispace.dbankcloud.cn" + }, + "agcgw_all" : { + "SG" : "connect-dra.dbankcloud.cn", + "SG_back" : "connect-dra.hispace.hicloud.com", + "CN" : "connect-drcn.dbankcloud.cn", + "CN_back" : "connect-drcn.hispace.hicloud.com", + "RU" : "connect-drru.hispace.dbankcloud.ru", + "RU_back" : "connect-drru.hispace.dbankcloud.cn", + "DE" : "connect-dre.dbankcloud.cn", + "DE_back" : "connect-dre.hispace.hicloud.com" + }, + "websocketgw_all" : { + "SG" : "connect-ws-dra.hispace.dbankcloud.cn", + "SG_back" : "connect-ws-dra.hispace.dbankcloud.com", + "CN" : "connect-ws-drcn.hispace.dbankcloud.cn", + "CN_back" : "connect-ws-drcn.hispace.dbankcloud.com", + "RU" : "connect-ws-drru.hispace.dbankcloud.ru", + "RU_back" : "connect-ws-drru.hispace.dbankcloud.cn", + "DE" : "connect-ws-dre.hispace.dbankcloud.cn", + "DE_back" : "connect-ws-dre.hispace.dbankcloud.com" + }, + "client" : { + "cp_id" : "20007000036693451", + "product_id" : "461323198429446894", + "client_id" : "1595824543740483264", + "client_secret" : "2A1727B0D31AA9BE1F5D9E8A5CDA1A67B9EF084AC9FB68D6C633A3CCB08ECDEE", + "project_id" : "461323198429446894", + "app_id" : "113238569", + "api_key" : "DQEDAJNRITp9M3uc1rPHyT8PzLc7bnVcKJAeavZZk0TQNMx9ifvBebr+JOmZe9tEvjnLwv7c1hAHXmPGR/gWG86iABDrCVotq+NGnw==", + "package_name" : "com.eatvioapp.app" + }, + "oauth_client" : { + "client_id" : "113238569", + "client_type" : 1 + }, + "app_info" : { + "app_id" : "113238569", + "package_name" : "com.eatvioapp.app" + }, + "service" : { + "analytics" : { + "collector_url" : "datacollector-drru.dt.dbankcloud.ru,datacollector-drru.dt.hicloud.com", + "collector_url_cn" : "datacollector-drcn.dt.hicloud.com,datacollector-drcn.dt.dbankcloud.cn", + "collector_url_de" : "datacollector-dre.dt.hicloud.com,datacollector-dre.dt.dbankcloud.cn", + "collector_url_ru" : "datacollector-drru.dt.dbankcloud.ru,datacollector-drru.dt.hicloud.com", + "collector_url_sg" : "datacollector-dra.dt.hicloud.com,datacollector-dra.dt.dbankcloud.cn", + "resource_id" : "p1", + "channel_id" : "" + }, + "ml" : { + "mlservice_url" : "ml-api-drru.ai.dbankcloud.ru,ml-api-drru.ai.dbankcloud.cn" + }, + "cloudstorage" : { + "storage_url" : "https://agc-storage-drcn.platform.dbankcloud.cn", + "storage_url_ru" : "https://agc-storage-drru.cloud.huawei.ru", + "storage_url_sg" : "https://ops-dra.agcstorage.link", + "storage_url_de" : "https://ops-dre.agcstorage.link", + "storage_url_cn" : "https://agc-storage-drcn.platform.dbankcloud.cn", + "storage_url_ru_back" : "https://agc-storage-drru.cloud.huawei.ru", + "storage_url_sg_back" : "https://agc-storage-dra.cloud.huawei.asia", + "storage_url_de_back" : "https://agc-storage-dre.cloud.huawei.eu", + "storage_url_cn_back" : "https://agc-storage-drcn.cloud.huawei.com.cn" + }, + "search" : { + "url" : "https://search-drru.cloud.huawei.ru" + }, + "edukit" : { + "edu_url" : "edukit.cloud.huawei.com.cn", + "dh_url" : "edukit.cloud.huawei.com.cn" + } + }, + "region" : "RU", + "configuration_version" : "3.0", + "appInfos" : [ { + "package_name" : "com.eatvioapp.app", + "client" : { + "app_id" : "113238569" + }, + "app_info" : { + "package_name" : "com.eatvioapp.app", + "app_id" : "113238569" + }, + "oauth_client" : { + "client_type" : 1, + "client_id" : "113238569" + } + } ] +} \ No newline at end of file diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 864f1a2c..e7a94938 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -21,6 +21,12 @@ + + deleteComment({required int id}) async { try { return service.deleteComment(id); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -17,7 +19,8 @@ class CommentsRepository { Future updateComment({required int id, required String body}) async { try { return service.updateComment(id, CommentUpdateModel(body: body)); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } diff --git a/lib/core/constants/app_constants.dart b/lib/core/constants/app_constants.dart index f9c945c9..317ddb94 100644 --- a/lib/core/constants/app_constants.dart +++ b/lib/core/constants/app_constants.dart @@ -69,4 +69,9 @@ class AppConstants { static const String usersEndpoint = '/users'; static const String postsEndpoint = '/posts'; static const String commentsEndpoint = '/comments'; + + // Delay of pending requests + /// 30 секунд стоит потому что запрос может идти 12-15 секунд + /// Особенно если производится отладка на сервере разработчика + static const Duration pendingDelay = Duration(seconds: 30); } diff --git a/lib/core/di/external_registrations.dart b/lib/core/di/external_registrations.dart index aecd2707..34f93aa6 100644 --- a/lib/core/di/external_registrations.dart +++ b/lib/core/di/external_registrations.dart @@ -1,10 +1,12 @@ import 'package:dio/dio.dart'; import 'package:eatvio_app/core/app_config.dart'; +import 'package:eatvio_app/core/constants/app_constants.dart'; import 'package:eatvio_app/core/interceptors/phone_verify_error_interceptor.dart'; import 'package:eatvio_app/core/interceptors/token_interceptor.dart'; import 'package:eatvio_app/core/prefs.dart'; import 'package:get_it/get_it.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import 'package:sentry_dio/sentry_dio.dart'; /// Регистрация внешних зависимостей в системе внедрения зависимостей class ExternalRegistrations { @@ -19,15 +21,16 @@ class ExternalRegistrations { /// Регистрация Dio HTTP клиента static void _registerDio() { sl.registerLazySingleton( - () => - Dio(BaseOptions( - baseUrl: sl().baseUrl, - headers: {'accept': 'application/json', 'content-type': 'application/json'}, - connectTimeout: const Duration(seconds: 10), - receiveTimeout: const Duration(seconds: 10), - sendTimeout: const Duration(seconds: 10), - )) - ..interceptors.addAll([TokenInterceptor(), PhoneVerifyErrorInterceptor()]), + () => Dio( + BaseOptions( + baseUrl: sl().baseUrl, + headers: {'accept': 'application/json', 'content-type': 'application/json'}, + connectTimeout: AppConstants.pendingDelay, + receiveTimeout: AppConstants.pendingDelay, + sendTimeout: AppConstants.pendingDelay, + ), + )..interceptors.addAll([TokenInterceptor(), PhoneVerifyErrorInterceptor()]) + ..addSentry(), ); } diff --git a/lib/core/firebase_api.dart b/lib/core/firebase_api.dart index a96aa9d5..e85807ee 100644 --- a/lib/core/firebase_api.dart +++ b/lib/core/firebase_api.dart @@ -12,6 +12,7 @@ import 'package:eatvio_app/core/locator_service.dart'; import 'package:eatvio_app/core/token_storage.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/services.dart' show MethodChannel, PlatformException; +import 'package:sentry_flutter/sentry_flutter.dart'; class FirebaseApi { final _firebaseMessaging = FirebaseMessaging.instance; @@ -49,7 +50,7 @@ class FirebaseApi { await sl().post( '/api/anonymous_token', data: { - 'platform': Platform.isIOS ? 'ios' : 'android', + 'platform': Platform.isIOS ? 'ios' : (await _isHuaweiDevice()) ? 'huawei' : 'android', 'hw_id': Platform.isIOS ? (await deviceInfo.iosInfo).identifierForVendor : (await deviceInfo.androidInfo).id, 'token': await getToken(), }, @@ -61,6 +62,7 @@ class FirebaseApi { ); } catch (e, st) { log(e.toString(), stackTrace: st); + await Sentry.captureException(e, stackTrace: st); // Не пробрасываем ошибку дальше, так как это не критичная операция } } @@ -85,6 +87,7 @@ class FirebaseApi { ); } catch (e, st) { log(e.toString(), stackTrace: st); + await Sentry.captureException(e, stackTrace: st); // Не пробрасываем ошибку дальше, так как это не критичная операция } } @@ -110,10 +113,12 @@ class FirebaseApi { } on PlatformException catch (e, st) { showErrorToast('$e $st'); log(e.toString(), stackTrace: st); + await Sentry.captureException(e, stackTrace: st); return null; } catch (e, st) { showErrorToast('$e $st'); log(e.toString(), stackTrace: st); + await Sentry.captureException(e, stackTrace: st); return null; } } diff --git a/lib/core/interceptors/token_interceptor.dart b/lib/core/interceptors/token_interceptor.dart index b2cd9207..05f6be0c 100644 --- a/lib/core/interceptors/token_interceptor.dart +++ b/lib/core/interceptors/token_interceptor.dart @@ -1,6 +1,8 @@ import 'package:dio/dio.dart'; import 'package:eatvio_app/core/app_router.dart'; import 'package:eatvio_app/core/token_storage.dart'; +import 'package:eatvio_app/core/websockets/ws_manager.dart'; +import 'package:eatvio_app/core/locator_service.dart'; import 'package:go_router/go_router.dart'; class TokenInterceptor extends Interceptor { @@ -23,6 +25,7 @@ class TokenInterceptor extends Interceptor { ErrorInterceptorHandler handler, ) async { if (err.response?.statusCode == 401) { + await sl().clear(); await TokenStorage.removeTokenInStorage(); AppRouter.navigatorKey.currentContext?.go('/auth-screen'); } diff --git a/lib/core/theme/theme_helper.dart b/lib/core/theme/theme_helper.dart index 01f9a96b..d1344e62 100644 --- a/lib/core/theme/theme_helper.dart +++ b/lib/core/theme/theme_helper.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:google_fonts/google_fonts.dart'; + /// Helper class for managing themes and colors. class ThemeHelper { @@ -34,7 +34,8 @@ class ThemeHelper { var colorScheme = _supportedColorScheme[_appTheme] ?? ColorSchemes.primaryColorScheme; return ThemeData( - textTheme: GoogleFonts.mulishTextTheme(), + fontFamily: 'Mulish-Local', + textTheme: ThemeData.light().textTheme.apply(fontFamily: 'Mulish-Local'), visualDensity: VisualDensity.standard, colorScheme: colorScheme, scaffoldBackgroundColor: colorScheme.onPrimary, diff --git a/lib/core/utils/validation_utils.dart b/lib/core/utils/validation_utils.dart index 1add6cda..0e9bbccd 100644 --- a/lib/core/utils/validation_utils.dart +++ b/lib/core/utils/validation_utils.dart @@ -16,6 +16,21 @@ class ValidationUtils { return regex.hasMatch(phone); } + /// Нормализация номера телефона (+79991234455) + static String normalizePhone(String phone) { + if (phone.isEmpty) return ''; + final digits = phone.replaceAll(RegExp(r'[^\d]'), ''); + if (digits.isEmpty) return ''; + + String normalized = digits; + if (digits.startsWith('8')) { + normalized = '7${digits.substring(1)}'; + } else if (digits.length == 10) { + normalized = '7$digits'; + } + return '+$normalized'; + } + /// Валидация URL static bool isValidUrl(String url) { if (url.isEmpty) return false; diff --git a/lib/core/views/repository/views_repository.dart b/lib/core/views/repository/views_repository.dart index 165a4956..869819ca 100644 --- a/lib/core/views/repository/views_repository.dart +++ b/lib/core/views/repository/views_repository.dart @@ -1,5 +1,6 @@ import 'package:eatvio_app/core/views/service/views_service.dart'; import 'package:eatvio_app/features/feed/models/view_model.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; class ViewsRepository { final ViewsService viewsService; @@ -9,7 +10,8 @@ class ViewsRepository { Future viewContent({required ViewModel viewModel}) async { try { await viewsService.viewContent(viewModel: viewModel); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } diff --git a/lib/core/websockets/ws_manager.dart b/lib/core/websockets/ws_manager.dart index 24430f6c..3a711177 100644 --- a/lib/core/websockets/ws_manager.dart +++ b/lib/core/websockets/ws_manager.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:convert'; import 'dart:developer'; @@ -8,6 +9,7 @@ import 'package:eatvio_app/core/const.dart'; import 'package:eatvio_app/core/locator_service.dart'; import 'package:eatvio_app/core/websockets/ws_service.dart'; import 'package:eatvio_app/core/token_storage.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:eatvio_app/features/chat/presentation/bloc/chats_cubit/chats_cubit.dart'; class WsManager { @@ -22,6 +24,10 @@ class WsManager { PrivateChannel? _privateMessagesChannel; PrivateChannel? _privateDirectChatChannel; + StreamSubscription? _lifecycleSubscription; + StreamSubscription? _eventSubscription; + StreamSubscription? _connectionEstablishedSubscription; + Stream? get newMessagesStream => _privateMessagesChannel?.bind('App\\Events\\ChatMessageUpdated'); Stream? get newNotificationsStream => _privateNotificationChannel?.bind('Illuminate\\Notifications\\Events\\BroadcastNotificationCreated'); @@ -33,7 +39,7 @@ class WsManager { Future initialize() async { if (client == null) { try { - await TokenStorage.haveTokenInStorage(); + if (!await TokenStorage.haveTokenInStorage()) return; final wsUrl = await wsService.getWebsocket(); final options = PusherChannelsOptions.fromHost( @@ -71,7 +77,7 @@ class WsManager { } } - client?.eventStream.listen((event) async { + _connectionEstablishedSubscription = client?.eventStream.listen((event) async { if (event.name == 'pusher:connection_established') { final data = event.data; final socketId = extractSocketId(data); @@ -87,21 +93,26 @@ class WsManager { await _initPrivateNotificationsChannel(); await _initPrivateMessagesChannel(); - client?.lifecycleStream.listen((data) { + _lifecycleSubscription = client?.lifecycleStream.listen((data) { if (data == PusherChannelsClientLifeCycleState.establishedConnection) { _unsubscribe(); _subscribe(); } }); - client?.eventStream.listen((event) { - EasyDebounce.debounce( - 'searchChatRoom', - const Duration(milliseconds: 300), - () => sl().getChatRooms(), - ); + _eventSubscription = client?.eventStream.listen((event) { + log('WsManager received event: ${event.name}'); + // Только если это не системные сообщения Pusher, обновляем список чатов + if (!event.name.startsWith('pusher:')) { + EasyDebounce.debounce( + 'searchChatRoom', + const Duration(milliseconds: 300), + () => sl().getChatRooms(), + ); + } }); _subscribe(); } catch (e, st) { + await Sentry.captureException(e, stackTrace: st); log(e.toString(), stackTrace: st); } } @@ -166,6 +177,12 @@ class WsManager { Future clear() async { await client?.disconnect(); + await _lifecycleSubscription?.cancel(); + await _eventSubscription?.cancel(); + await _connectionEstablishedSubscription?.cancel(); + _lifecycleSubscription = null; + _eventSubscription = null; + _connectionEstablishedSubscription = null; client = null; userId = null; _privateDirectChatChannel?.unsubscribe(); diff --git a/lib/core/widgets/feed_icon_button.dart b/lib/core/widgets/feed_icon_button.dart index bd5748f1..ee96cd0b 100644 --- a/lib/core/widgets/feed_icon_button.dart +++ b/lib/core/widgets/feed_icon_button.dart @@ -27,7 +27,7 @@ class FeedIconButton extends StatelessWidget { child: Row( mainAxisSize: MainAxisSize.min, children: [ - svgIcon ?? const SizedBox.shrink(), + ?svgIcon, if (icon != null) Icon( icon, diff --git a/lib/features/auth/presentation/pages/register_via_social_screen.dart b/lib/features/auth/presentation/pages/register_via_social_screen.dart index 4967930e..1b27237f 100644 --- a/lib/features/auth/presentation/pages/register_via_social_screen.dart +++ b/lib/features/auth/presentation/pages/register_via_social_screen.dart @@ -16,6 +16,7 @@ import 'package:eatvio_app/features/create_diet/presentation/widgets/error_widge import 'package:eatvio_app/features/notifications/presentation/notifications_cubit/notifications_cubit.dart'; import 'package:eatvio_app/core/firebase_api.dart'; import 'package:eatvio_app/core/utils/toasts.dart'; +import 'package:eatvio_app/core/utils/validation_utils.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -330,6 +331,25 @@ class _RegisterViaSocialScreenState extends State { Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ + SizedBox( + width: MediaQuery.of(context).size.width / 2 - 22, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text('Рост'), + const SizedBox(height: 4), + CustomTextFormField( + controller: heightController, + hintText: 'Введите рост', + keyboardType: TextInputType.number, + onChanged: (newValue) {}, + hasError: heightError, + ), + if (heightError) ...[const SizedBox(height: 4), const ValidationErrorWidget(error: 'Рост должен быть от 100 до 250 см')], + ], + ), + ), + const SizedBox(width: 12), SizedBox( width: MediaQuery.of(context).size.width / 2 - 22, child: Column( @@ -349,25 +369,6 @@ class _RegisterViaSocialScreenState extends State { ], ), ), - const SizedBox(width: 12), - SizedBox( - width: MediaQuery.of(context).size.width / 2 - 22, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text('Рост'), - const SizedBox(height: 4), - CustomTextFormField( - controller: heightController, - hintText: 'Введите рост', - keyboardType: TextInputType.number, - onChanged: (newValue) {}, - hasError: heightError, - ), - if (heightError) ...[const SizedBox(height: 4), const ValidationErrorWidget(error: 'Рост должен быть от 100 до 250 см')], - ], - ), - ), ], ), const SizedBox(height: 28), @@ -538,7 +539,7 @@ class _RegisterViaSocialScreenState extends State { if (hasErrorWeightOrHeightError) return; // Очищаем номер телефона от маски для отправки - final cleanPhone = phoneController.text.replaceAll(RegExp(r'[^\d+]'), ''); + final cleanPhone = ValidationUtils.normalizePhone(phoneController.text); if (clientID == null) return; // Вызываем OAuth регистрацию @@ -615,7 +616,7 @@ class _RegisterViaSocialScreenState extends State { final oauthCubit = CubitRegistrations.sl(); // Всегда передаем и email и phone final String email = emailController.text; - final String phone = phoneController.text.replaceAll(RegExp(r'[^\d+]'), ''); + final String phone = ValidationUtils.normalizePhone(phoneController.text); final response = await oauthCubit.requestOAuthVerification(email: email, phone: phone); @@ -726,7 +727,7 @@ class _RegisterViaSocialScreenState extends State { // Всегда передаем и email и phone (полный набор данных от провайдера) // Используем данные из формы, если они есть, иначе из authResult final String email = emailController.text.isNotEmpty ? emailController.text : (authResult.email ?? ''); - final String phone = phoneController.text.isNotEmpty ? phoneController.text.replaceAll(RegExp(r'[^\d+]'), '') : (authResult.phone ?? ''); + final String phone = phoneController.text.isNotEmpty ? ValidationUtils.normalizePhone(phoneController.text) : ValidationUtils.normalizePhone(authResult.phone ?? ''); // Проверяем, что данные есть if (email.isEmpty && phone.isEmpty) { diff --git a/lib/features/auth/repositories/auth_repository.dart b/lib/features/auth/repositories/auth_repository.dart index eedd295a..6c2b4c15 100644 --- a/lib/features/auth/repositories/auth_repository.dart +++ b/lib/features/auth/repositories/auth_repository.dart @@ -7,6 +7,7 @@ import 'package:eatvio_app/features/auth/models/oauth_verification_confirm_respo import 'package:eatvio_app/features/auth/models/oauth_verification_request_response.dart'; import 'package:eatvio_app/features/auth/models/token_model.dart'; import 'package:eatvio_app/features/auth/services/auth_service.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; class AuthRepository { final AuthService authService; @@ -18,7 +19,8 @@ class AuthRepository { return await authService.getTokenByCredentials( credentials: CredentialsModel(email: email, password: password), ); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -33,7 +35,8 @@ class AuthRepository { return await authService.signInViaSocialMedia( body: OAuthLoginRequest(provider: provider, accessToken: accessToken, refreshToken: refreshToken, clientId: clientId), ); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -72,7 +75,8 @@ class AuthRepository { ), ))['token']! as String; - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -89,7 +93,8 @@ class AuthRepository { return await authService.requestOAuthVerification(body: body); } return null; - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -134,8 +139,9 @@ class AuthRepository { 'AuthRepository: Sending OAuth verification confirm request: provider=$provider, verificationType=$verificationType, code=$code, email=$email, phone=$phone', ); return await authService.confirmOAuthVerification(body: request); - } catch (e) { + } catch (e, s) { log('AuthRepository: Error in confirmOAuthVerification: $e'); + await Sentry.captureException(e, stackTrace: s); rethrow; } } diff --git a/lib/features/auth/repositories/reset_password_repository.dart b/lib/features/auth/repositories/reset_password_repository.dart index e0b1d69f..d04270cb 100644 --- a/lib/features/auth/repositories/reset_password_repository.dart +++ b/lib/features/auth/repositories/reset_password_repository.dart @@ -1,4 +1,5 @@ import 'package:eatvio_app/features/auth/services/reset_password_service.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; class ResetPasswordRepository { final ResetPasswordService resetPasswordService; @@ -20,7 +21,8 @@ class ResetPasswordRepository { } else { return false; } - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -46,7 +48,8 @@ class ResetPasswordRepository { } else { return false; } - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } diff --git a/lib/features/auth/sdk/social_auth_facade.dart b/lib/features/auth/sdk/social_auth_facade.dart index de6e14e7..af787e61 100644 --- a/lib/features/auth/sdk/social_auth_facade.dart +++ b/lib/features/auth/sdk/social_auth_facade.dart @@ -11,6 +11,7 @@ import 'package:eatvio_app/core/env_config.dart'; import 'package:eatvio_app/generated/l10n.dart'; import 'package:go_router/go_router.dart'; import 'package:http/http.dart' as http; +import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:vk_id/vk_id.dart'; import 'package:webview_flutter/webview_flutter.dart'; @@ -41,7 +42,8 @@ class SocialAuthFacade { return null; } return await _parseVKAuthResult(redirectParams: redirectParams, controller: controller, codeVerifier: codeVerifier, clientId: clientIdStr); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); return null; } } @@ -157,8 +159,9 @@ class SocialAuthFacade { birthDate: yandexProfileData.birthday, clientId: clientId, ); - } catch (e) { + } catch (e, s) { log(e.toString()); + await Sentry.captureException(e, stackTrace: s); } return null; } @@ -205,7 +208,8 @@ class SocialAuthFacade { if (code == null || deviceId == null) return null; return await _parseVKAuthResult(redirectParams: redirectParams, controller: controller, codeVerifier: codeVerifier, clientId: clientIdStr); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); return null; } } diff --git a/lib/features/barcode/repositories/barcode_repository.dart b/lib/features/barcode/repositories/barcode_repository.dart index b83ace5c..030eca7e 100644 --- a/lib/features/barcode/repositories/barcode_repository.dart +++ b/lib/features/barcode/repositories/barcode_repository.dart @@ -1,6 +1,7 @@ import 'package:dio/dio.dart'; import 'package:eatvio_app/features/barcode/models/barcode_product_model.dart'; import 'package:eatvio_app/features/barcode/services/barcode_service.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; class BarcodeRepository { final BarcodeService barcodeService; @@ -28,14 +29,16 @@ class BarcodeRepository { } throw Exception('Unexpected status code: ${response.statusCode}'); - } on DioException catch (e) { + } on DioException catch (e, s) { if (e.response?.statusCode == 202) { // Если получен статус 202, значит запущена задача обработки final taskStatus = BarcodeTaskStatusModel.fromJson(e.response!.data as Map); return BarcodeResult.processing(taskStatus.taskId ?? ''); } + await Sentry.captureException(e, stackTrace: s); rethrow; - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -52,7 +55,8 @@ class BarcodeRepository { } else { return BarcodeTaskStatus.processing(); } - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } diff --git a/lib/features/blog_post_create/repositories/blog_post_repository.dart b/lib/features/blog_post_create/repositories/blog_post_repository.dart index 48f4f0c1..7f1fc49b 100644 --- a/lib/features/blog_post_create/repositories/blog_post_repository.dart +++ b/lib/features/blog_post_create/repositories/blog_post_repository.dart @@ -8,6 +8,7 @@ import 'package:eatvio_app/core/image_compressing/image_compress_utility.dart'; import 'package:eatvio_app/features/blog_post_create/services/blog_post_service.dart'; import 'package:flutter_image_compress/flutter_image_compress.dart'; import 'package:path_provider/path_provider.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; class BlogPostRepository { final BlogPostService blogPostService; @@ -29,9 +30,14 @@ class BlogPostRepository { if (image != null) 'image': await MultipartFile.fromFile((await ImageCompressUtility.compressFile(File(image.path))).path, filename: image.name), }); - return await blogPostService.createBlogPost( - formData: formData, - ); + try { + return await blogPostService.createBlogPost( + formData: formData, + ); + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); + rethrow; + } } Future downloadImage(String imageUrl) async { @@ -54,8 +60,9 @@ class BlogPostRepository { log('Ошибка загрузки изображения: ${response.statusCode}'); return null; } - } catch (e) { + } catch (e, s) { log('Произошла ошибка: $e'); + await Sentry.captureException(e, stackTrace: s); return null; } } @@ -79,11 +86,23 @@ class BlogPostRepository { : null, }); - return await blogPostService.updateBlogPost( - id: id, - formData: formData, - ); + try { + return await blogPostService.updateBlogPost( + id: id, + formData: formData, + ); + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); + rethrow; + } } - Future deleteBlogPost(int id) async => await blogPostService.deleteBlogPost(id: id); + Future deleteBlogPost(int id) async { + try { + await blogPostService.deleteBlogPost(id: id); + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); + rethrow; + } + } } diff --git a/lib/features/bottom_navigation_bar/presentation/widgets/bottom_navigation_bar_widget.dart b/lib/features/bottom_navigation_bar/presentation/widgets/bottom_navigation_bar_widget.dart index f177d57a..efac96bf 100644 --- a/lib/features/bottom_navigation_bar/presentation/widgets/bottom_navigation_bar_widget.dart +++ b/lib/features/bottom_navigation_bar/presentation/widgets/bottom_navigation_bar_widget.dart @@ -1,5 +1,6 @@ import 'package:badges/badges.dart' as badges; import 'package:eatvio_app/core/app_config.dart'; +import 'package:eatvio_app/core/env_config.dart'; import 'package:eatvio_app/core/locator_service.dart'; import 'package:eatvio_app/core/utils/image_constant.dart'; import 'package:eatvio_app/features/dev_tools/presentation/dev_tools_screen.dart'; @@ -19,10 +20,7 @@ import 'package:go_router/go_router.dart'; class BottomNavigationBarWidget extends StatefulWidget { final Widget child; - const BottomNavigationBarWidget({ - super.key, - required this.child, - }); + const BottomNavigationBarWidget({super.key, required this.child}); @override State createState() => _BottomNavigationBarWidgetState(); @@ -35,24 +33,9 @@ class _BottomNavigationBarWidgetState extends State { List _getPages(bool isConsultant) { if (isConsultant) { - return [ - '/my-profile-screen', - '/feed-screen', - '/diary-screen', - '/consultations-screen', - '/chat-select-screen', - '/notifications-screen', - '/menu-screen', - ]; + return ['/my-profile-screen', '/feed-screen', '/diary-screen', '/consultations-screen', '/chat-select-screen', '/notifications-screen', '/menu-screen']; } else { - return [ - '/my-profile-screen', - '/feed-screen', - '/diary-screen', - '/chat-select-screen', - '/notifications-screen', - '/menu-screen', - ]; + return ['/my-profile-screen', '/feed-screen', '/diary-screen', '/chat-select-screen', '/notifications-screen', '/menu-screen']; } } @@ -83,6 +66,49 @@ class _BottomNavigationBarWidgetState extends State { } } + static const Color _devAccentColor = Color(0xFFF59E0B); // amber-500 + + Widget _buildLogoIcon() { + final isProduction = sl().baseUrl == EnvConfig.baseUrl; + final logo = SvgPicture.asset( + ImageConstant.imgLogo, + height: 32, + colorFilter: isProduction ? null : const ColorFilter.mode(_devAccentColor, BlendMode.srcIn), + ); + if (isProduction) return logo; + return Stack( + clipBehavior: Clip.none, + alignment: Alignment.center, + children: [ + Container( + padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 4), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + border: Border.all(color: _devAccentColor.withValues(alpha: 0.6), width: 1.5), + color: _devAccentColor.withValues(alpha: 0.08), + ), + child: logo, + ), + Positioned( + top: -6, + right: -4, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 1), + decoration: BoxDecoration( + color: _devAccentColor, + borderRadius: BorderRadius.circular(4), + boxShadow: [BoxShadow(color: _devAccentColor.withValues(alpha: 0.4), blurRadius: 4, offset: const Offset(0, 1))], + ), + child: const Text( + 'DEV', + style: TextStyle(fontSize: 8, fontWeight: FontWeight.w700, color: Colors.white, letterSpacing: 0.3, height: 1.0), + ), + ), + ), + ], + ); + } + @override Widget build(BuildContext context) { final s = S.of(context); @@ -91,10 +117,10 @@ class _BottomNavigationBarWidgetState extends State { if (state is ProfileAvatarStateLoaded) { final profileModel = state.profile; final isConsultant = profileModel.roles.contains('consultant'); - + // Инициализируем pages на основе profileModel pages ??= _getPages(isConsultant); - + return Scaffold( body: widget.child, bottomNavigationBar: BottomNavigationBar( @@ -117,52 +143,26 @@ class _BottomNavigationBarWidgetState extends State { ), BottomNavigationBarItem( label: 'Лента', - icon: Padding( - padding: const EdgeInsets.only(bottom: 2), - child: SvgPicture.asset( - colorFilter: const ColorFilter.mode(Colors.grey, BlendMode.srcIn), - width: 20, - height: 20, - ImageConstant.icnFeed, - ), - ), - activeIcon: Padding( - padding: const EdgeInsets.only(bottom: 2), - child: SvgPicture.asset( - ImageConstant.icnFeed, - width: 20, - height: 20, - colorFilter: ColorFilter.mode( - appTheme.blueA400, - BlendMode.srcIn, - ), - ), - ), + icon: Padding( + padding: const EdgeInsets.only(bottom: 2), + child: SvgPicture.asset(colorFilter: const ColorFilter.mode(Colors.grey, BlendMode.srcIn), width: 20, height: 20, ImageConstant.icnFeed), + ), + activeIcon: Padding( + padding: const EdgeInsets.only(bottom: 2), + child: SvgPicture.asset(ImageConstant.icnFeed, width: 20, height: 20, colorFilter: ColorFilter.mode(appTheme.blueA400, BlendMode.srcIn)), + ), ), BottomNavigationBarItem( label: s.diary, - icon: Padding( - padding: const EdgeInsets.only(bottom: 2), - child: SvgPicture.asset( - colorFilter: const ColorFilter.mode(Colors.grey, BlendMode.srcIn), - ImageConstant.icnDiary, - width: 20, - height: 20, - ), - ), - activeIcon: Padding( - padding: const EdgeInsets.only(bottom: 2), - child: SvgPicture.asset( - ImageConstant.icnDiary, - width: 20, - height: 20, - colorFilter: ColorFilter.mode( - appTheme.blueA400, - BlendMode.srcIn, + icon: Padding( + padding: const EdgeInsets.only(bottom: 2), + child: SvgPicture.asset(colorFilter: const ColorFilter.mode(Colors.grey, BlendMode.srcIn), ImageConstant.icnDiary, width: 20, height: 20), + ), + activeIcon: Padding( + padding: const EdgeInsets.only(bottom: 2), + child: SvgPicture.asset(ImageConstant.icnDiary, width: 20, height: 20, colorFilter: ColorFilter.mode(appTheme.blueA400, BlendMode.srcIn)), + ), ), - ), - ), - ), if (profileModel.roles.contains('consultant')) BottomNavigationBarItem( label: 'Консультации', @@ -181,97 +181,69 @@ class _BottomNavigationBarWidgetState extends State { 'assets/icons/consultations.svg', width: 20, height: 20, - colorFilter: ColorFilter.mode( - appTheme.blueA400, - BlendMode.srcIn, - ), - ), - ), - ), + colorFilter: ColorFilter.mode(appTheme.blueA400, BlendMode.srcIn), + ), + ), + ), BottomNavigationBarItem( label: s.chat, - icon: BlocBuilder( - builder: (BuildContext context, ChatsState state) { - final hasUnreadMessages = state.chatsWithUnreadMessagesCount > 0; - return badges.Badge( - position: badges.BadgePosition.topEnd(), - badgeAnimation: const badges.BadgeAnimation.fade(), - badgeStyle: badges.BadgeStyle( - badgeColor: hasUnreadMessages ? Colors.red : Colors.transparent, + icon: BlocBuilder( + builder: (BuildContext context, ChatsState state) { + final hasUnreadMessages = state.chatsWithUnreadMessagesCount > 0; + return badges.Badge( + position: badges.BadgePosition.topEnd(), + badgeAnimation: const badges.BadgeAnimation.fade(), + badgeStyle: badges.BadgeStyle(badgeColor: hasUnreadMessages ? Colors.red : Colors.transparent), + badgeContent: Text( + hasUnreadMessages ? state.chatsWithUnreadMessagesCount.toString() : '', + style: TextStyle(color: hasUnreadMessages ? Colors.white : Colors.transparent, fontSize: 8), + ), + child: Padding( + padding: const EdgeInsets.only(bottom: 2), + child: SvgPicture.asset( + ImageConstant.icnChat, + width: 20, + height: 20, + colorFilter: pages![currentBottomNavBarIndex] == '/chat-select-screen' + ? ColorFilter.mode(appTheme.blueA400, BlendMode.srcIn) + : const ColorFilter.mode(Colors.grey, BlendMode.srcIn), + ), + ), + ); + }, ), - badgeContent: Text( - hasUnreadMessages ? state.chatsWithUnreadMessagesCount.toString() : '', - style: TextStyle( - color: hasUnreadMessages ? Colors.white : Colors.transparent, - fontSize: 8, - ), - ), - child: Padding( - padding: const EdgeInsets.only(bottom: 2), - child: SvgPicture.asset( - ImageConstant.icnChat, - width: 20, - height: 20, - colorFilter: pages![currentBottomNavBarIndex] == '/chat-select-screen' - ? ColorFilter.mode( - appTheme.blueA400, - BlendMode.srcIn, - ) - : const ColorFilter.mode(Colors.grey, BlendMode.srcIn), - ), - ), - ); - }, - ), ), BottomNavigationBarItem( label: 'Уведомления', - icon: BlocBuilder( - builder: (BuildContext context, NotificationsState state) { - return badges.Badge( - position: badges.BadgePosition.topEnd(), - badgeAnimation: const badges.BadgeAnimation.fade(), - showBadge: state is NotificationsStateLoaded && state.hasUnreadNotifications, - badgeContent: const Text( - '', - style: TextStyle( - color: Colors.white, - fontSize: 8, - ), - ), - child: Padding( - padding: const EdgeInsets.only(bottom: 2), - child: SvgPicture.asset( - ImageConstant.icnBell, - colorFilter: pages![currentBottomNavBarIndex] == '/notifications-screen' - ? ColorFilter.mode( - appTheme.blueA400, - BlendMode.srcIn, - ) - : const ColorFilter.mode(Colors.grey, BlendMode.srcIn), - width: 20, - height: 20, - ), - ), - ); - }, - ), - ), - BottomNavigationBarItem( - label: '', - icon: SvgPicture.asset( - ImageConstant.imgLogo, - height: 32, + icon: BlocBuilder( + builder: (BuildContext context, NotificationsState state) { + return badges.Badge( + position: badges.BadgePosition.topEnd(), + badgeAnimation: const badges.BadgeAnimation.fade(), + showBadge: state is NotificationsStateLoaded && state.hasUnreadNotifications, + badgeContent: const Text('', style: TextStyle(color: Colors.white, fontSize: 8)), + child: Padding( + padding: const EdgeInsets.only(bottom: 2), + child: SvgPicture.asset( + ImageConstant.icnBell, + colorFilter: pages![currentBottomNavBarIndex] == '/notifications-screen' + ? ColorFilter.mode(appTheme.blueA400, BlendMode.srcIn) + : const ColorFilter.mode(Colors.grey, BlendMode.srcIn), + width: 20, + height: 20, + ), + ), + ); + }, ), ), + BottomNavigationBarItem(label: '', icon: _buildLogoIcon()), ], ), ); } // Показываем child даже когда профиль еще загружается, чтобы экраны могли инициализироваться - return Scaffold( - body: widget.child, - ); + return Scaffold(body: widget.child); }, ); } diff --git a/lib/features/calendar/repositories/calendar_repository.dart b/lib/features/calendar/repositories/calendar_repository.dart index fd9f734c..87af6b20 100644 --- a/lib/features/calendar/repositories/calendar_repository.dart +++ b/lib/features/calendar/repositories/calendar_repository.dart @@ -1,5 +1,6 @@ import 'package:eatvio_app/features/calendar/models/calendar_model.dart'; import 'package:eatvio_app/features/calendar/services/calendar_service.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; class CalendarRepository { final CalendarService calendarService; @@ -9,7 +10,8 @@ class CalendarRepository { Future> getCalendar() async { try { return await calendarService.getCalendar(); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } diff --git a/lib/features/cancel_plan_bottom_sheet/repository/cancel_plan_repository.dart b/lib/features/cancel_plan_bottom_sheet/repository/cancel_plan_repository.dart index 2e55549a..2a7ccc66 100644 --- a/lib/features/cancel_plan_bottom_sheet/repository/cancel_plan_repository.dart +++ b/lib/features/cancel_plan_bottom_sheet/repository/cancel_plan_repository.dart @@ -1,5 +1,6 @@ import 'package:eatvio_app/features/cancel_plan_bottom_sheet/models/cancelation_model.dart'; import 'package:eatvio_app/features/cancel_plan_bottom_sheet/service/cancel_plan_service.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; class CancelPlanRepository { final CancelPlanService cancelService; @@ -10,7 +11,8 @@ class CancelPlanRepository { }) async { try { await cancelService.cancelChosenPlan(id: id, body: body); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } diff --git a/lib/features/chat/repositories/chat_repository.dart b/lib/features/chat/repositories/chat_repository.dart index 0a3e2960..36682421 100644 --- a/lib/features/chat/repositories/chat_repository.dart +++ b/lib/features/chat/repositories/chat_repository.dart @@ -12,6 +12,7 @@ import 'package:eatvio_app/features/chat/models/plan_complain_model.dart'; import 'package:eatvio_app/features/chat/services/chat_service.dart'; import 'package:eatvio_app/features/plans/models/chosen_plan_model.dart'; import 'package:http_parser/http_parser.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; class ChatRepository { final ChatService chatService; @@ -60,6 +61,7 @@ class ChatRepository { ); } catch (e, st) { log(e.toString(), stackTrace: st); + await Sentry.captureException(e, stackTrace: st); rethrow; } } @@ -69,6 +71,7 @@ class ChatRepository { return (await chatService.getChatRoom(uuid: uuid)); } catch (e, st) { log(e.toString(), stackTrace: st); + await Sentry.captureException(e, stackTrace: st); rethrow; } } @@ -76,7 +79,8 @@ class ChatRepository { Future getChatRooms({String? query, int page = 1}) async { try { return (await chatService.getChatRooms(query: query, page: page)); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -84,7 +88,8 @@ class ChatRepository { Future getChatMessages(String id, int page) async { try { return (await chatService.getChatMessages(id: id, page: page)); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -92,7 +97,8 @@ class ChatRepository { Future getChatRoomIdByUserSlug({required String slug}) async { try { return (await chatService.getChatRoomIdByUserSlug(user: slug)); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -100,7 +106,8 @@ class ChatRepository { Future markMessagesAsRead(String id, List messageIds) async { try { await chatService.markMessagesAsRead(id: id, body: {'messages_ids': messageIds}); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -110,7 +117,8 @@ class ChatRepository { }) async { try { await chatService.cancelChosenPlan(id: id); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -118,7 +126,8 @@ class ChatRepository { Future choseStartPlanDate({required int id, required Map date}) async { try { await chatService.choseStartPlanDate(id: id, date: date); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -128,7 +137,8 @@ class ChatRepository { }) async { try { return await chatService.getPaymentLink(id: id); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -138,7 +148,8 @@ class ChatRepository { }) async { try { return await chatService.getChosenPlan(id: id); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -148,7 +159,8 @@ class ChatRepository { }) async { try { await chatService.finishChosenPlan(id: id); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -159,7 +171,8 @@ class ChatRepository { }) async { try { await chatService.complainAboutConsultation(id: id, body: body); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -172,7 +185,8 @@ class ChatRepository { } else { return response.data['id']; } - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); return 'error'; } } diff --git a/lib/features/complaining/repositories/complaining_repository.dart b/lib/features/complaining/repositories/complaining_repository.dart index 32aab577..10a1cce3 100644 --- a/lib/features/complaining/repositories/complaining_repository.dart +++ b/lib/features/complaining/repositories/complaining_repository.dart @@ -1,5 +1,6 @@ import 'package:eatvio_app/features/complaining/models/complain_model.dart'; import 'package:eatvio_app/features/complaining/services/complaining_service.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; class ComplainingRepository { final ComplainingService service; @@ -12,7 +13,8 @@ class ComplainingRepository { }) async { try { await service.sendPlanComplain(slug: slug, category: category, body: body); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -24,7 +26,8 @@ class ComplainingRepository { }) async { try { await service.sendRecipeComplain(slug: slug, category: category, body: body); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -36,7 +39,8 @@ class ComplainingRepository { }) async { try { await service.sendBlogPostsComplain(id: id, category: category, body: body); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -48,7 +52,8 @@ class ComplainingRepository { }) async { try { await service.sendProfileComplain(slug: slug, category: category, body: body); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -60,7 +65,8 @@ class ComplainingRepository { }) async { try { await service.sendDietComplain(id: id, category: category, body: body); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -72,7 +78,8 @@ class ComplainingRepository { }) async { try { await service.sendCommentComplain(id: id, category: category, body: body); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } diff --git a/lib/features/consultant/become_consultant/repositories/consultant_repository.dart b/lib/features/consultant/become_consultant/repositories/consultant_repository.dart index 05f357c3..b3a95e7f 100644 --- a/lib/features/consultant/become_consultant/repositories/consultant_repository.dart +++ b/lib/features/consultant/become_consultant/repositories/consultant_repository.dart @@ -5,6 +5,7 @@ import 'package:eatvio_app/features/consultant/become_consultant/models/consulta import 'package:eatvio_app/features/consultant/become_consultant/models/consultation_request_model.dart'; import 'package:eatvio_app/features/consultant/become_consultant/services/consultant_service.dart'; import 'package:http_parser/http_parser.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; class ConsultantRepository { final ConsultantService consultantService; @@ -18,7 +19,8 @@ class ConsultantRepository { } else { return await consultantService.consultantRegistration(consultantModel: consultant); } - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -26,7 +28,8 @@ class ConsultantRepository { Future getConsultantRequestStatus(String slug) async { try { return await consultantService.getConsultantRequestStatus(slug: slug); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -34,7 +37,8 @@ class ConsultantRepository { Future getMyRequestStatus(String slug) async { try { return await consultantService.getMyRequestStatus(slug: slug); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -42,7 +46,8 @@ class ConsultantRepository { Future> getConsultantCertPhotos() async { try { return await consultantService.getConsultantCertPhotos(); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -62,7 +67,8 @@ class ConsultantRepository { } return await consultantService.storeConsultantCertPhotos(photos: multipartFiles); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -70,7 +76,8 @@ class ConsultantRepository { Future hideConsultantCertPhoto(int id) async { try { await consultantService.hideConsultantCertPhoto(payload: {'cert_photo_id': id}); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -78,7 +85,8 @@ class ConsultantRepository { Future unHideConsultantCertPhoto(int id) async { try { await consultantService.unHideConsultantCertPhoto(payload: {'cert_photo_id': id}); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } diff --git a/lib/features/consultant_rating/repositories/consultant_rating_repository.dart b/lib/features/consultant_rating/repositories/consultant_rating_repository.dart index b4ac3770..3d66e879 100644 --- a/lib/features/consultant_rating/repositories/consultant_rating_repository.dart +++ b/lib/features/consultant_rating/repositories/consultant_rating_repository.dart @@ -1,6 +1,7 @@ import 'package:eatvio_app/features/consultant_rating/models/rating_model.dart'; import 'package:eatvio_app/features/consultant_rating/models/statistics_model.dart'; import 'package:eatvio_app/features/consultant_rating/services/consultant_rating_service.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; class ConsultantRatingRepository { final ConsultantRatingService service; @@ -10,7 +11,8 @@ class ConsultantRatingRepository { Future getConsultantStatistics({String? date}) async { try { return await service.getConsultantStatistics(date: date); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -19,7 +21,8 @@ class ConsultantRatingRepository { try { final data = (await service.getScoreLogs(date: date)).data; return data; - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } diff --git a/lib/features/consultations/repositories/consultations_repository.dart b/lib/features/consultations/repositories/consultations_repository.dart index 15490dc9..06c2ee39 100644 --- a/lib/features/consultations/repositories/consultations_repository.dart +++ b/lib/features/consultations/repositories/consultations_repository.dart @@ -1,6 +1,7 @@ import 'package:eatvio_app/core/enums/filters_comparison_enum.dart'; import 'package:eatvio_app/features/consultations/models/consultation_model.dart'; import 'package:eatvio_app/features/consultations/services/consultation_service.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:eatvio_app/features/finances_and_consultations_archive/models/consultant_finance_model.dart'; import 'package:eatvio_app/features/finances_and_consultations_archive/models/statistic_model.dart'; import 'package:eatvio_app/features/finances_and_consultations_archive/models/user_purchases_model.dart'; @@ -13,7 +14,8 @@ class ConsultationsRepository { try { final consultations = await consultationService.getConsultations(); return consultations.data; - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -32,7 +34,8 @@ class ConsultationsRepository { dateComparison: dateComparison, ); return model.data; - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -40,7 +43,8 @@ class ConsultationsRepository { Future getStatistic() async { try { return await consultationService.getStatistics(); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -48,7 +52,8 @@ class ConsultationsRepository { Future> getConsultantFinances(String? date, FiltersComparisonEnum? dateComparison) async { try { return await consultationService.getConsultantFinances(date, dateComparison); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -56,7 +61,8 @@ class ConsultationsRepository { Future> getUserPurchases(String? date, FiltersComparisonEnum? dateComparison) async { try { return await consultationService.getUserPurchases(date, dateComparison); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } 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 59be7492..989ea170 100644 --- a/lib/features/create_diet/presentation/cubit/create_diet_cubit.dart +++ b/lib/features/create_diet/presentation/cubit/create_diet_cubit.dart @@ -1,7 +1,6 @@ import 'dart:convert'; import 'dart:io'; import 'dart:developer'; -import 'package:dio/dio.dart'; import 'package:eatvio_app/core/app_config.dart'; import 'package:eatvio_app/core/app_initializer.dart'; import 'package:eatvio_app/core/const.dart'; @@ -16,6 +15,7 @@ import 'package:eatvio_app/features/profile/repositories/profile_repository.dart import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_image_compress/flutter_image_compress.dart'; import 'package:http/http.dart' as http; +import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:path_provider/path_provider.dart'; class CreateDietCubit extends Cubit { @@ -347,10 +347,11 @@ class CreateDietCubit extends Cubit { emit(state.copyWith(published: dietState != DietState.draft, drafted: dietState == DietState.draft)); return null; - } on DioException catch (e, stackTrace) { - log(e.toString(), stackTrace: stackTrace); + } catch (e, st) { + await Sentry.captureException(e, stackTrace: st); + log(e.toString(), stackTrace: st); + emit(state.copyWith(isDietPublishing: false)); } - emit(state.copyWith(isDietPublishing: false)); return 'error'; } @@ -381,6 +382,7 @@ class CreateDietCubit extends Cubit { ); return null; } catch (e, st) { + await Sentry.captureException(e, stackTrace: st); log(e.toString(), stackTrace: st); return 'error'; } diff --git a/lib/features/create_diet/repositories/create_diet_repository.dart b/lib/features/create_diet/repositories/create_diet_repository.dart index 825e9424..d63be30f 100644 --- a/lib/features/create_diet/repositories/create_diet_repository.dart +++ b/lib/features/create_diet/repositories/create_diet_repository.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:dio/dio.dart'; import 'package:eatvio_app/features/create_diet/services/create_diet_service.dart'; import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; class CreateDietRepository { final CreateDietService createDietService; @@ -18,7 +19,7 @@ class CreateDietRepository { required String? details, required File? image, required List>>? dailySchedule, - }) { + }) async { final dietMap = {}; if (dailySchedule != null) { for (final (dayIndex, day) in dailySchedule.indexed) { @@ -61,13 +62,18 @@ class CreateDietRepository { dietMap.addAll({'name': name, 'duration': duration}); final formData = FormData.fromMap(dietMap); - switch (state) { - case DietState.active: - return createDietService.createPublishedDiet(formData: formData); - case DietState.draft: - return createDietService.createDraftedDiet(formData: formData); - case DietState.personal: - return createDietService.createPersonalDiet(formData: formData); + try { + switch (state) { + case DietState.active: + return await createDietService.createPublishedDiet(formData: formData); + case DietState.draft: + return await createDietService.createDraftedDiet(formData: formData); + case DietState.personal: + return await createDietService.createPersonalDiet(formData: formData); + } + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); + rethrow; } } @@ -94,7 +100,8 @@ class CreateDietRepository { ); }); return dailySchedule; - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -111,7 +118,7 @@ class CreateDietRepository { String? details, File? image, List>>? dailySchedule, - }) { + }) async { final dietMap = {}; if (dailySchedule != null) { for (final (dayIndex, day) in dailySchedule.indexed) { @@ -154,13 +161,18 @@ class CreateDietRepository { } final formData = FormData.fromMap(dietMap); - switch (state) { - case DietState.active: - return createDietService.updatePublished(diet: dietId, formData: formData); - case DietState.draft: - return createDietService.updateDrafted(diet: dietId, formData: formData); - case DietState.personal: - return createDietService.updatePersonal(diet: dietId, formData: formData); + try { + switch (state) { + case DietState.active: + return await createDietService.updatePublished(diet: dietId, formData: formData); + case DietState.draft: + return await createDietService.updateDrafted(diet: dietId, formData: formData); + case DietState.personal: + return await createDietService.updatePersonal(diet: dietId, formData: formData); + } + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); + rethrow; } } } diff --git a/lib/features/create_plan/presentation/cubit/create_plan_cubit.dart b/lib/features/create_plan/presentation/cubit/create_plan_cubit.dart index 2289b31f..21b07b68 100644 --- a/lib/features/create_plan/presentation/cubit/create_plan_cubit.dart +++ b/lib/features/create_plan/presentation/cubit/create_plan_cubit.dart @@ -341,7 +341,7 @@ class CreatePlanCubit extends Cubit { bool validateSelectedOptionDescription() { final option = state.planData.options[state.selectedOptionIndex ?? 0]; final optionsValidationsStates = [...state.validationState.optionsValidationState]; - if (option.description.length < 3) { + if (option.description == null || option.description!.length < 3) { optionsValidationsStates[state.selectedOptionIndex ?? 0] = optionsValidationsStates[state.selectedOptionIndex ?? 0].copyWith(descriptionHasError: true); emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates)); return false; @@ -356,7 +356,7 @@ class CreatePlanCubit extends Cubit { final optionsValidationsStates = [...state.validationState.optionsValidationState]; bool result = true; - if (options[0].description.length < 3) { + if (options[0].description == null || options[0].description!.length < 3) { optionsValidationsStates[0] = optionsValidationsStates[0].copyWith(descriptionHasError: true); emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates)); result = false; @@ -365,7 +365,7 @@ class CreatePlanCubit extends Cubit { emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates)); } if (state.selectedOptionIndex == null) return result; - if (options[1].description.length < 3) { + if (options[1].description == null || options[1].description!.length < 3) { optionsValidationsStates[1] = optionsValidationsStates[1].copyWith(descriptionHasError: true); emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates)); result = false; @@ -374,7 +374,7 @@ class CreatePlanCubit extends Cubit { emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates)); } - if (options[2].description.length < 3) { + if (options[2].description == null || options[2].description!.length < 3) { optionsValidationsStates[2] = optionsValidationsStates[2].copyWith(descriptionHasError: true); emit(state.copyWith.validationState(optionsValidationState: optionsValidationsStates)); result = false; diff --git a/lib/features/create_plan/presentation/widgets/option_widget.dart b/lib/features/create_plan/presentation/widgets/option_widget.dart index 24cde52b..cc4eb816 100644 --- a/lib/features/create_plan/presentation/widgets/option_widget.dart +++ b/lib/features/create_plan/presentation/widgets/option_widget.dart @@ -94,7 +94,7 @@ class _OptionWidgetState extends State { final price = double.tryParse(customerPriceController.text.replaceAll('₽', '')); creatorPriceController.text = '${((price ?? 0) * 0.8).toStringAsFixed(2)}₽'; firstOptionTitleController.text = state.planData.options[selectedOptionIndex].short; - firstOptionDescriptionController.text = state.planData.options[selectedOptionIndex].description; + firstOptionDescriptionController.text = state.planData.options[selectedOptionIndex].description ?? ''; return MainBackgroundBouble( padding: const EdgeInsets.all(16), child: Column( diff --git a/lib/features/create_plan/repositories/create_plan_repository.dart b/lib/features/create_plan/repositories/create_plan_repository.dart index 96e961d7..1011005e 100644 --- a/lib/features/create_plan/repositories/create_plan_repository.dart +++ b/lib/features/create_plan/repositories/create_plan_repository.dart @@ -1,5 +1,6 @@ import 'package:dio/dio.dart'; import 'package:eatvio_app/features/create_plan/services/create_plan_service.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; class CreatePlanRepository { final CreatePlanService createPlanService; @@ -9,7 +10,13 @@ class CreatePlanRepository { Future createPlan({ required Map model, }) async { - final formData = FormData.fromMap(model); - return await createPlanService.createPlan(formData: formData); - } + try { + final formData = FormData.fromMap(model); + return await createPlanService.createPlan(formData: formData); + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); + rethrow; + } + } + } diff --git a/lib/features/create_recipe/presentation/bloc/create_recipe_cubit/create_recipe_cubit.dart b/lib/features/create_recipe/presentation/bloc/create_recipe_cubit/create_recipe_cubit.dart index ded7cb1f..8f7bca23 100644 --- a/lib/features/create_recipe/presentation/bloc/create_recipe_cubit/create_recipe_cubit.dart +++ b/lib/features/create_recipe/presentation/bloc/create_recipe_cubit/create_recipe_cubit.dart @@ -11,6 +11,7 @@ import 'package:eatvio_app/features/feed/models/recipes_model.dart'; import 'package:eatvio_app/features/glossary/models/glossary_model.dart'; import 'package:eatvio_app/features/glossary/repositories/glossary_repository.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:http/http.dart' as http; import 'package:path_provider/path_provider.dart'; @@ -165,6 +166,7 @@ class CreateRecipeCubit extends Cubit { await recipeRepository.publishRecipe(model, update, slug); return true; } catch (e, st) { + await Sentry.captureException(e, stackTrace: st); log(e.toString(), stackTrace: st); return false; } @@ -329,7 +331,8 @@ class CreateRecipeCubit extends Cubit { try { await recipeRepository.deleteRecipe(slug); return true; - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); return false; } } diff --git a/lib/features/create_recipe/reporitories/recipe_repository.dart b/lib/features/create_recipe/reporitories/recipe_repository.dart index 289233a2..b1b5fce9 100644 --- a/lib/features/create_recipe/reporitories/recipe_repository.dart +++ b/lib/features/create_recipe/reporitories/recipe_repository.dart @@ -6,6 +6,7 @@ import 'package:eatvio_app/features/create_recipe/models/recipe_model.dart'; import 'package:eatvio_app/features/create_recipe/models/recipe_tag_model.dart'; import 'package:eatvio_app/features/create_recipe/models/step.dart'; import 'package:eatvio_app/features/create_recipe/services/recipe_service.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:eatvio_app/features/create_recipe/models/product.dart'; class RecipeRepository { @@ -16,7 +17,8 @@ class RecipeRepository { Future>> inputProduct(String name) async { try { return await recipeService.getInputProduct(name); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -75,7 +77,8 @@ class RecipeRepository { } else { await recipeService.publishRecipe(formData: formData); } - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -83,7 +86,8 @@ class RecipeRepository { Future getRecipe(String recipeID) async { try { return await recipeService.getRecipe(recipeID); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -91,7 +95,8 @@ class RecipeRepository { Future> getRecipesTags() async { try { return (await recipeService.getTags()).data; - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -99,7 +104,8 @@ class RecipeRepository { Future deleteRecipe(String slug) async { try { return await recipeService.deleteRecipe(slug: slug); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } diff --git a/lib/features/diary/presentation/widgets/add_product_modal.dart b/lib/features/diary/presentation/widgets/add_product_modal.dart index c3d9067d..a0895bf9 100644 --- a/lib/features/diary/presentation/widgets/add_product_modal.dart +++ b/lib/features/diary/presentation/widgets/add_product_modal.dart @@ -132,7 +132,12 @@ class _AddProductModalState extends State { subtitle: glossariesList[i].grInPortion == 0 ? null : Text('${glossariesList[i].grInPortion} г', style: TextStyle(height: 1, color: Colors.grey.shade600)), - trailing: Icon(Icons.add_rounded, color: Colors.grey.shade400), + trailing: Icon( + productNameController.text.trim().toLowerCase() == glossariesList[i].name.toLowerCase() + ? Icons.radio_button_checked + : Icons.radio_button_off, + color: Colors.blueAccent, + ), contentPadding: const EdgeInsets.only(left: 12), ); }, @@ -166,7 +171,9 @@ class _AddProductModalState extends State { InkWell( onTap: () { // Автоматически выбираем первый элемент, если текст точно совпадает - if (selectedGlossary == null && glossariesList.isNotEmpty && productNameController.text.trim().toLowerCase() == glossariesList[0].name.toLowerCase()) { + if (selectedGlossary == null && + glossariesList.isNotEmpty && + productNameController.text.trim().toLowerCase() == glossariesList[0].name.toLowerCase()) { selectedGlossary = glossariesList[0]; } diff --git a/lib/features/diary/repositories/diary_repository.dart b/lib/features/diary/repositories/diary_repository.dart index 18081eac..f0f71b54 100644 --- a/lib/features/diary/repositories/diary_repository.dart +++ b/lib/features/diary/repositories/diary_repository.dart @@ -9,6 +9,7 @@ import 'package:eatvio_app/features/diary/models/nutrition_model.dart'; import 'package:eatvio_app/features/diary/models/product_moderation_model.dart'; import 'package:eatvio_app/features/diary/services/diary_service.dart'; import 'package:eatvio_app/features/glossary/models/glossary_model.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; class DiaryRepository { final DiaryService diaryService; @@ -69,67 +70,83 @@ class DiaryRepository { return updatedDiaryDay; } return diaryDay; - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } Future> getDiaryIndexByDate(String date) async { - return (await diaryService.getDiaryIndexByDate(date: date)); + try { + return (await diaryService.getDiaryIndexByDate(date: date)); + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); + rethrow; + } } Future getDiaryDay(int diaryId) async { - // Align with getDiaryByDate: enrich portions with glossary nutritional info - final diaryWrapper = await diaryService.getDiaryDay(diary: diaryId); - final DiaryDayModel diaryDay = diaryWrapper.diary; - Set ids = {}; - for (var meal in diaryDay.meals) { - for (var portion in meal.portions) { - if (portion.productNutritionalValueId != null) { - ids.add(portion.productNutritionalValueId!); + try { + // Align with getDiaryByDate: enrich portions with glossary nutritional info + final diaryWrapper = await diaryService.getDiaryDay(diary: diaryId); + final DiaryDayModel diaryDay = diaryWrapper.diary; + Set ids = {}; + for (var meal in diaryDay.meals) { + for (var portion in meal.portions) { + if (portion.productNutritionalValueId != null) { + ids.add(portion.productNutritionalValueId!); + } } } - } - if (ids.isNotEmpty) { - final glossary = await getGlossariesIndex(ids: ids.toList()); - final List updatedMeals = List.from(diaryDay.meals); - for (var mealIndex = 0; mealIndex < updatedMeals.length; mealIndex++) { - final meal = updatedMeals[mealIndex]; - final updatedPortions = meal.portions.map((portion) { - if (portion.productNutritionalValueId != null) { - try { - final glossaryItem = glossary.glossariesList.firstWhere((g) => g.id == portion.productNutritionalValueId); - return portion.copyWith( - productCaloriesInfo: ProductCaloriesInfoModel( - id: glossaryItem.id, - productName: glossaryItem.name, - calories: glossaryItem.energyOn100Gr, - proteins: glossaryItem.proteinsOn100Gr, - fats: glossaryItem.fatsOn100Gr, - carbohydrates: glossaryItem.carbohydratesOn100Gr, - energyOnPortion: glossaryItem.energyOnPortion, - proteinsOnPortion: glossaryItem.proteinsOnPortion, - fatsOnPortion: glossaryItem.fatsOnPortion, - carbohydratesOnPortion: glossaryItem.carbohydratesOnPortion, - ), - ); - } catch (e) { - return portion; + if (ids.isNotEmpty) { + final glossary = await getGlossariesIndex(ids: ids.toList()); + final List updatedMeals = List.from(diaryDay.meals); + for (var mealIndex = 0; mealIndex < updatedMeals.length; mealIndex++) { + final meal = updatedMeals[mealIndex]; + final updatedPortions = meal.portions.map((portion) { + if (portion.productNutritionalValueId != null) { + try { + final glossaryItem = glossary.glossariesList.firstWhere((g) => g.id == portion.productNutritionalValueId); + return portion.copyWith( + productCaloriesInfo: ProductCaloriesInfoModel( + id: glossaryItem.id, + productName: glossaryItem.name, + calories: glossaryItem.energyOn100Gr, + proteins: glossaryItem.proteinsOn100Gr, + fats: glossaryItem.fatsOn100Gr, + carbohydrates: glossaryItem.carbohydratesOn100Gr, + energyOnPortion: glossaryItem.energyOnPortion, + proteinsOnPortion: glossaryItem.proteinsOnPortion, + fatsOnPortion: glossaryItem.fatsOnPortion, + carbohydratesOnPortion: glossaryItem.carbohydratesOnPortion, + ), + ); + } catch (e) { + return portion; + } } - } - return portion; - }).toList(); + return portion; + }).toList(); - updatedMeals[mealIndex] = meal.copyWith(portions: updatedPortions); + updatedMeals[mealIndex] = meal.copyWith(portions: updatedPortions); + } + final updatedDiaryDay = diaryDay.copyWith(meals: updatedMeals); + return updatedDiaryDay; } - final updatedDiaryDay = diaryDay.copyWith(meals: updatedMeals); - return updatedDiaryDay; + return diaryDay; + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); + rethrow; } - return diaryDay; } Future getNutritionData(int index) async { - return await diaryService.getNutrition(diary: index); + try { + return await diaryService.getNutrition(diary: index); + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); + rethrow; + } } Future addPortionToMeal({ @@ -140,14 +157,19 @@ class DiaryRepository { required int quantity, int? productNutritionalValueId, }) async { - return await diaryService.addProductToMeal( - diary: diaryId, - typeNo: mealId, - productName: productName, - portion: portion, - quantity: quantity, - productNutritionalValueId: productNutritionalValueId, - ); + try { + return await diaryService.addProductToMeal( + diary: diaryId, + typeNo: mealId, + productName: productName, + portion: portion, + quantity: quantity, + productNutritionalValueId: productNutritionalValueId, + ); + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); + rethrow; + } } Future updateProductInMeal({ @@ -160,26 +182,51 @@ class DiaryRepository { int? productNutritionalValueId, required PortionState portionState, }) async { - Map data = ProductInMeal(productName: productName, portion: portion, quantity: quantity).toJson(); - if (productNutritionalValueId != null) data['product_nutritional_value_id'] = productNutritionalValueId; - data['state'] = portionState.name; - return await diaryService.updateProductInMeal(diary: diaryId, typeNo: mealId, portionModel: portionId, productInMeal: data); + try { + Map data = ProductInMeal(productName: productName, portion: portion, quantity: quantity).toJson(); + if (productNutritionalValueId != null) data['product_nutritional_value_id'] = productNutritionalValueId; + data['state'] = portionState.name; + return await diaryService.updateProductInMeal(diary: diaryId, typeNo: mealId, portionModel: portionId, productInMeal: data); + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); + rethrow; + } } Future markEaten({required int diaryId, required int mealId, required int portionId}) async { - return await diaryService.markEaten(diary: diaryId, typeNo: mealId, portionModel: portionId); + try { + return await diaryService.markEaten(diary: diaryId, typeNo: mealId, portionModel: portionId); + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); + rethrow; + } } Future unmarkEaten({required int diaryId, required int mealId, required int portionId}) async { - return await diaryService.unmarkEaten(diary: diaryId, typeNo: mealId, portionModel: portionId); + try { + return await diaryService.unmarkEaten(diary: diaryId, typeNo: mealId, portionModel: portionId); + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); + rethrow; + } } Future restore({required int diaryId, required int mealId, required int portionId}) async { - return await diaryService.restore(diary: diaryId, typeNo: mealId, portionModel: portionId); + try { + return await diaryService.restore(diary: diaryId, typeNo: mealId, portionModel: portionId); + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); + rethrow; + } } Future deleteProductFromMeal({required int diaryId, required int mealId, required int portionId}) async { - return await diaryService.deleteProductFromMeal(diary: diaryId, typeNo: mealId, portionModel: portionId); + try { + return await diaryService.deleteProductFromMeal(diary: diaryId, typeNo: mealId, portionModel: portionId); + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); + rethrow; + } } Future uploadPhotos({required int diaryId, required int mealId, required List files}) async { @@ -187,7 +234,8 @@ class DiaryRepository { for (int i = 0; i < files.length; i++) { await diaryService.uploadPhotoOfMeal(diary: diaryId, typeNo: mealId, photo: files[i]); } - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -195,16 +243,22 @@ class DiaryRepository { Future deletePhoto({required int diaryId, required int mealId, required DiaryPhotoModel photo}) async { try { await diaryService.deletePhotoOfMeal(diaryId: diaryId, typeNo: mealId, userAttachment: photo.id); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } Future finishDiaryDay({required int diaryId, required String wakeUp, required String retire, String? comment}) async { - return await diaryService.finishDiaryDay( - diary: diaryId, - finishDiaryDayModel: FinishDiaryDayModel(wakeUp: wakeUp, retire: retire, comment: comment), - ); + try { + return await diaryService.finishDiaryDay( + diary: diaryId, + finishDiaryDayModel: FinishDiaryDayModel(wakeUp: wakeUp, retire: retire, comment: comment), + ); + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); + rethrow; + } } Future sendProductToModeration({ @@ -214,15 +268,21 @@ class DiaryRepository { required double fats, required double carbohydrates, }) async { - return await diaryService.sendProductToModeration( - data: ProductModerationModel(name: name, energy: energy, proteins: proteins, fats: fats, carbohydrates: carbohydrates).toJson(), - ); + try { + return await diaryService.sendProductToModeration( + data: ProductModerationModel(name: name, energy: energy, proteins: proteins, fats: fats, carbohydrates: carbohydrates).toJson(), + ); + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); + rethrow; + } } Future fillSatiety({required int diaryId, required int mealId, required int satiety}) async { try { return await diaryService.fillSatiety(diary: diaryId, typeNo: mealId, satiety: satiety); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -230,7 +290,8 @@ class DiaryRepository { Future fillMood({required int diaryId, required int mealId, required int mood}) async { try { return await diaryService.fillMood(diary: diaryId, typeNo: mealId, mood: mood); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -238,7 +299,8 @@ class DiaryRepository { Future fillTime({required int diaryId, required int mealId, required String time}) async { try { return await diaryService.fillTime(diary: diaryId, typeNo: mealId, time: time); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -246,7 +308,8 @@ class DiaryRepository { Future fillWakeup({required int diaryId, required String time}) async { try { return await diaryService.fillWakeup(diary: diaryId, wakeUp: time); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -254,7 +317,8 @@ class DiaryRepository { Future fillRetire({required int diaryId, required String time}) async { try { return await diaryService.fillRetire(diary: diaryId, retire: time); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -263,7 +327,8 @@ class DiaryRepository { try { await diaryService.commentUserDay(id: diaryId, body: commentModel); _diaryForceFetchController.add('force'); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -271,7 +336,8 @@ class DiaryRepository { Future getDiaryComments({required int planId, required int diaryId}) async { try { return await diaryService.getDiaryComments(planId: planId, diaryId: diaryId); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -279,7 +345,8 @@ class DiaryRepository { Future viewCommentOfDiaryDayByConsultant({required int diaryId}) async { try { await diaryService.viewCommentOfDiaryDayByConsultant(diaryId: diaryId); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -293,7 +360,8 @@ class DiaryRepository { } try { return await diaryService.getGlossariesIndex(filters: filters); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -302,7 +370,8 @@ class DiaryRepository { try { final diaryDay = await diaryService.getDiaryDayV2(day: day); return setCPFCToDiary(diaryDay); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -311,7 +380,8 @@ class DiaryRepository { try { await diaryService.createDay(body: CreateDayModel(day: day)); await getDiaryByDate(day); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } diff --git a/lib/features/diets/repositories/diet_repository.dart b/lib/features/diets/repositories/diet_repository.dart index 91116b3a..7ae0b6bc 100644 --- a/lib/features/diets/repositories/diet_repository.dart +++ b/lib/features/diets/repositories/diet_repository.dart @@ -4,6 +4,7 @@ import 'package:eatvio_app/features/diets/models/diet_likes_model.dart'; import 'package:eatvio_app/features/diets/services/diet_service.dart'; import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart'; import 'package:equatable/equatable.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; class DietRepository { DietRepository({required this.dietsService}); @@ -19,7 +20,8 @@ class DietRepository { Future likeDiet(int id) async { try { return await dietsService.likeDiet(id: id); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -28,7 +30,8 @@ class DietRepository { try { final diet = await dietsService.getDiet(id: id); return diet; - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -37,7 +40,8 @@ class DietRepository { try { final diet = await dietsService.getDietBySlug(slug: slug); return diet; - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -45,7 +49,8 @@ class DietRepository { Future addToFavorite(int id) async { try { return await dietsService.addToFavorite(id: id); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -53,7 +58,8 @@ class DietRepository { Future removeFromFavorite(int id) async { try { return await dietsService.removeFromFavorite(id: id); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -61,7 +67,8 @@ class DietRepository { Future startDiet(int id, String date) async { try { return await dietsService.startDiet(id: id, body: {'date': date}); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -69,7 +76,8 @@ class DietRepository { Future cancelDiet(int id) async{ try { return await dietsService.cancelDiet(id: id); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } diff --git a/lib/features/feed/models/plan_pagination_model.dart b/lib/features/feed/models/plan_pagination_model.dart index 6c948685..073021a7 100644 --- a/lib/features/feed/models/plan_pagination_model.dart +++ b/lib/features/feed/models/plan_pagination_model.dart @@ -66,7 +66,7 @@ abstract class PacketOptions with _$PacketOptions { @JsonKey(name: "items", fromJson: _parseItems) required List items, @JsonKey(name: "price") required dynamic price, @JsonKey(name: "short") required String short, - @JsonKey(name: "description_md") required String description, + @JsonKey(name: "description_md") String? description, @JsonKey(name: "has_personal_diet", fromJson: _parseHasPersonalDietField) required bool hasPersonalDiet, }) = _PacketOptions; diff --git a/lib/features/feed/presentation/pages/feed_screen.dart b/lib/features/feed/presentation/pages/feed_screen.dart index 579c8d21..dfc3e9d4 100644 --- a/lib/features/feed/presentation/pages/feed_screen.dart +++ b/lib/features/feed/presentation/pages/feed_screen.dart @@ -72,7 +72,7 @@ class _FeedScreenContentState extends State { final consultantsScrollController = ScrollController(); @override void initState() { - chipsLabelList = hidePlans ? ['Все', 'Рецепты', 'Диеты', 'Консультанты'] : ['Все', 'Рецепты', 'Диеты', 'Планы питания', 'Консультанты']; + chipsLabelList = hidePlans ? ['Все', 'Консультанты', 'Рецепты', 'Диеты'] : ['Все', 'Планы питания', 'Консультанты', 'Рецепты', 'Диеты']; consultantsScrollController.addListener(_onScrollConsultants); _checkRegistrationDate(context); // Инициализация глобальных кубитов при первом открытии экрана @@ -115,7 +115,7 @@ class _FeedScreenContentState extends State { Widget build(BuildContext context) { FlutterNativeSplash.remove(); return Scaffold( - floatingActionButton: _selectedChipIndex == 1 + floatingActionButton: ((_selectedChipIndex == 2 && hidePlans) || (_selectedChipIndex == 3 && !hidePlans)) ? FloatingActionButton.small( heroTag: 'recipesTagsFilter', backgroundColor: appTheme.blueA400, @@ -137,11 +137,13 @@ class _FeedScreenContentState extends State { body: NestedScrollView( controller: switch (_selectedChipIndex) { 0 => blogPostsScrollController, - 1 => recipesScrollController, - 2 => dietsScrollController, - 3 when !hidePlans => plansScrollController, - 3 when hidePlans => consultantsScrollController, - 4 when !hidePlans => consultantsScrollController, + 1 when !hidePlans => plansScrollController, + 1 when hidePlans => consultantsScrollController, + 2 when !hidePlans => consultantsScrollController, + 2 when hidePlans => recipesScrollController, + 3 when !hidePlans => recipesScrollController, + 3 when hidePlans => dietsScrollController, + 4 when !hidePlans => dietsScrollController, _ => blogPostsScrollController, }, headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) { @@ -173,11 +175,13 @@ class _FeedScreenContentState extends State { children: [ CustomSearchBar( searchController: switch (_selectedChipIndex) { - 1 => recipesController, - 2 => dietsController, - 3 when !hidePlans => plansController, - 3 when hidePlans => consultantsController, - 4 when !hidePlans => consultantsController, + 1 when !hidePlans => plansController, + 1 when hidePlans => consultantsController, + 2 when !hidePlans => consultantsController, + 2 when hidePlans => recipesController, + 3 when !hidePlans => recipesController, + 3 when hidePlans => dietsController, + 4 when !hidePlans => dietsController, _ => postsController, }, onChanged: (query) { @@ -187,18 +191,20 @@ class _FeedScreenContentState extends State { case 0: context.read().loadFeedBlogPostList(query: query); break; - case 1: - context.read().loadRecipesList(query: query); - break; - case 2: - context.read().loadDiets(query: query); - break; - case 3 when !hidePlans: + case 1 when !hidePlans: context.read().loadPlans(query: query); break; + case 1 when hidePlans: + case 2 when !hidePlans: + context.read().getConsultants(); + break; + case 2 when hidePlans: + case 3 when !hidePlans: + context.read().loadRecipesList(query: query); + break; case 3 when hidePlans: case 4 when !hidePlans: - context.read().getConsultants(); + context.read().loadDiets(query: query); break; } }); @@ -208,18 +214,20 @@ class _FeedScreenContentState extends State { case 0: context.read().loadFeedBlogPostList(); break; - case 1: - context.read().loadRecipesList(); - break; - case 2: - context.read().loadDiets(); - break; - case 3 when !hidePlans: + case 1 when !hidePlans: context.read().loadPlans(); break; + case 1 when hidePlans: + case 2 when !hidePlans: + context.read().getConsultants(); + break; + case 2 when hidePlans: + case 3 when !hidePlans: + context.read().loadRecipesList(); + break; case 3 when hidePlans: case 4 when !hidePlans: - context.read().getConsultants(); + context.read().loadDiets(); break; } }); @@ -228,18 +236,28 @@ class _FeedScreenContentState extends State { ), const SizedBox(width: 12), switch (_selectedChipIndex) { - 1 => AddItemButton( + 1 when !hidePlans => AddItemButton( + onTap: state.profile?.roles.contains('consultant') ?? false ? () => GoRouter.of(context).pushNamed(CreatePlanScreen.name) : null, + ), + 2 when hidePlans => AddItemButton( onTap: state.profile?.roles.contains('consultant') ?? false ? () => GoRouter.of(context).pushNamed(RecipeCreateScreen.name) : null, ), - 2 => AddItemButton( + 3 when !hidePlans => AddItemButton( + onTap: state.profile?.roles.contains('consultant') ?? false ? () => GoRouter.of(context).pushNamed(RecipeCreateScreen.name) : null, + ), + 3 when hidePlans => AddItemButton( onTap: state.profile?.roles.contains('consultant') ?? false ? () async { await GoRouter.of(context).pushNamed(CreateDietScreen.name, extra: {'isEditing': false}); } : null, ), - 3 when !hidePlans => AddItemButton( - onTap: state.profile?.roles.contains('consultant') ?? false ? () => GoRouter.of(context).pushNamed(CreatePlanScreen.name) : null, + 4 when !hidePlans => AddItemButton( + onTap: state.profile?.roles.contains('consultant') ?? false + ? () async { + await GoRouter.of(context).pushNamed(CreateDietScreen.name); + } + : null, ), _ => const AddItemButton(key: AppKeys.feedScreenBlogPostCreationButton), }, @@ -252,11 +270,13 @@ class _FeedScreenContentState extends State { }, body: switch (_selectedChipIndex) { 0 => BlogPosts(handleVisibility: context.read().handleVisibility), - 1 => Recipes(handleVisibility: context.read().handleVisibility), - 2 => Diets(handleVisibility: context.read().handleVisibility), - 3 when !hidePlans => Plans(handleVisibility: context.read().handleVisibility), - 3 when hidePlans => const Consultants(), - 4 when !hidePlans => const Consultants(), + 1 when !hidePlans => Plans(handleVisibility: context.read().handleVisibility), + 1 when hidePlans => const Consultants(), + 2 when !hidePlans => const Consultants(), + 2 when hidePlans => Recipes(handleVisibility: context.read().handleVisibility), + 3 when !hidePlans => Recipes(handleVisibility: context.read().handleVisibility), + 3 when hidePlans => Diets(handleVisibility: context.read().handleVisibility), + 4 when !hidePlans => Diets(handleVisibility: context.read().handleVisibility), _ => Center(child: Text(chipsLabelList[_selectedChipIndex])), }, ), diff --git a/lib/features/feed/repositories/feed_repository.dart b/lib/features/feed/repositories/feed_repository.dart index e540486f..6263d766 100644 --- a/lib/features/feed/repositories/feed_repository.dart +++ b/lib/features/feed/repositories/feed_repository.dart @@ -7,6 +7,7 @@ import 'package:eatvio_app/features/feed/models/plan_pagination_model.dart'; import 'package:eatvio_app/features/feed/models/recipes_model.dart'; import 'package:eatvio_app/features/feed/models/view_model.dart'; import 'package:eatvio_app/features/feed/services/feed_service.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; class FeedRepository { final FeedService feedService; @@ -16,7 +17,8 @@ class FeedRepository { Future getFeedBlogPosts({String? query, int page = 1}) async { try { return (await feedService.getBlogPostList(page: page, query: query)); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -24,7 +26,8 @@ class FeedRepository { Future getBlogPost({required int id}) async { try { return await feedService.getBlogPost(postId: id); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -32,7 +35,8 @@ class FeedRepository { Future> getRecipes({String? query, List? tags, int page = 1}) async { try { return (await feedService.getRecipes(query: query, tags: tags, page: page)).recipesList; - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -40,7 +44,8 @@ class FeedRepository { Future getDiets({int page = 1, String? query, String? types, int? duration, String? sorting}) async { try { return (await feedService.getDiets(page: page, query: query, types: types, duration: duration, sorting: sorting)); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -48,7 +53,8 @@ class FeedRepository { Future getPlans({int page = 1, String? query, String? types, int? duration, int? minPrice, int? maxPrice}) async { try { return (await feedService.getPlans(page: page, query: query, types: types, duration: duration, minPrice: minPrice, maxPrice: maxPrice)); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -56,7 +62,8 @@ class FeedRepository { Future getBlogPostComments({required String id}) async { try { return (await feedService.getBlogPostComments(id: id)); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -64,7 +71,8 @@ class FeedRepository { Future sendBlogPostComment({required String id, required String commentText, int? parentCommentId}) async { try { await feedService.sendBlogPostComment(id: id, body: commentText, parentCommentId: parentCommentId); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -72,7 +80,8 @@ class FeedRepository { Future getDietsComments({required String id}) async { try { return (await feedService.getDietsComments(id: id)); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -80,7 +89,8 @@ class FeedRepository { Future sendDietsComment({required String id, required String commentText, int? parentCommentId}) async { try { await feedService.sendDietsComment(id: id, body: commentText, parentCommentId: parentCommentId); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -88,7 +98,8 @@ class FeedRepository { Future getRecipesComments({required String id}) async { try { return (await feedService.getRecipesComments(id: id)); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -96,7 +107,8 @@ class FeedRepository { Future sendRecipesComment({required String id, required String commentText, int? parentCommentId}) async { try { await feedService.sendRecipesComment(id: id, body: commentText, parentCommentId: parentCommentId); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -104,7 +116,8 @@ class FeedRepository { Future getPlansComments({required String slug}) async { try { return (await feedService.getPlansComments(slug: slug)); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -112,7 +125,8 @@ class FeedRepository { Future sendPlansComment({required String slug, required String commentText, int? parentCommentId}) async { try { await feedService.sendPlansComment(slug: slug, body: commentText, parentCommentId: parentCommentId); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -120,7 +134,8 @@ class FeedRepository { Future likeBlogPost({required int id}) async { try { await feedService.likeBlogPost(id: id); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -128,7 +143,8 @@ class FeedRepository { Future unlikedBlogPost({required int id}) async { try { await feedService.unlikedBlogPost(id: id); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -136,7 +152,8 @@ class FeedRepository { Future> getConsultants([String? name]) async { try { return (await feedService.getConsultants(name: name)).data; - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -144,7 +161,8 @@ class FeedRepository { Future viewPost(int id) async { try { return (await feedService.viewPost(id: id)); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -152,7 +170,8 @@ class FeedRepository { Future viewRecipe(String slug) async { try { return (await feedService.viewRecipe(slug: slug)); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -160,7 +179,8 @@ class FeedRepository { Future viewDiet(int id) async { try { return (await feedService.viewDiet(id: id)); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -168,7 +188,8 @@ class FeedRepository { Future viewPlan(String slug) async { try { return (await feedService.viewPlan(slug: slug)); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -176,7 +197,8 @@ class FeedRepository { Future viewContent({required ViewModel viewModel}) async { try { await feedService.viewContent(viewModel: viewModel); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } diff --git a/lib/features/glossary/repositories/glossary_repository.dart b/lib/features/glossary/repositories/glossary_repository.dart index c0c3233e..9702db8e 100644 --- a/lib/features/glossary/repositories/glossary_repository.dart +++ b/lib/features/glossary/repositories/glossary_repository.dart @@ -1,5 +1,6 @@ import 'package:eatvio_app/features/glossary/models/glossary_model.dart'; import 'package:eatvio_app/features/glossary/services/glossary_service.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; class GlossaryRepository { final GlossaryService glossaryService; @@ -11,7 +12,8 @@ class GlossaryRepository { Future> getGlossaryListByQuery(String query) async { try { return (await glossaryService.getGlossaryByQuery(query: query)).glossariesList; - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } diff --git a/lib/features/menu/presentation/pages/menu_screen.dart b/lib/features/menu/presentation/pages/menu_screen.dart index 293f5485..dc4e3d82 100644 --- a/lib/features/menu/presentation/pages/menu_screen.dart +++ b/lib/features/menu/presentation/pages/menu_screen.dart @@ -1,5 +1,6 @@ import 'package:dio/dio.dart'; import 'package:eatvio_app/core/app_config.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:eatvio_app/core/const.dart'; import 'package:eatvio_app/core/theme/custom_text_style.dart'; import 'package:eatvio_app/core/token_storage.dart'; @@ -7,6 +8,7 @@ import 'package:eatvio_app/core/utils/image_constant.dart'; import 'package:eatvio_app/core/utils/date_time_utils.dart'; import 'package:eatvio_app/core/locator_service.dart'; import 'package:eatvio_app/core/theme/theme_helper.dart'; +import 'package:eatvio_app/core/websockets/ws_manager.dart'; import 'package:eatvio_app/features/consultant_rating/presentation/consultant_rating_screen.dart'; import 'package:eatvio_app/features/finances_and_consultations_archive/presentation/consultant_finances_screen.dart'; import 'package:eatvio_app/features/finances_and_consultations_archive/presentation/user_purchases_screen.dart'; @@ -141,7 +143,10 @@ class MenuScreen extends StatelessWidget { onPressed: () async { try { await sl().post('${sl().baseUrl}/api/profile/logout'); + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); } finally { + await sl().clear(); await TokenStorage.removeTokenInStorage(); if (context.mounted) { GoRouter.of(context).go('/auth-screen', extra: true); diff --git a/lib/features/menu/presentation/repositories/blocked_users_repository.dart b/lib/features/menu/presentation/repositories/blocked_users_repository.dart index 27dfafad..2620252f 100644 --- a/lib/features/menu/presentation/repositories/blocked_users_repository.dart +++ b/lib/features/menu/presentation/repositories/blocked_users_repository.dart @@ -1,7 +1,7 @@ import 'dart:developer'; - import 'package:eatvio_app/features/menu/services/blocked_users_service.dart'; import 'package:eatvio_app/features/profile/models/profile_model.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; class BlockedUsersRepository { final BlockedUserService blockedUserService; @@ -15,6 +15,7 @@ class BlockedUsersRepository { return (await blockedUserService.getBlockedUsers()).profilesList; } catch (e, st) { log(e.toString(), stackTrace: st); + await Sentry.captureException(e, stackTrace: st); rethrow; } } @@ -28,6 +29,7 @@ class BlockedUsersRepository { ); } catch (e, st) { log(e.toString(), stackTrace: st); + await Sentry.captureException(e, stackTrace: st); rethrow; } } @@ -41,6 +43,7 @@ class BlockedUsersRepository { ); } catch (e, st) { log(e.toString(), stackTrace: st); + await Sentry.captureException(e, stackTrace: st); rethrow; } } diff --git a/lib/features/menu/repositories/notification_settings_repository.dart b/lib/features/menu/repositories/notification_settings_repository.dart index e0aeaa31..81d782ef 100644 --- a/lib/features/menu/repositories/notification_settings_repository.dart +++ b/lib/features/menu/repositories/notification_settings_repository.dart @@ -1,5 +1,6 @@ import 'package:eatvio_app/features/menu/models/notification_settings_model.dart'; import 'package:eatvio_app/features/menu/services/notification_settings_service.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; class NotificationSettingsRepository { final NotificationSettingsService _notificationSettingsService; @@ -7,10 +8,20 @@ class NotificationSettingsRepository { NotificationSettingsRepository(this._notificationSettingsService); Future getNotificationSettings() async { - return await _notificationSettingsService.getNotificationSettings(); + try { + return await _notificationSettingsService.getNotificationSettings(); + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); + rethrow; + } } Future updateNotificationSettings(Map data) async { - return await _notificationSettingsService.updateNotificationSettings(data: data); + try { + return await _notificationSettingsService.updateNotificationSettings(data: data); + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); + rethrow; + } } } diff --git a/lib/features/my_diets/presentation/my_diets_screen.dart b/lib/features/my_diets/presentation/my_diets_screen.dart index 4a409f5d..51f7ee39 100644 --- a/lib/features/my_diets/presentation/my_diets_screen.dart +++ b/lib/features/my_diets/presentation/my_diets_screen.dart @@ -142,8 +142,7 @@ class MyDietsScreenContent extends StatelessWidget { } final diets = switch (state.selectedStateIndex) { - 1 => state.draftDiets, - 2 => state.personalDiets, + 1 => state.personalDiets, _ => state.activeDiets, }; 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 c4f7aeaf..e2dec663 100644 --- a/lib/features/my_diets/presentation/widgets/my_diet_widget.dart +++ b/lib/features/my_diets/presentation/widgets/my_diet_widget.dart @@ -169,8 +169,35 @@ class _MyDietWidgetState extends State { padding: const EdgeInsets.symmetric(horizontal: 16.0), child: Column( children: [ + // Верхний ряд: Удалить + Редактировать (или + Поделиться для опубликованных) Row( children: [ + // Кнопка удаления (слева) + CustomOutlinedButton( + onPressed: () async { + final result = await context.read().deleteDiet(widget.diet.id); + if (result) { + if (!context.mounted) return; + showBottomSheetWithBlur( + context: context, + child: DietDeletedBottomSheet(title: widget.diet.name), + ); + await context.read().loadAllDiets(); + } + }, + height: 52, + width: 52, + leftIcon: SvgPicture.asset('assets/icons/trash_can.svg', width: 20, height: 20), + buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.red), + buttonStyle: ButtonStyle( + shape: WidgetStatePropertyAll(RoundedRectangleBorder(borderRadius: BorderRadius.circular(20))), + backgroundColor: WidgetStatePropertyAll(appTheme.lightGray0), + side: const WidgetStatePropertyAll(BorderSide.none), + ), + text: '', + ), + const SizedBox(width: 6), + // Кнопка редактирования (центр) Expanded( child: CustomOutlinedButton( onPressed: () async { @@ -182,20 +209,17 @@ class _MyDietWidgetState extends State { _editDiet(widget.diet); } }, - text: switch (state.selectedStateIndex) { - 1 => s.publish, - 2 => s.toOffer, - _ => s.edit, - }, + text: s.edit, height: 52, - buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700), + buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.black), buttonStyle: ButtonStyle( shape: WidgetStatePropertyAll(RoundedRectangleBorder(borderRadius: BorderRadius.circular(20))), - backgroundColor: WidgetStatePropertyAll(appTheme.blueA400), + backgroundColor: WidgetStatePropertyAll(appTheme.lightGray0), side: const WidgetStatePropertyAll(BorderSide.none), ), ), ), + // Кнопка поделиться (справа) для опубликованных if (state.selectedStateIndex == 0) ...[ const SizedBox(width: 6), CustomOutlinedButton( diff --git a/lib/features/my_diets/presentation/widgets/my_diets_tab.dart b/lib/features/my_diets/presentation/widgets/my_diets_tab.dart index a653441c..be6a0d95 100644 --- a/lib/features/my_diets/presentation/widgets/my_diets_tab.dart +++ b/lib/features/my_diets/presentation/widgets/my_diets_tab.dart @@ -21,7 +21,6 @@ class _MyDietsTabState extends State { final s = S.of(context); if (tabTitles.isEmpty) { tabTitles.add(s.published); - tabTitles.add(s.drafted); tabTitles.add(s.personal); } super.didChangeDependencies(); @@ -31,37 +30,29 @@ class _MyDietsTabState extends State { Widget build(BuildContext context) { return BlocBuilder( builder: (context, state) { - return Align( - alignment: Alignment.centerLeft, - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: Container( + height: 48, + decoration: BoxDecoration( + color: appTheme.whiteA700, + borderRadius: BorderRadius.circular(100), + ), + padding: const EdgeInsets.all(4), child: Row( children: [ - Container( - width: 484, - height: 48, - decoration: BoxDecoration( - color: appTheme.whiteA700, - borderRadius: BorderRadius.circular(100), - ), - padding: const EdgeInsets.all(4), - child: Row( - children: [ - ...List.generate( - 3, - (index) => Expanded( - child: AnimatedOptionButton( - textStyle: TextStyles.textSMedium, - isSelected: state.selectedStateIndex == index, - text: tabTitles[index], - onTap: () { - widget.additionalOnTap?.call(); - context.read().changeSelectedStateIndex(index); - }, - ), - ), - ), - ], + ...List.generate( + 2, + (index) => Expanded( + child: AnimatedOptionButton( + textStyle: TextStyles.textSMedium, + isSelected: state.selectedStateIndex == index, + text: tabTitles[index], + onTap: () { + widget.additionalOnTap?.call(); + context.read().changeSelectedStateIndex(index); + }, + ), ), ), ], diff --git a/lib/features/my_diets/repositories/my_diets_repository.dart b/lib/features/my_diets/repositories/my_diets_repository.dart index e3ec0dd1..df90c157 100644 --- a/lib/features/my_diets/repositories/my_diets_repository.dart +++ b/lib/features/my_diets/repositories/my_diets_repository.dart @@ -1,5 +1,6 @@ import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart'; import 'package:eatvio_app/features/my_diets/services/my_diets_service.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; class MyDietsRepository { final MyDietsService myDietsService; @@ -9,7 +10,8 @@ class MyDietsRepository { Future deleteDiet(int dietId) async { try { await myDietsService.deleteDiet(dietId: dietId); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -31,7 +33,8 @@ class MyDietsRepository { sorting: sorting, state: state, )); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } diff --git a/lib/features/my_finance/repositories/cards_repository.dart b/lib/features/my_finance/repositories/cards_repository.dart index 42534348..9c77a917 100644 --- a/lib/features/my_finance/repositories/cards_repository.dart +++ b/lib/features/my_finance/repositories/cards_repository.dart @@ -1,6 +1,7 @@ import 'package:eatvio_app/features/my_finance/models/card_model.dart'; import 'package:eatvio_app/features/my_finance/models/payment_url_model.dart'; import 'package:eatvio_app/features/my_finance/services/cards_service.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; class CardsRepository { final CardsService cardsService; @@ -8,7 +9,8 @@ class CardsRepository { Future addCard() async { try { return await cardsService.addCard(); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -16,7 +18,8 @@ class CardsRepository { Future> getCards() async { try { return await cardsService.getCards(); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -24,7 +27,8 @@ class CardsRepository { Future markAsDefault({required int id}) async { try { await cardsService.markAsDefault(id: id); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -32,7 +36,8 @@ class CardsRepository { Future deleteCard({required int id}) async { try { await cardsService.deleteCard(id: id); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -40,7 +45,8 @@ class CardsRepository { Future collectCharge({required int chosenPlaId}) async { try { await cardsService.collectCharge(chosenPlanId: chosenPlaId); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } diff --git a/lib/features/my_plans/repositories/my_diets_repository.dart b/lib/features/my_plans/repositories/my_diets_repository.dart index 1ee9afb1..a378bc4f 100644 --- a/lib/features/my_plans/repositories/my_diets_repository.dart +++ b/lib/features/my_plans/repositories/my_diets_repository.dart @@ -1,5 +1,8 @@ +import 'dart:developer'; + import 'package:eatvio_app/features/feed/models/plan_pagination_model.dart'; import 'package:eatvio_app/features/my_plans/services/my_plans_service.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; class MyPlansRepository { final MyPlansService myPlansService; @@ -9,34 +12,28 @@ class MyPlansRepository { Future deletePlan(String slug) async { try { await myPlansService.deletePlan(slug: slug); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } - Future getPlans({ - int page = 1, - String? query, - String? types, - int? duration, - int? minPrice, - int? maxPrice, - }) async { + Future getPlans({int page = 1, String? query, String? types, int? duration, int? minPrice, int? maxPrice}) async { try { - return (await myPlansService.getPlans( - page: page, - query: query, - types: types, - duration: duration, - minPrice: minPrice, - maxPrice: maxPrice, - )); - } catch (e) { + return (await myPlansService.getPlans(page: page, query: query, types: types, duration: duration, minPrice: minPrice, maxPrice: maxPrice)); + } catch (e, st) { + log(e.toString(), error: e, stackTrace: st); + await Sentry.captureException(e, stackTrace: st); rethrow; } } Future unpublish(String slug) async { - return await myPlansService.unpublish(slug: slug); + try { + return await myPlansService.unpublish(slug: slug); + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); + rethrow; + } } } diff --git a/lib/features/notifications/repository/notifications_repository.dart b/lib/features/notifications/repository/notifications_repository.dart index 267faa89..6508ee2e 100644 --- a/lib/features/notifications/repository/notifications_repository.dart +++ b/lib/features/notifications/repository/notifications_repository.dart @@ -1,15 +1,17 @@ import 'dart:developer'; import 'package:eatvio_app/features/notifications/service/notifications_service.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; class NotificationsRepository { final NotificationsService notificationsService; NotificationsRepository({required this.notificationsService}); - Future?> getNotifications({required int page}) { + Future?> getNotifications({required int page}) async { try { - return notificationsService.getNotifications(page: page); - } catch (e) { + return await notificationsService.getNotifications(page: page); + } catch (e, s) { log(e.toString()); + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -17,7 +19,8 @@ class NotificationsRepository { Future markNotificationsAsRead() async { try { await notificationsService.markNotificationsAsRead(); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } diff --git a/lib/features/personal_diet_suggestion/presentation/personal_diet_suggestion_screen.dart b/lib/features/personal_diet_suggestion/presentation/personal_diet_suggestion_screen.dart index dad76406..32398019 100644 --- a/lib/features/personal_diet_suggestion/presentation/personal_diet_suggestion_screen.dart +++ b/lib/features/personal_diet_suggestion/presentation/personal_diet_suggestion_screen.dart @@ -41,27 +41,38 @@ class PersonalDietSuggestionScreenContent extends StatelessWidget { ), body: state.isLoading ? const Center(child: Loader()) - : SingleChildScrollView( - child: Column( - children: [ - ...List.generate(state.consultationsWithDiet.length, (index) { - return ConsultationWidget( - consultationModel: state.consultationsWithDiet[index], - dietName: state.dietName, - ); - }), - const SizedBox( - height: 12, + : (state.consultationsWithDiet.isEmpty && state.consultationsWithoutDiet.isEmpty) + ? Center( + child: Padding( + padding: const EdgeInsets.all(32.0), + child: Text( + 'Нет активных консультаций с персональной диетой', + style: TextStyles.textMSemibold.copyWith(color: appTheme.gray), + textAlign: TextAlign.center, + ), ), - ...List.generate(state.consultationsWithoutDiet.length, (index) { - return ConsultationWidget( - consultationModel: state.consultationsWithoutDiet[index], - dietName: state.dietName, - ); - }) - ], - ), - ), + ) + : SingleChildScrollView( + child: Column( + children: [ + ...List.generate(state.consultationsWithDiet.length, (index) { + return ConsultationWidget( + consultationModel: state.consultationsWithDiet[index], + dietName: state.dietName, + ); + }), + const SizedBox( + height: 12, + ), + ...List.generate(state.consultationsWithoutDiet.length, (index) { + return ConsultationWidget( + consultationModel: state.consultationsWithoutDiet[index], + dietName: state.dietName, + ); + }) + ], + ), + ), ); }, ); diff --git a/lib/features/personal_diet_suggestion/repository/personal_diet_suggestion_repository.dart b/lib/features/personal_diet_suggestion/repository/personal_diet_suggestion_repository.dart index 23c32436..784d9857 100644 --- a/lib/features/personal_diet_suggestion/repository/personal_diet_suggestion_repository.dart +++ b/lib/features/personal_diet_suggestion/repository/personal_diet_suggestion_repository.dart @@ -1,5 +1,6 @@ import 'package:eatvio_app/features/consultations/models/consultation_model.dart'; import 'package:eatvio_app/features/personal_diet_suggestion/services/personal_diet_suggestion_service.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; class PersonalDietSuggestionRepository { final PersonalDietSuggestionService personalDietSuggestionService; @@ -9,23 +10,26 @@ class PersonalDietSuggestionRepository { Future> getConsultationWithPersonalDiets() async{ try { return (await personalDietSuggestionService.getConsultationWithPersonalDiets()).data; - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } - Future attachPersonalDiet({required int consultationId, required int dietId}) { + Future attachPersonalDiet({required int consultationId, required int dietId}) async { try { return personalDietSuggestionService.attachPersonalDiet(consultationId: consultationId, dietId: dietId); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } - Future reattachPersonalDiet({required int consultationId, required int dietId}) { + Future reattachPersonalDiet({required int consultationId, required int dietId}) async { try { - return personalDietSuggestionService.reattachPersonalDiet(consultationId: consultationId, dietId: dietId); - } catch (e) { + return await personalDietSuggestionService.reattachPersonalDiet(consultationId: consultationId, dietId: dietId); + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } diff --git a/lib/features/plans/repositories/plan_repository.dart b/lib/features/plans/repositories/plan_repository.dart index 75ca7a08..fcc942f9 100644 --- a/lib/features/plans/repositories/plan_repository.dart +++ b/lib/features/plans/repositories/plan_repository.dart @@ -3,6 +3,7 @@ import 'package:eatvio_app/features/diets/repositories/diet_repository.dart'; import 'package:eatvio_app/features/feed/models/plan_pagination_model.dart'; import 'package:eatvio_app/features/plans/models/chosen_plan_model.dart'; import 'package:eatvio_app/features/plans/services/plan_service.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; class PlanRepository { final PlanService planService; @@ -19,7 +20,8 @@ class PlanRepository { Future getPlan(String slug) async { try { return planService.getPlan(slug); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -27,7 +29,8 @@ class PlanRepository { Future likePlan(String slug) async { try { await planService.likePlan(slug: slug); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -35,7 +38,8 @@ class PlanRepository { Future addToFavorite(String slug) async { try { await planService.addToFavorite(slug: slug); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -43,7 +47,8 @@ class PlanRepository { Future removeFromFavorite(String slug) async { try { await planService.removeFromFavorite(slug: slug); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -51,7 +56,8 @@ class PlanRepository { Future chosePlan(String slug, Map option ) async { try { return await planService.chosePlan(slug: slug, option: option); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } diff --git a/lib/features/plans/repositories/plans_repository.dart b/lib/features/plans/repositories/plans_repository.dart index 971c6059..74aec6b1 100644 --- a/lib/features/plans/repositories/plans_repository.dart +++ b/lib/features/plans/repositories/plans_repository.dart @@ -1,5 +1,6 @@ import 'package:eatvio_app/features/feed/models/plan_pagination_model.dart'; import 'package:eatvio_app/features/plans/services/plans_service.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; class PlansRepository { final PlansService plansService; @@ -10,7 +11,8 @@ class PlansRepository { try { final plans = (await plansService.getPlansBySlug(author: slug)).data; return plans.where((plan) => plan.state == 'active').toList(); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } diff --git a/lib/features/profile/repositories/profile_repository.dart b/lib/features/profile/repositories/profile_repository.dart index 0f2e903c..766e03c1 100644 --- a/lib/features/profile/repositories/profile_repository.dart +++ b/lib/features/profile/repositories/profile_repository.dart @@ -9,6 +9,7 @@ import 'package:eatvio_app/features/profile/models/profile_model.dart'; import 'package:eatvio_app/features/profile/models/sub_model.dart'; import 'package:eatvio_app/features/profile/models/weight_model.dart'; import 'package:eatvio_app/features/profile/services/profile_service.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; class ProfileRepository { final ProfileService profileService; @@ -18,7 +19,8 @@ class ProfileRepository { Future getProfileBySlug(String slug) async { try { return await profileService.getProfileBySlug(slug: slug); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -26,7 +28,8 @@ class ProfileRepository { Future getProfileDetailsBySlug(String slug) async { try { return await profileService.getProfileDetailsBySlug(slug: slug); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -34,7 +37,8 @@ class ProfileRepository { Future getMyProfile() async { try { return await profileService.getMyProfile(); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -42,7 +46,8 @@ class ProfileRepository { Future getMyProfileDetails() async { try { return await profileService.getMyProfileDetails(); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -54,7 +59,8 @@ class ProfileRepository { waistCircumference: waistCircumference, hipCircumference: hipCircumference, )); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -62,7 +68,8 @@ class ProfileRepository { Future addWeight({required double weight}) async { try { return (await profileService.addWeight(weight: weight)); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -70,7 +77,8 @@ class ProfileRepository { Future> getBlogPostsByAuthorSlug({required String slug}) async { try { return (await profileService.getBlogPostByAuthorSlug(slug: slug)); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -82,7 +90,8 @@ class ProfileRepository { } else { return (await profileService.getWeight(period: period)); } - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -94,7 +103,8 @@ class ProfileRepository { } else { return (await profileService.getCircumferences(period: period)); } - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -102,7 +112,8 @@ class ProfileRepository { Future> getSubscribersBySlug(String slug, [String? query]) async { try { return (await profileService.getSubscribers(slug: slug, query: query)).data; - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -110,7 +121,8 @@ class ProfileRepository { Future> getSubscriptionsBySlug(String slug, [String? query]) async { try { return (await profileService.getSubscriptions(slug: slug, query: query)).data; - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -118,7 +130,8 @@ class ProfileRepository { Future subscribe(String slug) async { try { return await profileService.subscribe(slug: slug); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -126,7 +139,8 @@ class ProfileRepository { Future unsubscribe(String slug) async { try { return await profileService.unsubscribe(slug: slug); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -134,7 +148,8 @@ class ProfileRepository { Future uploadPhoto(File file) async { try { return await profileService.uploadPhoto(photo: file); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -142,7 +157,8 @@ class ProfileRepository { Future deletePhoto() async { try { return await profileService.deletePhoto(); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -150,26 +166,42 @@ class ProfileRepository { Future> getChosenDiets() async { try { return (await profileService.getChosenDiets()).diets; - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } Future getPassKey({required String routeName, Map? routeParams}) async { - return (await profileService.getPassKey( - json: PassKeyParams(routeName: routeName, routeParams: routeParams), - )).data['key_is']!; + try { + return (await profileService.getPassKey( + json: PassKeyParams(routeName: routeName, routeParams: routeParams), + )).data['key_is']!; + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); + rethrow; + } } Future getMyPhone() async { - return (jsonDecode(await profileService.getMyPhone())['phone']); + try { + return (jsonDecode(await profileService.getMyPhone())['phone']); + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); + rethrow; + } } Future sendEmailCode({String? newEmail}) async { - if (newEmail != null) { - return await profileService.sendEmailCode(data: {'new_email': newEmail}); - } else { - return await profileService.sendEmailCode(); + try { + if (newEmail != null) { + return await profileService.sendEmailCode(data: {'new_email': newEmail}); + } else { + return await profileService.sendEmailCode(); + } + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); + rethrow; } } @@ -177,7 +209,8 @@ class ProfileRepository { try { await profileService.tryVerifyEmailByCode(code: "{\"code\": \"$code\"}"); return true; - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); return false; } } @@ -185,7 +218,8 @@ class ProfileRepository { Future deleteAccount() async { try { await profileService.deleteAccount(); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } diff --git a/lib/features/recipe/repositories/about_recipe_repository.dart b/lib/features/recipe/repositories/about_recipe_repository.dart index df433f5e..4b6de461 100644 --- a/lib/features/recipe/repositories/about_recipe_repository.dart +++ b/lib/features/recipe/repositories/about_recipe_repository.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:eatvio_app/features/diets/repositories/diet_repository.dart'; import 'package:eatvio_app/features/feed/models/recipes_model.dart'; import 'package:eatvio_app/features/recipe/services/about_recipe_service.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; class AboutRecipeRepository { final AboutRecipeService recipeService; @@ -18,7 +19,8 @@ class AboutRecipeRepository { Future getRecipe(String recipeID) async { try { return await recipeService.getRecipe(recipeID); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -26,7 +28,8 @@ class AboutRecipeRepository { Future likeRecipe(String slug) async { try { await recipeService.likeRecipe(slug: slug); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -34,7 +37,8 @@ class AboutRecipeRepository { Future addToFavorite(String slug) async { try { await recipeService.addToFavorite(slug: slug); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -42,7 +46,8 @@ class AboutRecipeRepository { Future removeFromFavorite(String slug) async { try { await recipeService.removeFromFavorite(slug: slug); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -50,7 +55,8 @@ class AboutRecipeRepository { Future deleteRecipe(String slug) async { try { await recipeService.deleteRecipe(slug: slug); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } diff --git a/lib/features/recipe/repositories/my_recipes_repository.dart b/lib/features/recipe/repositories/my_recipes_repository.dart index 73dc105c..fb67f395 100644 --- a/lib/features/recipe/repositories/my_recipes_repository.dart +++ b/lib/features/recipe/repositories/my_recipes_repository.dart @@ -1,5 +1,6 @@ import 'package:eatvio_app/features/recipe/models/my_recipe_model.dart'; import 'package:eatvio_app/features/recipe/services/my_recipes_service.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; class MyRecipesRepository { final MyRecipesService myRecipesService; @@ -9,7 +10,8 @@ class MyRecipesRepository { Future> getMyRecipes() async { try { return (await myRecipesService.getMyRecipes()).data; - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } diff --git a/lib/features/registration/repositories/registration_repository.dart b/lib/features/registration/repositories/registration_repository.dart index 548684b9..7ab8c43a 100644 --- a/lib/features/registration/repositories/registration_repository.dart +++ b/lib/features/registration/repositories/registration_repository.dart @@ -4,6 +4,7 @@ import 'package:eatvio_app/features/registration/models/user_registration_model. import 'package:eatvio_app/features/registration/models/location_model.dart'; import 'package:eatvio_app/features/registration/services/registration_service.dart'; import 'package:eatvio_app/core/env_config.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; class RegistrationRepository { final RegistrationService registrationService; @@ -37,7 +38,7 @@ class RegistrationRepository { throw 'Ошибка регистрации: $response'; } } - } on DioException catch (e) { + } on DioException catch (e, s) { if (e.response != null && e.response?.statusCode == 422) { final data = e.response?.data; String errorMsg = ''; @@ -62,6 +63,10 @@ class RegistrationRepository { throw 'Ошибка регистрации: $data'; } } + await Sentry.captureException(e, stackTrace: s); + rethrow; + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -69,7 +74,8 @@ class RegistrationRepository { Future getLocationsListByQuery(String query) async { try { return (await registrationService.getLocationsByAddress(query: query)); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -77,7 +83,8 @@ class RegistrationRepository { Future getLocationsListByCoordinates(String latitude, String longitude) async { try { return (await registrationService.getLocationsByCoordinate(latitude: latitude, longitude: longitude)); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -90,7 +97,8 @@ class RegistrationRepository { } else { return false; } - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); return false; } } diff --git a/lib/features/registration_v2/cubit/registration_cubit.dart b/lib/features/registration_v2/cubit/registration_cubit.dart index 49d4eabc..cfc72b4b 100644 --- a/lib/features/registration_v2/cubit/registration_cubit.dart +++ b/lib/features/registration_v2/cubit/registration_cubit.dart @@ -3,6 +3,7 @@ import 'dart:developer'; import 'package:dio/dio.dart'; import 'package:eatvio_app/core/token_storage.dart'; import 'package:eatvio_app/core/utils/date_time_utils.dart'; +import 'package:eatvio_app/core/utils/validation_utils.dart'; import 'package:eatvio_app/features/registration_v2/cubit/registration_state.dart'; import 'package:eatvio_app/features/registration_v2/models/registration_model.dart'; import 'package:eatvio_app/features/registration_v2/repositories/registration_repository_v2.dart'; @@ -180,7 +181,7 @@ class RegistrationCubitV2 extends Cubit { firstName: state.firstName, lastName: state.lastName, email: state.email, - phone: state.phone, + phone: ValidationUtils.normalizePhone(state.phone), gender: state.gender, password: password, confirmPassword: password, diff --git a/lib/features/registration_v2/presentation/widgets/first_step_content.dart b/lib/features/registration_v2/presentation/widgets/first_step_content.dart index 78244da8..dbb22951 100644 --- a/lib/features/registration_v2/presentation/widgets/first_step_content.dart +++ b/lib/features/registration_v2/presentation/widgets/first_step_content.dart @@ -21,6 +21,7 @@ class FirstStepContent extends StatefulWidget { class _FirstStepContentState extends State { final maskFormatterForBirthday = MaskTextInputFormatter(mask: '##.##.####', filter: {"#": RegExp(r'[0-9]')}, type: MaskAutoCompletionType.lazy); + final maskFormatter = MaskTextInputFormatter(mask: '+7 (###) ### ## ##', filter: {"#": RegExp(r'[0-9]')}, type: MaskAutoCompletionType.lazy); final firstNameController = TextEditingController(); @@ -37,7 +38,10 @@ class _FirstStepContentState extends State { firstNameController.text = context.read().state.firstName; lastNameController.text = context.read().state.lastName; emailController.text = context.read().state.email; - phoneController.text = context.read().state.phone; + final phone = context.read().state.phone; + if (phone.isNotEmpty) { + phoneController.text = maskFormatter.maskText(phone); + } dateController.text = context.read().state.birthday?.format('dd.MM.yyyy') ?? ''; } @@ -113,6 +117,7 @@ class _FirstStepContentState extends State { hintText: '+7 (999) 999 99 99', controller: phoneController, hasError: !state.isPhoneValid, + inputFormatters: [maskFormatter], keyboardType: TextInputType.phone, onChanged: (val) { context.read().setPhone(val); diff --git a/lib/features/registration_v2/repositories/registration_repository_v2.dart b/lib/features/registration_v2/repositories/registration_repository_v2.dart index b30e61d8..7640ec30 100644 --- a/lib/features/registration_v2/repositories/registration_repository_v2.dart +++ b/lib/features/registration_v2/repositories/registration_repository_v2.dart @@ -1,5 +1,6 @@ import 'package:eatvio_app/features/registration_v2/models/registration_model.dart'; import 'package:eatvio_app/features/registration_v2/services/registration_service_v2.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; class RegistrationRepositoryV2 { final RegistrationServiceV2 registrationService; @@ -9,7 +10,8 @@ class RegistrationRepositoryV2 { try { final response = await registrationService.register(model: registrationModel.toJson()); return response['token']; - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } diff --git a/lib/features/settings/repositories/service_repository.dart b/lib/features/settings/repositories/service_repository.dart index 139ee31e..aa86ec57 100644 --- a/lib/features/settings/repositories/service_repository.dart +++ b/lib/features/settings/repositories/service_repository.dart @@ -3,6 +3,7 @@ import 'package:eatvio_app/features/settings/models/oauth_account_model.dart'; import 'package:eatvio_app/features/settings/models/oauth_attach_request.dart'; import 'package:eatvio_app/features/settings/models/profile_settings_model.dart'; import 'package:eatvio_app/features/settings/services/settings_service.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; class SettingsRepository { final SettingsService settingsService; @@ -12,7 +13,8 @@ class SettingsRepository { Future getProfileSettings() async { try { return await settingsService.getProfileSettings(); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -20,7 +22,8 @@ class SettingsRepository { Future updateProfileSettings({required ProfileSettingsModel profileSettingsModel}) async { try { return await settingsService.updateProfileSettings(data: profileSettingsModel.toJson()); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -28,7 +31,8 @@ class SettingsRepository { Future updateProfileDetails({required ProfileDetailsModel profileDetailsModel}) async { try { return await settingsService.updateProfileDetails(data: profileDetailsModel.toJson()); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -36,7 +40,8 @@ class SettingsRepository { Future getOAuthAccounts() async { try { return await settingsService.getOAuthAccounts(); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -44,7 +49,8 @@ class SettingsRepository { Future linkOAuthAccount({required OAuthAttachRequest request}) async { try { return await settingsService.linkOAuthAccount(request); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } @@ -52,7 +58,8 @@ class SettingsRepository { Future unlinkOAuthAccount({required String provider}) async { try { return await settingsService.unlinkOAuthAccount(provider); - } catch (e) { + } catch (e, s) { + await Sentry.captureException(e, stackTrace: s); rethrow; } } diff --git a/pubspec.yaml b/pubspec.yaml index 9a17e842..0e6808b1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: eatvio_app description: A new Flutter project. -version: 1.3.13+94 +version: 1.3.17+97 publish_to: 'none' environment: @@ -36,7 +36,6 @@ dependencies: url_launcher: ^6.3.2 easy_debounce: ^2.0.3 badges: ^3.1.2 - google_fonts: ^6.3.3 syncfusion_flutter_charts: ^32.1.20 syncfusion_flutter_gauges: ^32.1.20 table_calendar: ^3.2.0 @@ -84,6 +83,7 @@ dependencies: gpassword: ^1.0.0 webview_flutter_platform_interface: ^2.14.0 intl_utils: ^2.8.13 + sentry_dio: ^9.10.0 dev_dependencies: build_runner: ^2.10.4 @@ -111,6 +111,50 @@ flutter: - assets/icons/ - assets/splash/ - assets/lottie_animations/ + + fonts: + - family: Mulish-Local + fonts: + - asset: assets/fonts/Mulish-ExtraLight.ttf + weight: 200 + - asset: assets/fonts/Mulish-ExtraLightItalic.ttf + weight: 200 + style: italic + - asset: assets/fonts/Mulish-Light.ttf + weight: 300 + - asset: assets/fonts/Mulish-LightItalic.ttf + weight: 300 + style: italic + - asset: assets/fonts/Mulish-Regular.ttf + weight: 400 + - asset: assets/fonts/Mulish-Italic.ttf + weight: 400 + style: italic + - asset: assets/fonts/Mulish-Medium.ttf + weight: 500 + - asset: assets/fonts/Mulish-MediumItalic.ttf + weight: 500 + style: italic + - asset: assets/fonts/Mulish-SemiBold.ttf + weight: 600 + - asset: assets/fonts/Mulish-SemiBoldItalic.ttf + weight: 600 + style: italic + - asset: assets/fonts/Mulish-Bold.ttf + weight: 700 + - asset: assets/fonts/Mulish-BoldItalic.ttf + weight: 700 + style: italic + - asset: assets/fonts/Mulish-ExtraBold.ttf + weight: 800 + - asset: assets/fonts/Mulish-ExtraBoldItalic.ttf + weight: 800 + style: italic + - asset: assets/fonts/Mulish-Black.ttf + weight: 900 + - asset: assets/fonts/Mulish-BlackItalic.ttf + weight: 900 + style: italic flutter_intl: enabled: true diff --git a/test/core/interceptors/token_interceptor_test.dart b/test/core/interceptors/token_interceptor_test.dart index 56b1cecd..5995d4a5 100644 --- a/test/core/interceptors/token_interceptor_test.dart +++ b/test/core/interceptors/token_interceptor_test.dart @@ -1,6 +1,8 @@ import 'package:dio/dio.dart'; import 'package:eatvio_app/core/interceptors/token_interceptor.dart'; import 'package:eatvio_app/core/token_storage.dart'; +import 'package:eatvio_app/core/websockets/ws_manager.dart'; +import 'package:eatvio_app/core/locator_service.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -13,6 +15,8 @@ class MockErrorInterceptorHandler extends Mock implements ErrorInterceptorHandle class MockResponse extends Mock implements Response {} +class MockWsManager extends Mock implements WsManager {} + class MockDioException extends Mock implements DioException {} class FakeRequestOptions extends Fake implements RequestOptions {} @@ -31,6 +35,13 @@ void main() { setUp(() async { SharedPreferences.setMockInitialValues({}); + + final mockWsManager = MockWsManager(); + when(() => mockWsManager.clear()).thenAnswer((_) async {}); + + await sl.reset(); // Сбрасываем sl перед каждым тестом + sl.registerSingleton(mockWsManager); + interceptor = TokenInterceptor(); // Сбрасываем токен перед каждым тестом try { diff --git a/test/core/utils/validation_utils_test.dart b/test/core/utils/validation_utils_test.dart index e3f875a6..30c4adf1 100644 --- a/test/core/utils/validation_utils_test.dart +++ b/test/core/utils/validation_utils_test.dart @@ -30,6 +30,28 @@ void main() { }); }); + group('normalizePhone', () { + test('normalizes phone starting with +7', () { + expect(ValidationUtils.normalizePhone('+7 (915) 062 61 51'), '+79150626151'); + }); + + test('normalizes phone starting with 8', () { + expect(ValidationUtils.normalizePhone('89150626151'), '+79150626151'); + }); + + test('normalizes 10-digit phone', () { + expect(ValidationUtils.normalizePhone('9150626151'), '+79150626151'); + }); + + test('returns empty string for empty input', () { + expect(ValidationUtils.normalizePhone(''), ''); + }); + + test('returns empty string for input with no digits', () { + expect(ValidationUtils.normalizePhone('abc'), ''); + }); + }); + group('isValidUrl', () { test('returns true for valid URL', () { expect(ValidationUtils.isValidUrl('https://example.com'), isTrue); diff --git a/test/features/create_diet/integration_widget_test.dart b/test/features/create_diet/integration_widget_test.dart index 0d8c7b31..541056f6 100644 --- a/test/features/create_diet/integration_widget_test.dart +++ b/test/features/create_diet/integration_widget_test.dart @@ -326,7 +326,7 @@ void main() { expect(find.byType(DietTypeChoiceWidget), findsOneWidget); expect(find.byType(DietImagePickerWidget), findsOneWidget); // Action buttons - expect(find.byType(CustomOutlinedButton), findsNWidgets(3)); + expect(find.byType(CustomOutlinedButton), findsNWidgets(2)); }); }); } diff --git a/test/features/my_diets/presentation/widgets/basic_widget_test.dart b/test/features/my_diets/presentation/widgets/basic_widget_test.dart index 9afb6818..ef674d0f 100644 --- a/test/features/my_diets/presentation/widgets/basic_widget_test.dart +++ b/test/features/my_diets/presentation/widgets/basic_widget_test.dart @@ -106,7 +106,7 @@ void main() { // Assert expect(find.byType(MyDietsTab), findsOneWidget); - expect(find.byType(AnimatedOptionButton), findsNWidgets(3)); + expect(find.byType(AnimatedOptionButton), findsNWidgets(2)); }); testWidgets('should handle tab selection in MyDietsTab', (WidgetTester tester) async { @@ -119,7 +119,7 @@ void main() { // Act - Find and tap the second tab final tabButtons = find.byType(AnimatedOptionButton); - expect(tabButtons, findsNWidgets(3)); + expect(tabButtons, findsNWidgets(2)); // Tap the second tab (index 1) await tester.tap(tabButtons.at(1)); @@ -221,7 +221,7 @@ void main() { // Assert expect(find.byType(MyDietsTab), findsOneWidget); - expect(find.byType(AnimatedOptionButton), findsNWidgets(3)); + expect(find.byType(AnimatedOptionButton), findsNWidgets(2)); }); testWidgets('should handle MyDietsTab additionalOnTap callback', (WidgetTester tester) async { diff --git a/test_errors.txt b/test_errors.txt deleted file mode 100644 index 2db05e50..00000000 Binary files a/test_errors.txt and /dev/null differ diff --git a/test_output.txt b/test_output.txt deleted file mode 100644 index f57e44e0..00000000 --- a/test_output.txt +++ /dev/null @@ -1,23 +0,0 @@ - 00:00 +0: loading /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/image_compressing/image_compress_utility_test.dart 00:01 +0: loading /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/image_compressing/image_compress_utility_test.dart 00:02 +0: loading /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/image_compressing/image_compress_utility_test.dart 00:03 +0: loading /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/image_compressing/image_compress_utility_test.dart 00:03 +0: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/image_compressing/image_compress_utility_test.dart: ImageCompressUtility _isImageFile returns true for image extensions 00:03 +1: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/image_compressing/image_compress_utility_test.dart: ImageCompressUtility _isImageFile returns true for image extensions 00:03 +1: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/image_compressing/image_compress_utility_test.dart: ImageCompressUtility compressFilesList returns files list 00:03 +2: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/image_compressing/image_compress_utility_test.dart: ImageCompressUtility compressFilesList returns files list 00:03 +2: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/image_compressing/image_compress_utility_test.dart: ... compressFile returns original file in test environment 00:03 +3: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/image_compressing/image_compress_utility_test.dart: ... compressFile returns original file in test environment 00:03 +3: loading /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/interceptors/phone_verify_error_interceptor_test.dart 00:03 +3: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/interceptors/phone_verify_error_interceptor_test.dart: PhoneVerifyErrorInterceptor (setUpAll) 00:03 +3: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/interceptors/phone_verify_error_interceptor_test.dart: ... onError - обрабатывает 405 с unverificated_phone 00:03 +4: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/interceptors/phone_verify_error_interceptor_test.dart: ... onError - обрабатывает 405 с unverificated_phone 00:03 +4: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/interceptors/phone_verify_error_interceptor_test.dart: PhoneVerifyErrorInterceptor onError - пропускает другие ошибки 00:03 +5: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/interceptors/phone_verify_error_interceptor_test.dart: PhoneVerifyErrorInterceptor onError - пропускает другие ошибки 00:03 +5: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/interceptors/phone_verify_error_interceptor_test.dart: PhoneVerifyErrorInterceptor (tearDownAll) 00:04 +5: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/interceptors/phone_verify_error_interceptor_test.dart: PhoneVerifyErrorInterceptor (tearDownAll) 00:04 +5: loading /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/repositories/comments_repository_test.dart 00:04 +5: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/repositories/comments_repository_test.dart: CommentsRepository deleteComment calls service.deleteComment 00:04 +6: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/repositories/comments_repository_test.dart: CommentsRepository deleteComment calls service.deleteComment 00:04 +6: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/repositories/comments_repository_test.dart: CommentsRepository deleteComment rethrows exception when service throws 00:04 +7: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/repositories/comments_repository_test.dart: CommentsRepository deleteComment rethrows exception when service throws 00:04 +7: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/repositories/comments_repository_test.dart: ... updateComment calls service.updateComment with correct model 00:04 +8: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/repositories/comments_repository_test.dart: ... updateComment calls service.updateComment with correct model 00:04 +8: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/repositories/comments_repository_test.dart: CommentsRepository updateComment rethrows exception when service throws 00:04 +9: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/repositories/comments_repository_test.dart: CommentsRepository updateComment rethrows exception when service throws 00:04 +9: loading /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/interceptors/token_interceptor_test.dart 00:04 +9: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/interceptors/token_interceptor_test.dart: (setUpAll) 00:04 +10: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/interceptors/token_interceptor_test.dart: (setUpAll) 00:04 +10: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/models/comment_update_model_test.dart: CommentUpdateModel creates CommentUpdateModel with empty body 00:04 +11: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/interceptors/token_interceptor_test.dart: TokenInterceptor onRequest добавляет Authorization header когда токен есть 00:04 +12: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/interceptors/token_interceptor_test.dart: TokenInterceptor onRequest добавляет Authorization header когда токен есть 00:04 +13: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/interceptors/token_interceptor_test.dart: TokenInterceptor onRequest добавляет Authorization header когда токен есть 00:04 +14: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/interceptors/token_interceptor_test.dart: TokenInterceptor onRequest добавляет Authorization header когда токен есть 00:04 +15: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/interceptors/token_interceptor_test.dart: TokenInterceptor onRequest добавляет Authorization header когда токен есть 00:04 +15: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/interceptors/token_interceptor_test.dart: TokenInterceptor onRequest не добавляет Authorization header когда токена нет 00:04 +16: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/interceptors/token_interceptor_test.dart: TokenInterceptor onRequest не добавляет Authorization header когда токена нет 00:04 +16: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/interceptors/token_interceptor_test.dart: TokenInterceptor onRequest не добавляет Authorization header когда токен пустой 00:04 +17: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/interceptors/token_interceptor_test.dart: TokenInterceptor onRequest не добавляет Authorization header когда токен пустой 00:04 +17: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/interceptors/token_interceptor_test.dart: TokenInterceptor onError удаляет токен и перенаправляет на auth при 401 00:04 +18: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/interceptors/token_interceptor_test.dart: TokenInterceptor onError удаляет токен и перенаправляет на auth при 401 00:04 +18: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/interceptors/token_interceptor_test.dart: TokenInterceptor onError не удаляет токен при других статусах 00:04 +19: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/interceptors/token_interceptor_test.dart: TokenInterceptor onError не удаляет токен при других статусах 00:04 +19: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/interceptors/token_interceptor_test.dart: TokenInterceptor onError не удаляет токен когда response null 00:04 +20: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/interceptors/token_interceptor_test.dart: TokenInterceptor onError не удаляет токен когда response null 00:04 +20: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/interceptors/token_interceptor_test.dart: (tearDownAll) 00:05 +20: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/interceptors/token_interceptor_test.dart: (tearDownAll) 00:05 +20: loading /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/comments_cubit/comments_cubit_test.dart 00:05 +20: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/comments_cubit/comments_cubit_test.dart: CommentsCubit начальное состояние - CommentsState 00:05 +21: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/comments_cubit/comments_cubit_test.dart: CommentsCubit начальное состояние - CommentsState 00:05 +21: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/comments_cubit/comments_cubit_test.dart: CommentsCubit getComments - успешная загрузка комментариев для blogPost 00:05 +22: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/comments_cubit/comments_cubit_test.dart: CommentsCubit getComments - успешная загрузка комментариев для blogPost 00:05 +22: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/comments_cubit/comments_cubit_test.dart: CommentsCubit getComments - обработка ошибки 00:05 +23: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/comments_cubit/comments_cubit_test.dart: CommentsCubit getComments - обработка ошибки 00:05 +23: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/comments_cubit/comments_cubit_test.dart: CommentsCubit sendComment - отправка комментария для blogPost 00:05 +24: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/comments_cubit/comments_cubit_test.dart: CommentsCubit sendComment - отправка комментария для blogPost 00:05 +24: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/comments_cubit/comments_cubit_test.dart: CommentsCubit deleteComment - успешное удаление 00:05 +25: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/comments_cubit/comments_cubit_test.dart: CommentsCubit deleteComment - успешное удаление 00:05 +25: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/comments_cubit/comments_cubit_test.dart: CommentsCubit updateComment - успешное обновление 00:05 +26: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/comments_cubit/comments_cubit_test.dart: CommentsCubit updateComment - успешное обновление 00:05 +26: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/comments_cubit/comments_cubit_test.dart: CommentsCubit setReplyId - устанавливает replyId 00:05 +27: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/comments_cubit/comments_cubit_test.dart: CommentsCubit setReplyId - устанавливает replyId 00:05 +27: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/comments_cubit/comments_cubit_test.dart: CommentsCubit setReplyId - устанавливает null replyId 00:05 +28: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/comments_cubit/comments_cubit_test.dart: CommentsCubit setReplyId - устанавливает null replyId 00:05 +28: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/comments_cubit/comments_cubit_test.dart: CommentsCubit getComments - успешная загрузка комментариев для diet 00:05 +29: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/comments_cubit/comments_cubit_test.dart: CommentsCubit getComments - успешная загрузка комментариев для diet 00:05 +29: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/comments_cubit/comments_cubit_test.dart: CommentsCubit getComments - успешная загрузка комментариев для recipe 00:05 +30: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/comments_cubit/comments_cubit_test.dart: CommentsCubit getComments - успешная загрузка комментариев для recipe 00:05 +30: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/comments_cubit/comments_cubit_test.dart: CommentsCubit getComments - успешная загрузка комментариев для plan 00:05 +31: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/comments_cubit/comments_cubit_test.dart: CommentsCubit getComments - успешная загрузка комментариев для plan 00:05 +31: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/comments_cubit/comments_cubit_test.dart: CommentsCubit getComments - без isFirstFetch не показывает loading 00:05 +32: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: ... displays edit and delete actions for my comment 00:05 +33: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: ... displays edit and delete actions for my comment 00:05 +34: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: ... displays edit and delete actions for my comment 00:05 +35: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: ... displays edit and delete actions for my comment 00:05 +36: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: ... displays edit and delete actions for my comment 00:05 +37: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: ... displays edit and delete actions for my comment 00:05 +38: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: ... displays edit and delete actions for my comment 00:05 +39: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: ... displays edit and delete actions for my comment 00:05 +40: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: ... displays edit and delete actions for my comment 00:05 +41: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: ... displays edit and delete actions for my comment 00:05 +42: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: ... displays edit and delete actions for my comment 00:05 +43: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: ... displays edit and delete actions for my comment 00:05 +44: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: ... displays edit and delete actions for my comment 00:05 +45: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: ... displays edit and delete actions for my comment 00:05 +46: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: ... displays edit and delete actions for my comment 00:05 +47: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: ... displays edit and delete actions for my comment 00:05 +48: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: ... displays edit and delete actions for my comment 00:05 +49: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: ... displays edit and delete actions for my comment 00:05 +50: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/cubit/state_utils_test.dart: StateUtils isLoaded returns false for non-loaded state 00:05 +51: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: ... displays reply and complain actions for other comment 00:05 +52: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: ... displays reply and complain actions for other comment 00:05 +53: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: ... displays reply and complain actions for other comment 00:05 +54: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: ... displays reply and complain actions for other comment 00:05 +55: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: ... displays reply and complain actions for other comment 00:05 +56: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: ... displays reply and complain actions for other comment 00:05 +57: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: ... displays reply and complain actions for other comment 00:05 +57: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: ... switches to edit mode when edit is tapped 00:05 +58: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: ... switches to edit mode when edit is tapped 00:05 +58: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: CommentsActionsBottomSheet calls copy callback 00:05 +59: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: CommentsActionsBottomSheet calls copy callback 00:05 +59: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: CommentsActionsBottomSheet displays handler bar 00:05 +60: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: CommentsActionsBottomSheet displays handler bar 00:05 +60: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: ... shows edit mode when edit button is tapped 00:05 +61: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: ... shows edit mode when edit button is tapped 00:05 +62: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: ... shows edit mode when edit button is tapped 00:05 +63: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: ... shows edit mode when edit button is tapped 00:05 +64: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: ... shows edit mode when edit button is tapped 00:05 +65: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: ... shows edit mode when edit button is tapped 00:05 +66: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: ... shows edit mode when edit button is tapped 00:05 +67: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: ... shows edit mode when edit button is tapped 00:06 +67: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: ... shows edit mode when edit button is tapped 00:06 +68: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: ... shows edit mode when edit button is tapped 00:06 +68: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: ... calls edit callback when send button is tapped 00:06 +69: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: ... calls edit callback when send button is tapped 00:06 +69: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: ... shows reply action when reply callback is provided 00:06 +70: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: ... shows reply action when reply callback is provided 00:06 +70: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: ... shows delete action for my comment 00:06 +71: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: ... shows delete action for my comment 00:06 +71: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: ... shows complain action for other comment 00:06 +72: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: ... shows complain action for other comment 00:06 +72: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: ... shows reply action for my comment when reply is provided 00:06 +73: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: ... shows reply action for my comment when reply is provided 00:06 +73: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: ... send button is disabled when text is too short 00:06 +74: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: ... send button is disabled when text is too short 00:06 +74: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: ... initializes with comment text in controller 00:06 +75: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: ... initializes with comment text in controller 00:06 +75: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: showCommentsActionsBottomSheet shows modal bottom sheet 00:06 +76: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: showCommentsActionsBottomSheet shows modal bottom sheet 00:06 +76: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/comments/presentation/comments_actions_bottom_sheet_test.dart: ... calls callbacks when actions are tapped 00:06 +77: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/cubit/base_cubit_test.dart: BaseCubit initial state is set correctly 00:06 +78: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/cubit/base_cubit_test.dart: BaseCubit initial state is set correctly 00:06 +78: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/cubit/base_cubit_test.dart: BaseCubit setState updates state 00:06 +79: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/cubit/base_cubit_test.dart: BaseCubit setState updates state 00:06 +79: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/cubit/base_cubit_test.dart: BaseCubit safeExecute returns result on success 00:06 +80: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/cubit/base_cubit_test.dart: BaseCubit safeExecute returns result on success 00:06 +80: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/cubit/base_cubit_test.dart: BaseCubit safeExecute returns null on error 00:06 +81: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/cubit/base_cubit_test.dart: BaseCubit safeExecute returns null on error 00:06 +81: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/cubit/base_cubit_test.dart: BaseCubit safeExecuteWithState emits loading then success state 00:06 +82: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/cubit/base_cubit_test.dart: BaseCubit safeExecuteWithState emits loading then success state 00:06 +82: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/cubit/base_cubit_test.dart: BaseCubit safeExecuteWithState emits loading then error state on failure 00:06 +83: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/cubit/base_cubit_test.dart: BaseCubit safeExecuteWithState emits loading then error state on failure 00:06 +83: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/cubit/base_cubit_test.dart: BaseCubit safeExecuteWithState returns null on error 00:06 +84: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/cubit/base_cubit_test.dart: BaseCubit safeExecuteWithState returns null on error 00:06 +84: loading /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/cubit/marketing/marketing_cubit_test.dart 00:06 +84: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/cubit/marketing/marketing_cubit_test.dart: MarketingCubit initial state is MarketingCubitState 00:06 +85: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/cubit/marketing/marketing_cubit_test.dart: MarketingCubit initial state is MarketingCubitState 00:06 +85: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/cubit/marketing/marketing_cubit_test.dart: MarketingCubit returns false when registration date is null 00:06 +86: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/cubit/marketing/marketing_cubit_test.dart: MarketingCubit returns false when registration date is null 00:06 +86: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/cubit/marketing/marketing_cubit_test.dart: MarketingCubit returns false when marketing bottom sheet has been shown 00:06 +87: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/cubit/marketing/marketing_cubit_test.dart: MarketingCubit returns false when marketing bottom sheet has been shown 00:06 +87: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/cubit/marketing/marketing_cubit_test.dart: MarketingCubit returns false when less than 14 days have passed 00:06 +88: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/cubit/marketing/marketing_cubit_test.dart: MarketingCubit returns false when less than 14 days have passed 00:06 +88: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/cubit/marketing/marketing_cubit_test.dart: MarketingCubit returns true and sets flag when 14 or more days have passed 00:06 +89: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/cubit/marketing/marketing_cubit_test.dart: MarketingCubit returns true and sets flag when 14 or more days have passed 00:06 +89: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/cubit/marketing/marketing_cubit_test.dart: MarketingCubit returns true when more than 14 days have passed 00:06 +90: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/cubit/marketing/marketing_cubit_test.dart: MarketingCubit returns true when more than 14 days have passed 00:06 +90: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/cubit/marketing/marketing_cubit_test.dart: MarketingCubit rethrows exception when error occurs 00:06 +91: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/cubit/marketing/marketing_cubit_test.dart: MarketingCubit rethrows exception when error occurs 00:07 +91: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/cubit/marketing/marketing_cubit_test.dart: MarketingCubit rethrows exception when error occurs 00:07 +91: loading /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/cubit/marketing/marketing_cubit_state_test.dart 00:07 +91: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/cubit/marketing/marketing_cubit_state_test.dart: MarketingCubitState creates MarketingCubitState 00:07 +92: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: (setUpAll) 00:07 +93: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: (setUpAll) 00:07 +94: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: (setUpAll) 00:07 +94: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has baseUrl getter 00:07 +95: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has baseUrl getter 00:07 +95: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has devBaseUrl getter 00:07 +96: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has devBaseUrl getter 00:07 +96: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has sentryDsn getter 00:07 +97: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has sentryDsn getter 00:07 +97: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has appMetricaApiKey getter 00:07 +98: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has appMetricaApiKey getter 00:07 +98: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has firebaseProjectId getter 00:07 +99: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has firebaseProjectId getter 00:07 +99: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has firebaseDatabaseUrl getter 00:07 +100: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has firebaseDatabaseUrl getter 00:07 +100: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has firebaseStorageBucket getter 00:07 +101: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has firebaseStorageBucket getter 00:07 +101: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has firebaseMessagingSenderId getter 00:07 +102: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has firebaseMessagingSenderId getter 00:07 +102: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has firebaseAndroidApiKey getter 00:07 +103: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has firebaseAndroidApiKey getter 00:07 +103: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has firebaseAndroidAppId getter 00:07 +104: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has firebaseAndroidAppId getter 00:07 +104: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has firebaseIosApiKey getter 00:07 +105: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has firebaseIosApiKey getter 00:07 +105: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has firebaseIosAppId getter 00:07 +106: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has firebaseIosAppId getter 00:07 +106: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has firebaseIosBundleId getter 00:07 +107: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has firebaseIosBundleId getter 00:07 +107: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has appStoreUrl getter 00:07 +108: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has appStoreUrl getter 00:07 +108: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has rustoreUrl getter 00:07 +109: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has rustoreUrl getter 00:07 +109: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has telegramSupportUrl getter 00:07 +110: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has telegramSupportUrl getter 00:07 +110: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has supportEmail getter 00:07 +111: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has supportEmail getter 00:07 +111: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has testEmail getter 00:07 +112: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has testEmail getter 00:07 +112: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has testPassword getter 00:07 +113: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has testPassword getter 00:07 +113: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has emailValidationApiUrl getter 00:07 +114: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has emailValidationApiUrl getter 00:07 +114: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has googleCheckUrl getter 00:07 +115: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has googleCheckUrl getter 00:07 +115: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has yandexCheckUrl getter 00:07 +116: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has yandexCheckUrl getter 00:07 +116: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has vkClientIdIos getter 00:07 +117: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has vkClientIdIos getter 00:07 +117: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has vkClientIdAndroid getter 00:07 +118: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has vkClientIdAndroid getter 00:07 +118: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has yandexClientId getter 00:07 +119: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has yandexClientId getter 00:07 +119: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has yandexClientSecret getter 00:07 +120: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has yandexClientSecret getter 00:07 +120: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has yandexRedirectUri getter 00:07 +121: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: EnvConfig has yandexRedirectUri getter 00:07 +121: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/env_config_test.dart: (tearDownAll) 00:07 +121: loading /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/prefs_keys_test.dart 00:07 +121: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/prefs_keys_test.dart: PrefsKeys все значения enum определены 00:07 +122: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/prefs_keys_test.dart: PrefsKeys все значения enum определены 00:07 +122: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/prefs_keys_test.dart: PrefsKeys имена ключей корректны 00:07 +123: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/prefs_keys_test.dart: PrefsKeys имена ключей корректны 00:08 +123: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/prefs_keys_test.dart: PrefsKeys имена ключей корректны 00:08 +123: loading /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/di/app_config_registration_test.dart 00:08 +123: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/di/app_config_registration_test.dart: AppConfigRegistration registerAppConfig регистрирует AppConfig 00:08 +124: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/di/app_config_registration_test.dart: AppConfigRegistration registerAppConfig регистрирует AppConfig 00:08 +124: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/di/app_config_registration_test.dart: AppConfigRegistration registerAppConfig регистрирует AppConfig с разными buildType 00:08 +125: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/di/app_config_registration_test.dart: AppConfigRegistration registerAppConfig регистрирует AppConfig с разными buildType 00:08 +125: loading /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/token_storage_test.dart 00:08 +125: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/token_storage_test.dart: TokenStorage haveTokenInStorage returns false when no token is stored 00:08 +126: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/token_storage_test.dart: TokenStorage haveTokenInStorage returns false when no token is stored 00:08 +126: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/token_storage_test.dart: TokenStorage haveTokenInStorage returns true when token is stored 00:08 +127: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/token_storage_test.dart: TokenStorage haveTokenInStorage returns true when token is stored 00:08 +127: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/token_storage_test.dart: TokenStorage getTokenFromStorage returns null when no token is stored 00:08 +128: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/token_storage_test.dart: TokenStorage getTokenFromStorage returns null when no token is stored 00:08 +128: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/token_storage_test.dart: TokenStorage getTokenFromStorage returns token when token is stored 00:08 +129: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/token_storage_test.dart: TokenStorage getTokenFromStorage returns token when token is stored 00:08 +129: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/token_storage_test.dart: TokenStorage saveTokenInStorage saves token correctly 00:08 +130: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/token_storage_test.dart: TokenStorage saveTokenInStorage saves token correctly 00:08 +130: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/token_storage_test.dart: TokenStorage removeTokenInStorage removes token correctly 00:08 +131: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/token_storage_test.dart: TokenStorage removeTokenInStorage removes token correctly 00:08 +131: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/token_storage_test.dart: TokenStorage can save and retrieve multiple tokens sequentially 00:08 +132: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/token_storage_test.dart: TokenStorage can save and retrieve multiple tokens sequentially 00:08 +132: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/token_storage_test.dart: SocketIdStorage haveSocketIdInStorage returns false when no socketId is stored 00:08 +133: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/token_storage_test.dart: SocketIdStorage haveSocketIdInStorage returns false when no socketId is stored 00:08 +133: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/token_storage_test.dart: SocketIdStorage haveSocketIdInStorage returns true when socketId is stored 00:08 +134: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/token_storage_test.dart: SocketIdStorage haveSocketIdInStorage returns true when socketId is stored 00:08 +134: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/token_storage_test.dart: SocketIdStorage getSocketIdFromStorage returns null when no socketId is stored 00:08 +135: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/token_storage_test.dart: SocketIdStorage getSocketIdFromStorage returns null when no socketId is stored 00:08 +135: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/token_storage_test.dart: SocketIdStorage getSocketIdFromStorage returns socketId when socketId is stored 00:08 +136: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/token_storage_test.dart: SocketIdStorage getSocketIdFromStorage returns socketId when socketId is stored 00:08 +136: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/token_storage_test.dart: SocketIdStorage saveSocketIdInStorage saves socketId correctly 00:08 +137: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/token_storage_test.dart: SocketIdStorage saveSocketIdInStorage saves socketId correctly 00:08 +137: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/token_storage_test.dart: SocketIdStorage removeSocketIdInStorage removes socketId correctly 00:08 +138: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/token_storage_test.dart: SocketIdStorage removeSocketIdInStorage removes socketId correctly 00:08 +138: loading /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/di/external_registrations_test.dart 00:08 +138: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/di/external_registrations_test.dart: ExternalRegistrations registerAll регистрирует все внешние зависимости 00:08 +139: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/di/external_registrations_test.dart: ExternalRegistrations registerAll регистрирует все внешние зависимости 00:08 +139: loading /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/app_initializer_test.dart 00:08 +139: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/app_initializer_test.dart: AppBuildType has prod value 00:08 +140: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/app_initializer_test.dart: AppBuildType has prod value 00:08 +140: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/app_initializer_test.dart: AppBuildType has tests value 00:08 +141: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/app_initializer_test.dart: AppBuildType has tests value 00:08 +141: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/app_initializer_test.dart: AppBuildType has dev value 00:08 +142: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/app_initializer_test.dart: AppBuildType has dev value 00:08 +142: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/app_initializer_test.dart: AppBuildType all values are distinct 00:08 +143: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/app_initializer_test.dart: AppBuildType all values are distinct 00:08 +143: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/app_initializer_test.dart: AppInitializer (setUpAll) 00:08 +143: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/app_initializer_test.dart: AppInitializer is a class 00:08 +144: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/app_initializer_test.dart: AppInitializer is a class 00:08 +144: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/app_initializer_test.dart: AppInitializer has initialize static method 00:08 +145: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/app_initializer_test.dart: AppInitializer has initialize static method 00:08 +145: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/app_initializer_test.dart: AppInitializer has runAppWithSentry static method 00:08 +146: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/app_initializer_test.dart: AppInitializer has runAppWithSentry static method 00:08 +146: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/app_initializer_test.dart: AppInitializer initialize - вызывает все методы инициализации для tests buildType 00:08 +147: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/app_initializer_test.dart: AppInitializer initialize - вызывает все методы инициализации для tests buildType 00:08 +147: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/app_initializer_test.dart: AppInitializer initialize - вызывает все методы инициализации для dev buildType 00:08 +148: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/app_initializer_test.dart: AppInitializer initialize - вызывает все методы инициализации для dev buildType 00:08 +148: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/app_initializer_test.dart: AppInitializer initialize - вызывает все методы инициализации для prod buildType 00:08 +149: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/app_initializer_test.dart: AppInitializer initialize - вызывает все методы инициализации для prod buildType 00:08 +149: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/app_initializer_test.dart: AppInitializer runAppWithSentry - может быть вызван 00:08 +150: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/app_initializer_test.dart: AppInitializer runAppWithSentry - может быть вызван 00:08 +150: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/app_initializer_test.dart: AppInitializer (tearDownAll) 00:09 +150: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/app_initializer_test.dart: AppInitializer (tearDownAll) 00:09 +150: loading /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/locator_service_test.dart 00:09 +150: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/locator_service_test.dart: LocatorService sl является экземпляром GetIt 00:09 +151: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/locator_service_test.dart: LocatorService sl является экземпляром GetIt 00:09 +151: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/locator_service_test.dart: LocatorService init регистрирует зависимости 00:09 +152: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/locator_service_test.dart: LocatorService init регистрирует зависимости 00:09 +152: loading /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/constants/app_constants_test.dart 00:09 +152: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/constants/app_constants_test.dart: AppConstants Sizes has correct padding values 00:09 +153: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/constants/app_constants_test.dart: AppConstants Sizes has correct padding values 00:09 +153: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/constants/app_constants_test.dart: AppConstants Sizes has correct border radius values 00:09 +154: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/constants/app_constants_test.dart: AppConstants Sizes has correct border radius values 00:09 +154: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/constants/app_constants_test.dart: AppConstants Sizes has correct button height values 00:09 +155: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/constants/app_constants_test.dart: AppConstants Sizes has correct button height values 00:09 +155: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/constants/app_constants_test.dart: AppConstants Sizes has correct content width values 00:09 +156: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/constants/app_constants_test.dart: AppConstants Sizes has correct content width values 00:09 +156: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/constants/app_constants_test.dart: AppConstants Animations has correct animation durations 00:09 +157: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/constants/app_constants_test.dart: AppConstants Animations has correct animation durations 00:09 +157: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/constants/app_constants_test.dart: AppConstants Timeouts has correct timeout durations 00:09 +158: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/constants/app_constants_test.dart: AppConstants Timeouts has correct timeout durations 00:09 +158: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/constants/app_constants_test.dart: AppConstants Limits has correct size limits 00:09 +159: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/constants/app_constants_test.dart: AppConstants Limits has correct size limits 00:09 +159: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/constants/app_constants_test.dart: AppConstants Limits has correct text length limits 00:09 +160: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/constants/app_constants_test.dart: AppConstants Limits has correct text length limits 00:09 +160: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/constants/app_constants_test.dart: AppConstants Pagination has correct page sizes 00:09 +161: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/constants/app_constants_test.dart: AppConstants Pagination has correct page sizes 00:09 +161: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/constants/app_constants_test.dart: AppConstants Formats has supported image formats 00:09 +162: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/constants/app_constants_test.dart: AppConstants Formats has supported image formats 00:09 +162: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/constants/app_constants_test.dart: AppConstants Formats has supported video formats 00:09 +163: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/constants/app_constants_test.dart: AppConstants Formats has supported video formats 00:09 +163: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/constants/app_constants_test.dart: AppConstants Regex has email regex 00:09 +164: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/constants/app_constants_test.dart: AppConstants Regex has email regex 00:09 +164: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/constants/app_constants_test.dart: AppConstants Regex has phone regex 00:09 +165: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/constants/app_constants_test.dart: AppConstants Regex has phone regex 00:09 +165: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/constants/app_constants_test.dart: AppConstants Regex has URL regex 00:09 +166: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/constants/app_constants_test.dart: AppConstants Regex has URL regex 00:09 +166: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/constants/app_constants_test.dart: AppConstants Messages has error messages 00:09 +167: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/constants/app_constants_test.dart: AppConstants Messages has error messages 00:09 +167: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/constants/app_constants_test.dart: AppConstants Keys has SharedPreferences keys 00:09 +168: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/constants/app_constants_test.dart: AppConstants Keys has SharedPreferences keys 00:09 +168: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/constants/app_constants_test.dart: AppConstants API Endpoints has API endpoints 00:09 +169: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/constants/app_constants_test.dart: AppConstants API Endpoints has API endpoints 00:09 +169: loading /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/mixins/registration_date_cache_mixin_test.dart 00:09 +169: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/mixins/registration_date_cache_mixin_test.dart: RegistrationDateCacheMixin caches registration date 00:09 +170: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/mixins/registration_date_cache_mixin_test.dart: RegistrationDateCacheMixin caches registration date 00:09 +170: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/mixins/registration_date_cache_mixin_test.dart: RegistrationDateCacheMixin caches different dates correctly 00:09 +171: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/mixins/registration_date_cache_mixin_test.dart: RegistrationDateCacheMixin caches different dates correctly 00:09 +171: loading /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/enums/prefs_keys_test.dart 00:09 +171: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/enums/prefs_keys_test.dart: PrefsKeys has all expected values 00:09 +172: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/enums/prefs_keys_test.dart: PrefsKeys has all expected values 00:09 +172: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/enums/prefs_keys_test.dart: PrefsKeys can be compared 00:09 +173: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/enums/prefs_keys_test.dart: PrefsKeys can be compared 00:09 +173: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/enums/prefs_keys_test.dart: PrefsKeys has correct name property 00:09 +174: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/enums/prefs_keys_test.dart: PrefsKeys has correct name property 00:10 +174: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/enums/prefs_keys_test.dart: PrefsKeys has correct name property 00:10 +174: loading /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/enums/product_type_test.dart 00:10 +174: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/enums/product_type_test.dart: ProductType has all expected values 00:10 +175: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/enums/product_type_test.dart: ProductType has all expected values 00:10 +175: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/enums/product_type_test.dart: ProductType can be compared 00:10 +176: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/enums/product_type_test.dart: ProductType can be compared 00:10 +176: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/enums/product_type_test.dart: ProductType has correct index 00:10 +177: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/enums/product_type_test.dart: ProductType has correct index 00:10 +177: loading /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/enums/base_state_test.dart 00:10 +177: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/enums/base_state_test.dart: BaseState has all expected values 00:10 +178: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/enums/base_state_test.dart: BaseState has all expected values 00:10 +178: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/enums/base_state_test.dart: BaseState can be compared 00:10 +179: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/enums/base_state_test.dart: BaseState can be compared 00:10 +179: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/enums/base_state_test.dart: BaseState has correct index 00:10 +180: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/enums/base_state_test.dart: BaseState has correct index 00:10 +180: loading /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/enums/filters_comparison_enum_test.dart 00:10 +180: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/enums/filters_comparison_enum_test.dart: FiltersComparisonEnum has all expected values 00:11 +181: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/enums/filters_comparison_enum_test.dart: FiltersComparisonEnum has all expected values 00:11 +181: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/enums/filters_comparison_enum_test.dart: FiltersComparisonEnum has correct string values 00:11 +182: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/enums/filters_comparison_enum_test.dart: FiltersComparisonEnum has correct string values 00:11 +182: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/enums/filters_comparison_enum_test.dart: FiltersComparisonEnum can be compared 00:11 +183: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/enums/filters_comparison_enum_test.dart: FiltersComparisonEnum can be compared 00:11 +183: loading /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/enums/complained_content_test.dart 00:11 +183: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/enums/complained_content_test.dart: ComplainedContent has all expected values 00:11 +184: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/enums/complained_content_test.dart: ComplainedContent has all expected values 00:11 +184: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/enums/complained_content_test.dart: ComplainedContent can be compared 00:11 +185: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/enums/complained_content_test.dart: ComplainedContent can be compared 00:11 +185: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/enums/complained_content_test.dart: ComplainedContent has correct index 00:11 +186: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/enums/complained_content_test.dart: ComplainedContent has correct index 00:11 +186: loading /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/validation_utils_test.dart 00:11 +186: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/validation_utils_test.dart: ValidationUtils isValidEmail returns true for valid email 00:11 +187: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/validation_utils_test.dart: ValidationUtils isValidEmail returns true for valid email 00:11 +187: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/validation_utils_test.dart: ValidationUtils isValidEmail returns false for invalid email 00:11 +188: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/validation_utils_test.dart: ValidationUtils isValidEmail returns false for invalid email 00:11 +188: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/validation_utils_test.dart: ValidationUtils isValidPhone returns true for valid phone 00:11 +189: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/validation_utils_test.dart: ValidationUtils isValidPhone returns true for valid phone 00:11 +189: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/validation_utils_test.dart: ValidationUtils isValidPhone returns false for invalid phone 00:11 +190: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/validation_utils_test.dart: ValidationUtils isValidPhone returns false for invalid phone 00:11 +190: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/validation_utils_test.dart: ValidationUtils isValidUrl returns true for valid URL 00:11 +191: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/validation_utils_test.dart: ValidationUtils isValidUrl returns true for valid URL 00:11 +191: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/validation_utils_test.dart: ValidationUtils isValidUrl returns false for invalid URL 00:11 +192: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/validation_utils_test.dart: ValidationUtils isValidUrl returns false for invalid URL 00:11 +192: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/validation_utils_test.dart: ValidationUtils isValidTextLength returns true for text within max length 00:11 +193: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/validation_utils_test.dart: ValidationUtils isValidTextLength returns true for text within max length 00:11 +193: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/validation_utils_test.dart: ValidationUtils isValidTextLength returns false for text exceeding max length 00:11 +194: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/validation_utils_test.dart: ValidationUtils isValidTextLength returns false for text exceeding max length 00:11 +194: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/validation_utils_test.dart: ValidationUtils isValidTitleLength returns true for valid title 00:11 +195: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/validation_utils_test.dart: ValidationUtils isValidTitleLength returns true for valid title 00:11 +195: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/validation_utils_test.dart: ValidationUtils isValidTitleLength returns false for invalid title 00:11 +196: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/validation_utils_test.dart: ValidationUtils isValidTitleLength returns false for invalid title 00:11 +196: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/validation_utils_test.dart: ValidationUtils isValidDescriptionLength returns true for valid description 00:11 +197: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/validation_utils_test.dart: ValidationUtils isValidDescriptionLength returns true for valid description 00:11 +197: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/validation_utils_test.dart: ... isValidDescriptionLength returns false for description exceeding max length 00:11 +198: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/validation_utils_test.dart: ... isValidDescriptionLength returns false for description exceeding max length 00:11 +198: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/validation_utils_test.dart: ValidationUtils isValidPassword returns true for valid password 00:11 +199: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/validation_utils_test.dart: ValidationUtils isValidPassword returns true for valid password 00:11 +199: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/validation_utils_test.dart: ValidationUtils isValidPassword returns false for invalid password 00:11 +200: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/validation_utils_test.dart: ValidationUtils isValidPassword returns false for invalid password 00:11 +200: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/validation_utils_test.dart: ValidationUtils isValidUsername returns true for valid username 00:11 +201: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/validation_utils_test.dart: ValidationUtils isValidUsername returns true for valid username 00:11 +201: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/validation_utils_test.dart: ValidationUtils isValidUsername returns false for invalid username 00:11 +202: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/validation_utils_test.dart: ValidationUtils isValidUsername returns false for invalid username 00:11 +202: loading /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/websockets/ws_manager_test.dart 00:11 +202: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/websockets/ws_manager_test.dart: WsManager (setUpAll) 00:11 +202: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/websockets/ws_manager_test.dart: WsManager initialize returns early when client is not null 00:11 +203: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/websockets/ws_manager_test.dart: WsManager initialize returns early when client is not null 00:11 +203: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/websockets/ws_manager_test.dart: WsManager newMessagesStream returns null when channel is null 00:11 +204: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/websockets/ws_manager_test.dart: WsManager newMessagesStream returns null when channel is null 00:11 +204: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/websockets/ws_manager_test.dart: WsManager newNotificationsStream returns null when channel is null 00:11 +205: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/websockets/ws_manager_test.dart: WsManager newNotificationsStream returns null when channel is null 00:11 +205: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/websockets/ws_manager_test.dart: WsManager directChatStream returns null when channel is null 00:11 +206: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/websockets/ws_manager_test.dart: WsManager directChatStream returns null when channel is null 00:11 +206: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/websockets/ws_manager_test.dart: WsManager clientTypingStream returns null when channel is null 00:11 +207: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/websockets/ws_manager_test.dart: WsManager clientTypingStream returns null when channel is null 00:11 +207: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/websockets/ws_manager_test.dart: WsManager subscribeDirectChatChannel does nothing when channel is null 00:11 +208: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/websockets/ws_manager_test.dart: WsManager subscribeDirectChatChannel does nothing when channel is null 00:11 +208: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/websockets/ws_manager_test.dart: WsManager unsubscribeDirectChatChannel does nothing when channel is null 00:11 +209: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/websockets/ws_manager_test.dart: WsManager unsubscribeDirectChatChannel does nothing when channel is null 00:11 +209: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/websockets/ws_manager_test.dart: WsManager clear sets client and userId to null 00:11 +210: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/websockets/ws_manager_test.dart: WsManager clear sets client and userId to null 00:11 +210: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/websockets/ws_manager_test.dart: WsManager (tearDownAll) 00:11 +210: loading /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/image_constant_test.dart 00:11 +210: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/image_constant_test.dart: ImageConstant imagePath is correct 00:11 +211: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/image_constant_test.dart: ImageConstant imagePath is correct 00:11 +211: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/image_constant_test.dart: ImageConstant common images are defined 00:11 +212: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/image_constant_test.dart: ImageConstant common images are defined 00:11 +212: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/image_constant_test.dart: ImageConstant settings images are defined 00:11 +213: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/image_constant_test.dart: ImageConstant settings images are defined 00:11 +213: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/image_constant_test.dart: ImageConstant blog images are defined 00:11 +214: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/image_constant_test.dart: ImageConstant blog images are defined 00:11 +214: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/image_constant_test.dart: ImageConstant all image paths contain imagePath prefix or assets path 00:11 +215: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/image_constant_test.dart: ImageConstant all image paths contain imagePath prefix or assets path 00:12 +215: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/image_constant_test.dart: ImageConstant all image paths contain imagePath prefix or assets path 00:12 +215: loading /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/localization_utils_test.dart 00:12 +215: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/localization_utils_test.dart: LocalizationUtils getLocalizedMeasurementUnits returns grams for Portion.grams 00:12 +216: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/localization_utils_test.dart: LocalizationUtils getLocalizedMeasurementUnits returns grams for Portion.grams 00:12 +216: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/localization_utils_test.dart: ... getLocalizedMeasurementUnits returns milliliters for Portion.milliliters 00:12 +217: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/localization_utils_test.dart: ... getLocalizedMeasurementUnits returns milliliters for Portion.milliliters 00:12 +217: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/localization_utils_test.dart: LocalizationUtils getLocalizedMeasurementUnits returns pcs for Portion.pcs 00:12 +218: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/localization_utils_test.dart: LocalizationUtils getLocalizedMeasurementUnits returns pcs for Portion.pcs 00:12 +218: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/localization_utils_test.dart: LocalizationUtils getDayForm returns correct form for 1 day 00:12 +219: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/localization_utils_test.dart: LocalizationUtils getDayForm returns correct form for 1 day 00:12 +219: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/localization_utils_test.dart: LocalizationUtils getDayForm returns correct form for 2-4 days 00:12 +220: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/localization_utils_test.dart: LocalizationUtils getDayForm returns correct form for 2-4 days 00:12 +220: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/localization_utils_test.dart: LocalizationUtils getDayForm returns correct form for 5+ days 00:12 +221: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/localization_utils_test.dart: LocalizationUtils getDayForm returns correct form for 5+ days 00:12 +221: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/localization_utils_test.dart: LocalizationUtils getDayForm returns correct form for 11-14 days 00:12 +222: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/localization_utils_test.dart: LocalizationUtils getDayForm returns correct form for 11-14 days 00:12 +222: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/localization_utils_test.dart: LocalizationUtils getCommentForm returns correct form for 1 comment 00:12 +223: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/localization_utils_test.dart: LocalizationUtils getCommentForm returns correct form for 1 comment 00:12 +223: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/localization_utils_test.dart: LocalizationUtils getCommentForm returns correct form for 2-4 comments 00:12 +224: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/localization_utils_test.dart: LocalizationUtils getCommentForm returns correct form for 2-4 comments 00:12 +224: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/localization_utils_test.dart: LocalizationUtils getCommentForm returns correct form for 5+ comments 00:12 +225: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/localization_utils_test.dart: LocalizationUtils getCommentForm returns correct form for 5+ comments 00:12 +225: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/localization_utils_test.dart: LocalizationUtils getCommentForm returns correct form for 11-14 comments 00:12 +226: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/localization_utils_test.dart: LocalizationUtils getCommentForm returns correct form for 11-14 comments 00:12 +226: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/localization_utils_test.dart: LocalizationUtils getMinuteForm returns correct form for 1 minute 00:12 +227: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/localization_utils_test.dart: LocalizationUtils getMinuteForm returns correct form for 1 minute 00:12 +227: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/localization_utils_test.dart: LocalizationUtils getMinuteForm returns correct form for 2-4 minutes 00:12 +228: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/localization_utils_test.dart: LocalizationUtils getMinuteForm returns correct form for 2-4 minutes 00:12 +228: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/localization_utils_test.dart: LocalizationUtils getMinuteForm returns correct form for 5+ minutes 00:12 +229: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/localization_utils_test.dart: LocalizationUtils getMinuteForm returns correct form for 5+ minutes 00:12 +229: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/localization_utils_test.dart: LocalizationUtils getMinuteForm returns correct form for 11 minutes 00:12 +230: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/localization_utils_test.dart: LocalizationUtils getMinuteForm returns correct form for 11 minutes 00:12 +230: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/localization_utils_test.dart: LocalizationUtils getHourForm returns correct form for 1 hour 00:12 +231: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/localization_utils_test.dart: LocalizationUtils getHourForm returns correct form for 1 hour 00:12 +231: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/localization_utils_test.dart: LocalizationUtils getHourForm returns correct form for 2-4 hours 00:12 +232: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/localization_utils_test.dart: LocalizationUtils getHourForm returns correct form for 2-4 hours 00:12 +232: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/localization_utils_test.dart: LocalizationUtils getHourForm returns correct form for 5+ hours 00:12 +233: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/localization_utils_test.dart: LocalizationUtils getHourForm returns correct form for 5+ hours 00:12 +233: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/localization_utils_test.dart: LocalizationUtils getHourForm returns correct form for 11 hours 00:12 +234: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/localization_utils_test.dart: LocalizationUtils getHourForm returns correct form for 11 hours 00:12 +234: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/localization_utils_test.dart: LocalizationUtils getStepForm returns correct form for 1 step 00:12 +235: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/localization_utils_test.dart: LocalizationUtils getStepForm returns correct form for 1 step 00:12 +235: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/localization_utils_test.dart: LocalizationUtils getStepForm returns correct form for 2-4 steps 00:12 +236: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/localization_utils_test.dart: LocalizationUtils getStepForm returns correct form for 2-4 steps 00:12 +236: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/localization_utils_test.dart: LocalizationUtils getStepForm returns correct form for 5+ steps 00:12 +237: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/localization_utils_test.dart: LocalizationUtils getStepForm returns correct form for 5+ steps 00:12 +237: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/localization_utils_test.dart: LocalizationUtils getStepForm returns correct form for 11 steps 00:12 +238: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/localization_utils_test.dart: LocalizationUtils getStepForm returns correct form for 11 steps 00:13 +238: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/localization_utils_test.dart: LocalizationUtils getStepForm returns correct form for 11 steps 00:13 +238: loading /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/pagination_utils_test.dart 00:13 +238: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/pagination_utils_test.dart: PaginationUtils loadNewContent function exists 00:13 +239: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/pagination_utils_test.dart: PaginationUtils loadNewContent function exists 00:13 +239: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/pagination_utils_test.dart: PaginationUtils loadNewContent can be called with ScrollNotification 00:13 +240: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/pagination_utils_test.dart: PaginationUtils loadNewContent can be called with ScrollNotification 00:13 +241: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/pagination_utils_test.dart: PaginationUtils loadNewContent can be called with ScrollNotification 00:13 +242: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/pagination_utils_test.dart: PaginationUtils loadNewContent can be called with ScrollNotification 00:13 +243: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/pagination_utils_test.dart: PaginationUtils loadNewContent can be called with ScrollNotification 00:13 +244: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/pagination_utils_test.dart: PaginationUtils loadNewContent can be called with ScrollNotification 00:13 +245: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/pagination_utils_test.dart: PaginationUtils loadNewContent can be called with ScrollNotification 00:13 +246: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/pagination_utils_test.dart: PaginationUtils loadNewContent can be called with ScrollNotification 00:13 +247: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/pagination_utils_test.dart: PaginationUtils loadNewContent can be called with ScrollNotification 00:13 +247: loading /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/string_extentions_test.dart 00:13 +247: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/string_extentions_test.dart: String Extensions capitalize capitalizes first letter and lowercases rest 00:13 +248: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/string_extentions_test.dart: String Extensions capitalize capitalizes first letter and lowercases rest 00:13 +248: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/string_extentions_test.dart: String Extensions capitalize handles empty string 00:13 +249: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/string_extentions_test.dart: String Extensions capitalize handles empty string 00:13 +249: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/string_extentions_test.dart: String Extensions capitalize handles single character 00:13 +250: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/string_extentions_test.dart: String Extensions capitalize handles single character 00:13 +250: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/string_extentions_test.dart: String Extensions capitalize handles already capitalized string 00:13 +251: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/string_extentions_test.dart: String Extensions capitalize handles already capitalized string 00:13 +251: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/string_extentions_test.dart: String Extensions parseGender parses male gender 00:13 +252: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/string_extentions_test.dart: String Extensions parseGender parses male gender 00:13 +252: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/string_extentions_test.dart: String Extensions parseGender parses female gender 00:13 +253: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/string_extentions_test.dart: String Extensions parseGender parses female gender 00:13 +253: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/string_extentions_test.dart: String Extensions parseGender returns original string for unknown gender 00:13 +254: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/string_extentions_test.dart: String Extensions parseGender returns original string for unknown gender 00:13 +254: loading /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/date_time_utils_test.dart 00:13 +254: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/date_time_utils_test.dart: DateTimeUtils formatBuildDate formats date string correctly 00:13 +255: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/date_time_utils_test.dart: DateTimeUtils formatBuildDate formats date string correctly 00:13 +255: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/date_time_utils_test.dart: DateTimeUtils formatBuildDate returns empty string for empty input 00:13 +256: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/date_time_utils_test.dart: DateTimeUtils formatBuildDate returns empty string for empty input 00:13 +256: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/date_time_utils_test.dart: DateTimeUtils formatBuildDate handles invalid date format 00:13 +257: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/date_time_utils_test.dart: DateTimeUtils formatBuildDate handles invalid date format 00:13 +257: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/date_time_utils_test.dart: DateTimeUtils formatBuildDate handles date without time 00:13 +258: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/date_time_utils_test.dart: DateTimeUtils formatBuildDate handles date without time 00:13 +258: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/date_time_utils_test.dart: DateTimeUtils DateTimeExtension format formats date with default pattern 00:13 +259: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/date_time_utils_test.dart: DateTimeUtils DateTimeExtension format formats date with default pattern 00:13 +259: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/date_time_utils_test.dart: DateTimeUtils DateTimeExtension format formats date with custom pattern 00:13 +260: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/date_time_utils_test.dart: DateTimeUtils DateTimeExtension format formats date with custom pattern 00:13 +260: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/date_time_utils_test.dart: DateTimeUtils DateTimeExtension format formats UTC date to local 00:13 +261: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/date_time_utils_test.dart: DateTimeUtils DateTimeExtension format formats UTC date to local 00:13 +261: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/date_time_utils_test.dart: DateTimeUtils DateTimeExtension isYesterday returns true for yesterday 00:13 +262: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/date_time_utils_test.dart: DateTimeUtils DateTimeExtension isYesterday returns true for yesterday 00:13 +262: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/date_time_utils_test.dart: DateTimeUtils DateTimeExtension isYesterday returns false for today 00:13 +263: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/date_time_utils_test.dart: DateTimeUtils DateTimeExtension isYesterday returns false for today 00:13 +263: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/date_time_utils_test.dart: DateTimeUtils DateTimeExtension isYesterday returns false for two days ago 00:13 +264: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/date_time_utils_test.dart: DateTimeUtils DateTimeExtension isYesterday returns false for two days ago 00:13 +264: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/date_time_utils_test.dart: DateTimeUtils DateTimeExtension isToday returns true for today 00:13 +265: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/date_time_utils_test.dart: DateTimeUtils DateTimeExtension isToday returns true for today 00:13 +265: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/date_time_utils_test.dart: DateTimeUtils DateTimeExtension isToday returns false for yesterday 00:13 +266: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/date_time_utils_test.dart: DateTimeUtils DateTimeExtension isToday returns false for yesterday 00:13 +266: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/date_time_utils_test.dart: DateTimeUtils DateTimeExtension isToday returns false for tomorrow 00:13 +267: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/date_time_utils_test.dart: DateTimeUtils DateTimeExtension isToday returns false for tomorrow 00:13 +267: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/date_time_utils_test.dart: ... DateTimeExtension toISO8601WithTimeZoneOffset formats date with timezone offset 00:13 +268: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/date_time_utils_test.dart: ... DateTimeExtension toISO8601WithTimeZoneOffset formats date with timezone offset 00:13 +268: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/date_time_utils_test.dart: DateTimeUtils calculateDifference returns "Сегодня" for today 00:13 +269: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/date_time_utils_test.dart: DateTimeUtils calculateDifference returns "Сегодня" for today 00:13 +269: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/date_time_utils_test.dart: DateTimeUtils calculateDifference returns "Вчера" for yesterday 00:13 +270: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/date_time_utils_test.dart: DateTimeUtils calculateDifference returns "Вчера" for yesterday 00:13 +270: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/date_time_utils_test.dart: DateTimeUtils calculateDifference returns formatted date for older dates 00:13 +271: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/date_time_utils_test.dart: DateTimeUtils calculateDifference returns formatted date for older dates 00:13 +271: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/date_time_utils_test.dart: DateTimeUtils calculateDifference uses custom format when provided 00:13 +272: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/date_time_utils_test.dart: DateTimeUtils calculateDifference uses custom format when provided 00:14 +272: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/utils/date_time_utils_test.dart: DateTimeUtils calculateDifference uses custom format when provided 00:14 +272: loading /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/app_router_test.dart 00:14 +272: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/app_router_test.dart: AppRouter navigatorKey инициализирован 00:14 +273: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/app_router_test.dart: AppRouter navigatorKey инициализирован 00:14 +273: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/app_router_test.dart: AppRouter appRouter инициализирован 00:14 +274: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/app_router_test.dart: AppRouter appRouter инициализирован 00:14 +274: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/app_router_test.dart: AppRouter appRouter инициализирован с правильной конфигурацией 00:14 +275: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/app_router_test.dart: AppRouter appRouter инициализирован с правильной конфигурацией 00:14 +275: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/app_router_test.dart: AppRouter appRouter имеет правильный navigatorKey 00:14 +276: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/app_router_test.dart: AppRouter appRouter имеет правильный navigatorKey 00:14 +276: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/app_router_test.dart: AppRouter navigateToRegisterViaSocial - метод существует 00:14 +277: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/app_router_test.dart: AppRouter navigateToRegisterViaSocial - метод существует 00:14 +277: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/app_router_test.dart: AppRouter navigateToRegisterViaSocial - принимает правильные параметры 00:14 +278: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/app_router_test.dart: AppRouter navigateToRegisterViaSocial - принимает правильные параметры 00:14 +278: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/app_router_test.dart: AppRouter appRouter содержит маршруты 00:14 +279: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/app_router_test.dart: AppRouter appRouter содержит маршруты 00:14 +279: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/app_router_test.dart: AppRouter appRouter имеет правильную конфигурацию 00:14 +280: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/app_router_test.dart: AppRouter appRouter имеет правильную конфигурацию 00:14 +280: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/app_router_test.dart: AppRouter appRouter имеет redirect функцию 00:14 +281: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/app_router_test.dart: AppRouter appRouter имеет redirect функцию 00:14 +281: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/app_router_test.dart: AppRouter navigateToRegisterViaSocial вызывает pushNamed с правильными параметрами 00:14 +282: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/app_router_test.dart: AppRouter navigateToRegisterViaSocial вызывает pushNamed с правильными параметрами 00:14 +282: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/app_router_test.dart: AppRouter appRouter configuration routes содержит маршруты разных типов 00:14 +283: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/app_router_test.dart: AppRouter appRouter configuration routes содержит маршруты разных типов 00:14 +283: loading /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/models/pagination_model_test.dart 00:14 +283: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/models/pagination_model_test.dart: Links creates Links with all parameters 00:15 +284: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/models/pagination_model_test.dart: Links creates Links with all parameters 00:15 +284: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/models/pagination_model_test.dart: Links creates Links with null prev and next 00:15 +285: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/models/pagination_model_test.dart: Links creates Links with null prev and next 00:15 +285: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/models/pagination_model_test.dart: Link creates Link with all parameters 00:15 +286: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/models/pagination_model_test.dart: Link creates Link with all parameters 00:15 +286: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/models/pagination_model_test.dart: Link creates Link with null values 00:15 +287: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/models/pagination_model_test.dart: Link creates Link with null values 00:15 +287: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/models/pagination_model_test.dart: Meta creates Meta with all parameters 00:15 +288: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/models/pagination_model_test.dart: Meta creates Meta with all parameters 00:15 +288: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/models/pagination_model_test.dart: Meta creates Meta with null optional values 00:15 +289: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/models/pagination_model_test.dart: Meta creates Meta with null optional values 00:15 +289: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/models/pagination_model_test.dart: Meta creates Meta with links list 00:15 +290: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/models/pagination_model_test.dart: Meta creates Meta with links list 00:15 +290: loading /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/const_test.dart 00:15 +290: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/const_test.dart: Constants durationValues contains expected values 00:15 +291: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/const_test.dart: Constants durationValues contains expected values 00:15 +291: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/const_test.dart: Constants dietTypeValues contains expected values 00:15 +292: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/const_test.dart: Constants dietTypeValues contains expected values 00:15 +292: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/const_test.dart: Constants planTypeValues contains expected values 00:15 +293: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/const_test.dart: Constants planTypeValues contains expected values 00:15 +293: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/const_test.dart: Constants channel names are defined 00:15 +294: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/const_test.dart: Constants channel names are defined 00:15 +294: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/const_test.dart: Constants thresholdForPagination has correct value 00:15 +295: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/const_test.dart: Constants thresholdForPagination has correct value 00:15 +295: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/const_test.dart: Constants hidePlans is a boolean 00:15 +296: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/const_test.dart: Constants hidePlans is a boolean 00:15 +296: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/const_test.dart: Constants buildDate is a string 00:15 +297: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/const_test.dart: Constants buildDate is a string 00:16 +297: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/const_test.dart: Constants buildDate is a string 00:16 +297: loading /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/permission_handler/permission_handler_test.dart 00:16 +297: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/permission_handler/permission_handler_test.dart: PermissionHandler calls onGranted when permission is granted on Android 00:17 +297: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/permission_handler/permission_handler_test.dart: PermissionHandler calls onGranted when permission is granted on Android 00:17 +298: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/permission_handler/permission_handler_test.dart: PermissionHandler calls onGranted when permission is granted on Android 00:17 +298: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/permission_handler/permission_handler_test.dart: PermissionHandler shows error dialog when permission is denied 00:17 +299: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/permission_handler/permission_handler_test.dart: PermissionHandler shows error dialog when permission is denied 00:17 +299: loading /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart 00:17 +299: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles headlineSmallMulish returns TextStyle 00:18 +299: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles headlineSmallMulish returns TextStyle 00:18 +300: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles headlineSmallMulish returns TextStyle 00:18 +300: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles titleMediumSegoeUI returns TextStyle 00:18 +301: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles titleMediumSegoeUI returns TextStyle 00:18 +301: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles bodyMediumBlack900 returns TextStyle 00:18 +302: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles bodyMediumBlack900 returns TextStyle 00:18 +302: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles bodyMediumBlack90013 returns TextStyle 00:18 +303: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles bodyMediumBlack90013 returns TextStyle 00:18 +303: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles bodyMediumBluegray800 returns TextStyle 00:18 +304: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles bodyMediumBluegray800 returns TextStyle 00:18 +304: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles bodyMediumBluegray800_1 returns TextStyle 00:18 +305: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles bodyMediumBluegray800_1 returns TextStyle 00:18 +305: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles bodyMediumBluegray800_2 returns TextStyle 00:18 +306: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles bodyMediumBluegray800_2 returns TextStyle 00:18 +306: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles bodyMediumWhiteA700 returns TextStyle 00:18 +307: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles bodyMediumWhiteA700 returns TextStyle 00:18 +307: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles bodySmallMulishBlack900 returns TextStyle 00:18 +308: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles bodySmallMulishBlack900 returns TextStyle 00:18 +308: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles bodySmallMulishBluegray800 returns TextStyle 00:18 +309: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles bodySmallMulishBluegray800 returns TextStyle 00:18 +309: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles bodySmallMulishBluegray80012 returns TextStyle 00:18 +310: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles bodySmallMulishBluegray80012 returns TextStyle 00:18 +310: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles bodySmallMulishBluegray80012_1 returns TextStyle 00:18 +311: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles bodySmallMulishBluegray80012_1 returns TextStyle 00:18 +311: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles bodySmallMulishBluegray900 returns TextStyle 00:18 +312: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles bodySmallMulishBluegray900 returns TextStyle 00:18 +312: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles bodySmallMulishGray600 returns TextStyle 00:18 +313: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles bodySmallMulishGray600 returns TextStyle 00:18 +313: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles bodySmallMulishPrimary returns TextStyle 00:18 +314: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles bodySmallMulishPrimary returns TextStyle 00:18 +314: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles bodySmallSegoeUIBlack900 returns TextStyle 00:18 +315: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles bodySmallSegoeUIBlack900 returns TextStyle 00:18 +315: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles labelLargeGray60003 returns TextStyle 00:18 +316: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles labelLargeGray60003 returns TextStyle 00:18 +316: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles labelLargeWhiteA700 returns TextStyle 00:18 +317: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles labelLargeWhiteA700 returns TextStyle 00:18 +317: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles titleMedium16 returns TextStyle 00:18 +318: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles titleMedium16 returns TextStyle 00:18 +318: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: TextStyles textMMedium returns TextStyle 00:18 +319: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: TextStyles textMMedium returns TextStyle 00:18 +319: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: TextStyles textSMedium returns TextStyle 00:18 +320: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: TextStyles textSMedium returns TextStyle 00:18 +320: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: TextStyles headingXLSemibold returns TextStyle 00:18 +321: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: TextStyles headingXLSemibold returns TextStyle 00:18 +321: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: TextStyles headingFS40FW600 returns TextStyle 00:18 +322: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: TextStyles headingFS40FW600 returns TextStyle 00:18 +322: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: TextStyles textLSemibold returns TextStyle 00:18 +323: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: TextStyles textLSemibold returns TextStyle 00:18 +323: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: TextStyles textLMedium returns TextStyle 00:18 +324: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: TextStyles textLMedium returns TextStyle 00:18 +324: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: TextStyles textMSemibold returns TextStyle 00:18 +325: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: TextStyles textMSemibold returns TextStyle 00:18 +325: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: TextStyles textMMediumAuto returns TextStyle 00:18 +326: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: TextStyles textMMediumAuto returns TextStyle 00:18 +326: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: TextStyles textSSemibold returns TextStyle 00:18 +327: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: TextStyles textSSemibold returns TextStyle 00:18 +327: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: TextStyles textFS13FW400 returns TextStyle 00:18 +328: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: TextStyles textFS13FW400 returns TextStyle 00:18 +328: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: TextStyles textXSSemibold returns TextStyle 00:18 +329: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: TextStyles textXSSemibold returns TextStyle 00:18 +329: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: TextStyles textFS10FW500 returns TextStyle 00:18 +330: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: TextStyles textFS10FW500 returns TextStyle 00:18 +330: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: TextStyles textFS24FW700 returns TextStyle 00:18 +331: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: TextStyles textFS24FW700 returns TextStyle 00:18 +331: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: TextStyles textFS22FW600 returns TextStyle 00:18 +332: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: TextStyles textFS22FW600 returns TextStyle 00:18 +332: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles additional styles titleMediumBlack returns TextStyle 00:18 +333: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles additional styles titleMediumBlack returns TextStyle 00:18 +333: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles additional styles titleMediumBlack900 returns TextStyle 00:18 +334: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles additional styles titleMediumBlack900 returns TextStyle 00:18 +334: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles additional styles titleMediumSemiBold returns TextStyle 00:18 +335: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles additional styles titleMediumSemiBold returns TextStyle 00:18 +335: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles additional styles titleMediumWhiteA700 returns TextStyle 00:18 +336: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles additional styles titleMediumWhiteA700 returns TextStyle 00:18 +336: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles additional styles titleSmallBlue800 returns TextStyle 00:18 +337: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles additional styles titleSmallBlue800 returns TextStyle 00:18 +337: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles additional styles titleSmallBluegray800 returns TextStyle 00:18 +338: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles additional styles titleSmallBluegray800 returns TextStyle 00:18 +338: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles additional styles titleSmallBluegray800Bold returns TextStyle 00:18 +339: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles additional styles titleSmallBluegray800Bold returns TextStyle 00:18 +339: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles additional styles titleSmallBluegray800ExtraBold returns TextStyle 00:18 +340: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles additional styles titleSmallBluegray800ExtraBold returns TextStyle 00:18 +340: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles additional styles titleSmallBold returns TextStyle 00:18 +341: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles additional styles titleSmallBold returns TextStyle 00:18 +341: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles additional styles titleSmallGreenA70001 returns TextStyle 00:18 +342: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles additional styles titleSmallGreenA70001 returns TextStyle 00:18 +342: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles additional styles titleSmallGreenA70001ExtraBold returns TextStyle 00:18 +343: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles additional styles titleSmallGreenA70001ExtraBold returns TextStyle 00:18 +343: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles additional styles titleSmallLightgreen300 returns TextStyle 00:18 +344: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles additional styles titleSmallLightgreen300 returns TextStyle 00:18 +344: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles additional styles titleSmallRed200 returns TextStyle 00:18 +345: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles additional styles titleSmallRed200 returns TextStyle 00:18 +345: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles additional styles titleSmallSecondaryContainer returns TextStyle 00:18 +346: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles additional styles titleSmallSecondaryContainer returns TextStyle 00:18 +346: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles additional styles titleSmallSecondaryContainer_1 returns TextStyle 00:18 +347: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles additional styles titleSmallSecondaryContainer_1 returns TextStyle 00:18 +347: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles additional styles titleSmallSegoeUIOnPrimary returns TextStyle 00:18 +348: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/custom_text_style_test.dart: CustomTextStyles additional styles titleSmallSegoeUIOnPrimary returns TextStyle 00:18 +348: loading /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/theme_helper_test.dart 00:18 +348: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/theme_helper_test.dart: ThemeHelper themeColor returns PrimaryColors 00:18 +349: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/theme_helper_test.dart: ThemeHelper themeColor returns PrimaryColors 00:18 +349: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/theme_helper_test.dart: ThemeHelper themeData returns ThemeData 00:19 +349: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/theme_helper_test.dart: ThemeHelper themeData returns ThemeData 00:19 +350: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/theme_helper_test.dart: ThemeHelper themeData returns ThemeData 00:19 +350: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/theme_helper_test.dart: ThemeHelper themeData has correct colorScheme 00:19 +351: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/theme_helper_test.dart: ThemeHelper themeData has correct colorScheme 00:19 +351: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/theme_helper_test.dart: ThemeHelper themeData has scaffoldBackgroundColor 00:19 +352: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/theme_helper_test.dart: ThemeHelper themeData has scaffoldBackgroundColor 00:19 +352: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/theme_helper_test.dart: ThemeHelper themeData has appBarTheme 00:19 +353: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/theme_helper_test.dart: ThemeHelper themeData has appBarTheme 00:19 +353: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/theme_helper_test.dart: ThemeHelper themeData has outlinedButtonTheme 00:19 +354: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/theme_helper_test.dart: ThemeHelper themeData has outlinedButtonTheme 00:19 +354: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/theme_helper_test.dart: PrimaryColors has all color getters 00:19 +355: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/theme_helper_test.dart: PrimaryColors has all color getters 00:19 +355: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/theme_helper_test.dart: ColorSchemes primaryColorScheme is defined 00:19 +356: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/theme_helper_test.dart: ColorSchemes primaryColorScheme is defined 00:19 +356: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/theme_helper_test.dart: ColorSchemes primaryColorScheme has primary color 00:19 +357: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/theme_helper_test.dart: ColorSchemes primaryColorScheme has primary color 00:19 +357: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/theme_helper_test.dart: ColorSchemes primaryColorScheme has onPrimary color 00:19 +358: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/theme_helper_test.dart: ColorSchemes primaryColorScheme has onPrimary color 00:19 +358: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/theme_helper_test.dart: ColorSchemes primaryColorScheme has onPrimaryContainer color 00:19 +359: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/theme_helper_test.dart: ColorSchemes primaryColorScheme has onPrimaryContainer color 00:19 +359: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/theme_helper_test.dart: Global theme getters appTheme returns PrimaryColors 00:19 +360: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/theme_helper_test.dart: Global theme getters appTheme returns PrimaryColors 00:19 +360: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/theme_helper_test.dart: Global theme getters theme returns ThemeData 00:19 +361: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/theme_helper_test.dart: Global theme getters theme returns ThemeData 00:19 +361: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/theme_helper_test.dart: ThemeHelper edge cases _getThemeColors throws exception for unsupported theme 00:19 +362: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/theme_helper_test.dart: ThemeHelper edge cases _getThemeColors throws exception for unsupported theme 00:19 +362: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/theme_helper_test.dart: ThemeHelper edge cases _getThemeData throws exception for unsupported theme 00:19 +363: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/theme_helper_test.dart: ThemeHelper edge cases _getThemeData throws exception for unsupported theme 00:19 +363: loading /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart 00:19 +363: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: AppDecoration fillOnPrimary returns correct BoxDecoration 00:20 +363: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: AppDecoration fillOnPrimary returns correct BoxDecoration 00:20 +364: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: AppDecoration fillOnPrimary returns correct BoxDecoration 00:20 +364: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: AppDecoration outlineBlueA returns correct BoxDecoration with border 00:20 +365: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: AppDecoration outlineBlueA returns correct BoxDecoration with border 00:20 +365: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: AppDecoration outlinePrimary returns correct BoxDecoration with border 00:20 +366: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: AppDecoration outlinePrimary returns correct BoxDecoration with border 00:20 +366: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: AppDecoration fillDeepOrange returns correct BoxDecoration 00:20 +367: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: AppDecoration fillDeepOrange returns correct BoxDecoration 00:20 +367: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: AppDecoration fillGray returns correct BoxDecoration 00:20 +368: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: AppDecoration fillGray returns correct BoxDecoration 00:20 +368: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: AppDecoration fillGray100 returns correct BoxDecoration 00:20 +369: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: AppDecoration fillGray100 returns correct BoxDecoration 00:20 +369: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: AppDecoration fillGray60002 returns correct BoxDecoration 00:20 +370: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: AppDecoration fillGray60002 returns correct BoxDecoration 00:20 +370: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: AppDecoration fillGreenA returns correct BoxDecoration 00:20 +371: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: AppDecoration fillGreenA returns correct BoxDecoration 00:20 +371: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: AppDecoration fillPrimary returns correct BoxDecoration 00:20 +372: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: AppDecoration fillPrimary returns correct BoxDecoration 00:20 +372: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: AppDecoration fillWhiteA returns correct BoxDecoration 00:20 +373: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: AppDecoration fillWhiteA returns correct BoxDecoration 00:20 +373: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: AppDecoration outlineBlueGray returns correct BoxDecoration with border 00:20 +374: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: AppDecoration outlineBlueGray returns correct BoxDecoration with border 00:20 +374: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: BorderRadiusStyle circleBorder8 returns correct BorderRadius 00:20 +375: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: BorderRadiusStyle circleBorder8 returns correct BorderRadius 00:20 +375: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: BorderRadiusStyle roundedBorder1 returns correct BorderRadius 00:20 +376: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: BorderRadiusStyle roundedBorder1 returns correct BorderRadius 00:20 +376: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: BorderRadiusStyle outlineBlack returns correct BoxDecoration 00:20 +377: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: BorderRadiusStyle outlineBlack returns correct BoxDecoration 00:20 +377: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: BorderRadiusStyle outlineBlueGray returns correct BoxDecoration with border 00:20 +378: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: BorderRadiusStyle outlineBlueGray returns correct BoxDecoration with border 00:20 +378: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: BorderRadiusStyle circleBorder17 returns correct BorderRadius 00:20 +379: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: BorderRadiusStyle circleBorder17 returns correct BorderRadius 00:20 +379: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: BorderRadiusStyle circleBorder20 returns correct BorderRadius 00:20 +380: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: BorderRadiusStyle circleBorder20 returns correct BorderRadius 00:20 +380: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: BorderRadiusStyle circleBorder62 returns correct BorderRadius 00:20 +381: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: BorderRadiusStyle circleBorder62 returns correct BorderRadius 00:20 +381: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: BorderRadiusStyle customBorderBL8 returns correct BorderRadius 00:20 +382: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: BorderRadiusStyle customBorderBL8 returns correct BorderRadius 00:20 +382: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: BorderRadiusStyle customBorderTL5 returns correct BorderRadius 00:20 +383: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: BorderRadiusStyle customBorderTL5 returns correct BorderRadius 00:20 +383: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: BorderRadiusStyle customBorderTL51 returns correct BorderRadius 00:20 +384: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: BorderRadiusStyle customBorderTL51 returns correct BorderRadius 00:20 +384: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: BorderRadiusStyle roundedBorder12 returns correct BorderRadius 00:20 +385: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: BorderRadiusStyle roundedBorder12 returns correct BorderRadius 00:20 +385: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: BorderRadiusStyle roundedBorder5 returns correct BorderRadius 00:20 +386: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: BorderRadiusStyle roundedBorder5 returns correct BorderRadius 00:20 +386: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: BorderRadiusStyle roundedBorder8 returns correct BorderRadius 00:20 +387: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: BorderRadiusStyle roundedBorder8 returns correct BorderRadius 00:20 +387: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: Stroke align constants strokeAlignInside returns correct value 00:20 +388: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: Stroke align constants strokeAlignInside returns correct value 00:20 +388: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: Stroke align constants strokeAlignCenter returns correct value 00:20 +389: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: Stroke align constants strokeAlignCenter returns correct value 00:20 +389: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: Stroke align constants strokeAlignOutside returns correct value 00:20 +390: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/theme/app_decoration_test.dart: Stroke align constants strokeAlignOutside returns correct value 00:20 +390: loading /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/prefs_test.dart 00:20 +390: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/prefs_test.dart: Prefs setString stores string value 00:20 +391: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/prefs_test.dart: Prefs setString stores string value 00:20 +391: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/prefs_test.dart: Prefs getString retrieves string value 00:20 +392: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/prefs_test.dart: Prefs getString retrieves string value 00:20 +392: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/prefs_test.dart: Prefs getString returns null when key does not exist 00:20 +393: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/prefs_test.dart: Prefs getString returns null when key does not exist 00:20 +393: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/prefs_test.dart: Prefs setInt stores int value 00:20 +394: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/prefs_test.dart: Prefs setInt stores int value 00:20 +394: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/prefs_test.dart: Prefs getInt retrieves int value 00:20 +395: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/prefs_test.dart: Prefs getInt retrieves int value 00:20 +395: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/prefs_test.dart: Prefs getInt returns null when key does not exist 00:20 +396: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/prefs_test.dart: Prefs getInt returns null when key does not exist 00:20 +396: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/prefs_test.dart: Prefs setBool stores bool value 00:20 +397: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/prefs_test.dart: Prefs setBool stores bool value 00:20 +397: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/prefs_test.dart: Prefs getBool retrieves bool value 00:20 +398: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/prefs_test.dart: Prefs getBool retrieves bool value 00:20 +398: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/prefs_test.dart: Prefs getBool returns null when key does not exist 00:20 +399: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/prefs_test.dart: Prefs getBool returns null when key does not exist 00:20 +399: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/prefs_test.dart: Prefs remove deletes key 00:20 +400: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/prefs_test.dart: Prefs remove deletes key 00:20 +400: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/prefs_test.dart: Prefs clear removes all keys 00:20 +401: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/prefs_test.dart: Prefs clear removes all keys 00:21 +401: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/prefs_test.dart: Prefs clear removes all keys 00:21 +401: loading /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/other_routes_test.dart 00:21 +401: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/other_routes_test.dart: OtherRoutes createRoutes возвращает список маршрутов 00:21 +402: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/other_routes_test.dart: OtherRoutes createRoutes возвращает список маршрутов 00:21 +402: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/other_routes_test.dart: OtherRoutes createRoutes содержит маршрут welcomeScreen 00:21 +403: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/other_routes_test.dart: OtherRoutes createRoutes содержит маршрут welcomeScreen 00:21 +403: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/other_routes_test.dart: OtherRoutes createRoutes содержит маршрут updateScreen 00:21 +404: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/other_routes_test.dart: OtherRoutes createRoutes содержит маршрут updateScreen 00:21 +404: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/other_routes_test.dart: OtherRoutes createRoutes содержит маршрут calendarScreen 00:21 +405: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/other_routes_test.dart: OtherRoutes createRoutes содержит маршрут calendarScreen 00:21 +405: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/other_routes_test.dart: OtherRoutes createRoutes содержит маршрут supportScreen 00:21 +406: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/other_routes_test.dart: OtherRoutes createRoutes содержит маршрут supportScreen 00:21 +406: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/other_routes_test.dart: OtherRoutes createRoutes содержит маршрут glossaryScreen 00:21 +407: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/other_routes_test.dart: OtherRoutes createRoutes содержит маршрут glossaryScreen 00:21 +407: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/other_routes_test.dart: OtherRoutes createRoutes содержит маршрут becomeConsultantScreen 00:21 +408: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/other_routes_test.dart: OtherRoutes createRoutes содержит маршрут becomeConsultantScreen 00:21 +408: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/other_routes_test.dart: OtherRoutes createRoutes содержит маршрут createPlanScreen 00:21 +409: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/other_routes_test.dart: OtherRoutes createRoutes содержит маршрут createPlanScreen 00:21 +409: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/other_routes_test.dart: OtherRoutes createRoutes содержит маршрут с redirect функцией для welcomeScreen 00:21 +410: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/other_routes_test.dart: OtherRoutes createRoutes содержит маршрут с redirect функцией для welcomeScreen 00:21 +410: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/other_routes_test.dart: OtherRoutes createRoutes содержит маршрут с redirect функцией для updateScreen 00:21 +411: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/other_routes_test.dart: OtherRoutes createRoutes содержит маршрут с redirect функцией для updateScreen 00:21 +411: loading /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/internet_connection_cubit/internet_connection_state_test.dart 00:21 +411: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/internet_connection_cubit/internet_connection_state_test.dart: ... creates state with default isInternetAvailable value 00:21 +412: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/internet_connection_cubit/internet_connection_state_test.dart: ... creates state with default isInternetAvailable value 00:21 +412: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/internet_connection_cubit/internet_connection_state_test.dart: ... creates state with isInternetAvailable set to false 00:21 +413: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/internet_connection_cubit/internet_connection_state_test.dart: ... creates state with isInternetAvailable set to false 00:21 +413: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/internet_connection_cubit/internet_connection_state_test.dart: ... creates state with isInternetAvailable set to true 00:21 +414: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/internet_connection_cubit/internet_connection_state_test.dart: ... creates state with isInternetAvailable set to true 00:21 +414: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/internet_connection_cubit/internet_connection_state_test.dart: InternetConnectionState can be compared 00:21 +415: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/internet_connection_cubit/internet_connection_state_test.dart: InternetConnectionState can be compared 00:21 +415: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/internet_connection_cubit/internet_connection_state_test.dart: InternetConnectionState different states are not equal 00:21 +416: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/internet_connection_cubit/internet_connection_state_test.dart: InternetConnectionState different states are not equal 00:22 +416: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/internet_connection_cubit/internet_connection_state_test.dart: InternetConnectionState different states are not equal 00:22 +416: loading /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/auth_routes_test.dart 00:22 +416: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/auth_routes_test.dart: AuthRoutes createRoutes возвращает список маршрутов 00:22 +417: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/auth_routes_test.dart: AuthRoutes createRoutes возвращает список маршрутов 00:22 +417: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/auth_routes_test.dart: AuthRoutes createRoutes содержит маршрут authScreen 00:22 +418: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/auth_routes_test.dart: AuthRoutes createRoutes содержит маршрут authScreen 00:22 +418: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/auth_routes_test.dart: AuthRoutes createRoutes содержит вложенные маршруты 00:22 +419: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/auth_routes_test.dart: AuthRoutes createRoutes содержит вложенные маршруты 00:22 +419: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/auth_routes_test.dart: AuthRoutes createRoutes содержит маршрут registrationScreen 00:22 +420: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/auth_routes_test.dart: AuthRoutes createRoutes содержит маршрут registrationScreen 00:22 +420: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/auth_routes_test.dart: AuthRoutes createRoutes содержит маршрут registrationScreenV2 00:22 +421: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/auth_routes_test.dart: AuthRoutes createRoutes содержит маршрут registrationScreenV2 00:22 +421: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/auth_routes_test.dart: AuthRoutes createRoutes содержит маршрут forgetPasswordScreen 00:22 +422: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/auth_routes_test.dart: AuthRoutes createRoutes содержит маршрут forgetPasswordScreen 00:22 +422: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/auth_routes_test.dart: AuthRoutes createRoutes содержит маршрут scanQRCodeScreen 00:22 +423: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/auth_routes_test.dart: AuthRoutes createRoutes содержит маршрут scanQRCodeScreen 00:22 +423: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/auth_routes_test.dart: AuthRoutes createRoutes содержит маршрут verifyScreen 00:22 +424: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/auth_routes_test.dart: AuthRoutes createRoutes содержит маршрут verifyScreen 00:22 +424: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/auth_routes_test.dart: AuthRoutes createRoutes содержит маршрут verifyEmailScreen 00:22 +425: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/auth_routes_test.dart: AuthRoutes createRoutes содержит маршрут verifyEmailScreen 00:22 +425: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/auth_routes_test.dart: AuthRoutes createRoutes содержит маршрут registerViaSocialScreen 00:22 +426: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/auth_routes_test.dart: AuthRoutes createRoutes содержит маршрут registerViaSocialScreen 00:22 +426: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/auth_routes_test.dart: AuthRoutes authScreen имеет redirect функцию 00:22 +427: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/auth_routes_test.dart: AuthRoutes authScreen имеет redirect функцию 00:22 +427: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/auth_routes_test.dart: AuthRoutes forgetPasswordScreen builder обрабатывает extra данные 00:22 +428: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/auth_routes_test.dart: AuthRoutes forgetPasswordScreen builder обрабатывает extra данные 00:22 +428: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/auth_routes_test.dart: AuthRoutes verifyScreen builder обрабатывает extra данные 00:22 +429: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/auth_routes_test.dart: AuthRoutes verifyScreen builder обрабатывает extra данные 00:22 +429: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/auth_routes_test.dart: AuthRoutes registerViaSocialScreen builder обрабатывает extra данные 00:22 +430: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/auth_routes_test.dart: AuthRoutes registerViaSocialScreen builder обрабатывает extra данные 00:22 +430: loading /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/internet_connection_cubit/internet_connection_cubit_test.dart 00:22 +430: ... InternetConnectionCubit начальное состояние - InternetConnectionState 00:22 +431: ... InternetConnectionCubit начальное состояние - InternetConnectionState 00:22 +431: ... InternetConnectionCubit checkConnection вызывает проверку подключения 00:22 +432: ... InternetConnectionCubit checkConnection вызывает проверку подключения 00:22 +432: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/internet_connection_cubit/internet_connection_cubit_test.dart: InternetConnectionCubit close корректно закрывает cubit 00:22 +433: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/internet_connection_cubit/internet_connection_cubit_test.dart: InternetConnectionCubit close корректно закрывает cubit 00:23 +433: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/internet_connection_cubit/internet_connection_cubit_test.dart: InternetConnectionCubit close корректно закрывает cubit 00:24 +433: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/internet_connection_cubit/internet_connection_cubit_test.dart: InternetConnectionCubit close корректно закрывает cubit 00:24 +433: loading /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/route_constants_test.dart 00:24 +433: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/route_constants_test.dart: RouteConstants has all auth routes defined 00:24 +434: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/route_constants_test.dart: RouteConstants has all auth routes defined 00:24 +434: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/route_constants_test.dart: RouteConstants has all main navigation routes defined 00:24 +435: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/route_constants_test.dart: RouteConstants has all main navigation routes defined 00:24 +435: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/route_constants_test.dart: RouteConstants has all content routes defined 00:24 +436: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/route_constants_test.dart: RouteConstants has all content routes defined 00:24 +436: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/route_constants_test.dart: RouteConstants has all profile routes defined 00:24 +437: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/route_constants_test.dart: RouteConstants has all profile routes defined 00:24 +437: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/route_constants_test.dart: RouteConstants has all settings routes defined 00:24 +438: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/route_constants_test.dart: RouteConstants has all settings routes defined 00:24 +438: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/route_constants_test.dart: RouteConstants has all consultant routes defined 00:24 +439: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/route_constants_test.dart: RouteConstants has all consultant routes defined 00:24 +439: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/route_constants_test.dart: RouteConstants has all other routes defined 00:24 +440: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/route_constants_test.dart: RouteConstants has all other routes defined 00:24 +440: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/route_constants_test.dart: RouteConstants has all finance routes defined 00:24 +441: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/route_constants_test.dart: RouteConstants has all finance routes defined 00:24 +441: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/route_constants_test.dart: RouteConstants has all content creation routes defined 00:24 +442: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/route_constants_test.dart: RouteConstants has all content creation routes defined 00:24 +442: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/route_constants_test.dart: RouteConstants has all user details routes defined 00:24 +443: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/route_constants_test.dart: RouteConstants has all user details routes defined 00:24 +443: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/route_constants_test.dart: RouteConstants has dev tools route defined 00:24 +444: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/route_constants_test.dart: RouteConstants has dev tools route defined 00:24 +444: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/route_constants_test.dart: RouteConstants has all archive routes defined 00:24 +445: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/route_constants_test.dart: RouteConstants has all archive routes defined 00:24 +445: loading /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/main_navigation_routes_test.dart 00:24 +445: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/main_navigation_routes_test.dart: MainNavigationRoutes createRoutes возвращает список маршрутов 00:24 +446: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/main_navigation_routes_test.dart: MainNavigationRoutes createRoutes возвращает список маршрутов 00:24 +446: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/main_navigation_routes_test.dart: MainNavigationRoutes createRoutes содержит ShellRoute 00:24 +447: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/main_navigation_routes_test.dart: MainNavigationRoutes createRoutes содержит ShellRoute 00:24 +447: loading /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/content_routes_test.dart 00:24 +447: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/content_routes_test.dart: ContentRoutes createRoutes возвращает список маршрутов 00:24 +448: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/content_routes_test.dart: ContentRoutes createRoutes возвращает список маршрутов 00:24 +448: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/content_routes_test.dart: ContentRoutes createRoutes содержит маршрут aboutPlanScreen 00:24 +449: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/content_routes_test.dart: ContentRoutes createRoutes содержит маршрут aboutPlanScreen 00:24 +449: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/content_routes_test.dart: ContentRoutes createRoutes содержит маршрут aboutRecipesScreen 00:24 +450: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/content_routes_test.dart: ContentRoutes createRoutes содержит маршрут aboutRecipesScreen 00:24 +450: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/content_routes_test.dart: ContentRoutes createRoutes содержит маршрут aboutDietScreen 00:24 +451: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/content_routes_test.dart: ContentRoutes createRoutes содержит маршрут aboutDietScreen 00:24 +451: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/content_routes_test.dart: ContentRoutes createRoutes содержит маршрут blogPostCreateScreen 00:24 +452: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/content_routes_test.dart: ContentRoutes createRoutes содержит маршрут blogPostCreateScreen 00:24 +452: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/content_routes_test.dart: ContentRoutes createRoutes содержит маршрут blogPostScreen 00:24 +453: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/content_routes_test.dart: ContentRoutes createRoutes содержит маршрут blogPostScreen 00:24 +453: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/content_routes_test.dart: ContentRoutes aboutPlanScreen builder обрабатывает slug из query параметров 00:24 +454: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/content_routes_test.dart: ContentRoutes aboutPlanScreen builder обрабатывает slug из query параметров 00:24 +454: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/content_routes_test.dart: ContentRoutes aboutPlanScreen builder обрабатывает extra данные 00:24 +455: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/content_routes_test.dart: ContentRoutes aboutPlanScreen builder обрабатывает extra данные 00:24 +455: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/content_routes_test.dart: ContentRoutes aboutRecipesScreen builder обрабатывает slug из query параметров 00:24 +456: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/content_routes_test.dart: ContentRoutes aboutRecipesScreen builder обрабатывает slug из query параметров 00:24 +456: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/content_routes_test.dart: ContentRoutes aboutRecipesScreen builder обрабатывает extra данные 00:24 +457: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/content_routes_test.dart: ContentRoutes aboutRecipesScreen builder обрабатывает extra данные 00:24 +457: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/content_routes_test.dart: ContentRoutes aboutDietScreen builder обрабатывает slug из query параметров 00:24 +458: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/content_routes_test.dart: ContentRoutes aboutDietScreen builder обрабатывает slug из query параметров 00:24 +458: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/content_routes_test.dart: ContentRoutes aboutDietScreen builder обрабатывает extra данные 00:24 +459: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/content_routes_test.dart: ContentRoutes aboutDietScreen builder обрабатывает extra данные 00:24 +459: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/content_routes_test.dart: ContentRoutes blogPostCreateScreen builder обрабатывает extra данные 00:24 +460: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/content_routes_test.dart: ContentRoutes blogPostCreateScreen builder обрабатывает extra данные 00:24 +460: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/content_routes_test.dart: ContentRoutes blogPostScreen builder обрабатывает id из query параметров 00:24 +461: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/content_routes_test.dart: ContentRoutes blogPostScreen builder обрабатывает id из query параметров 00:24 +461: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/content_routes_test.dart: ContentRoutes blogPostScreen builder обрабатывает extra данные 00:24 +462: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/content_routes_test.dart: ContentRoutes blogPostScreen builder обрабатывает extra данные 00:25 +462: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/content_routes_test.dart: ContentRoutes blogPostScreen builder обрабатывает extra данные 00:25 +462: loading /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/firebase_api_test.dart 00:25 +462: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/firebase_api_test.dart: FirebaseApi создается экземпляр 00:25 +463: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/firebase_api_test.dart: FirebaseApi создается экземпляр 00:25 +463: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/firebase_api_test.dart: FirebaseApi getToken возвращает Future 00:25 +464: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/firebase_api_test.dart: FirebaseApi getToken возвращает Future 00:25 +464: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/firebase_api_test.dart: FirebaseApi registerAnonymousPushToken возвращает Future 00:25 +465: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/firebase_api_test.dart: FirebaseApi registerAnonymousPushToken возвращает Future 00:25 +465: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/firebase_api_test.dart: FirebaseApi registerPushToken возвращает Future 00:25 +466: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/firebase_api_test.dart: FirebaseApi registerPushToken возвращает Future 00:25 +466: loading /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/settings_routes_test.dart 00:25 +466: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/settings_routes_test.dart: SettingsRoutes createRoutes возвращает список маршрутов 00:25 +467: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/settings_routes_test.dart: SettingsRoutes createRoutes возвращает список маршрутов 00:25 +467: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/settings_routes_test.dart: SettingsRoutes createRoutes содержит маршрут settingsScreen 00:25 +468: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/settings_routes_test.dart: SettingsRoutes createRoutes содержит маршрут settingsScreen 00:25 +468: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/settings_routes_test.dart: SettingsRoutes createRoutes содержит вложенные маршруты 00:25 +469: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/settings_routes_test.dart: SettingsRoutes createRoutes содержит вложенные маршруты 00:26 +469: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/routing/settings_routes_test.dart: SettingsRoutes createRoutes содержит вложенные маршруты 00:26 +469: loading /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/data/selection_popup_model_test.dart 00:26 +469: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/data/selection_popup_model_test.dart: SelectionPopupModel creates model with all parameters 00:26 +470: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/data/selection_popup_model_test.dart: SelectionPopupModel creates model with all parameters 00:26 +470: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/data/selection_popup_model_test.dart: SelectionPopupModel creates model with default isSelected value 00:26 +471: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/data/selection_popup_model_test.dart: SelectionPopupModel creates model with default isSelected value 00:26 +471: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/data/selection_popup_model_test.dart: SelectionPopupModel creates model with null id 00:26 +472: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/data/selection_popup_model_test.dart: SelectionPopupModel creates model with null id 00:26 +472: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/data/selection_popup_model_test.dart: SelectionPopupModel creates model with different value types 00:26 +473: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/data/selection_popup_model_test.dart: SelectionPopupModel creates model with different value types 00:26 +473: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/data/selection_popup_model_test.dart: SelectionPopupModel can be compared 00:26 +474: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/data/selection_popup_model_test.dart: SelectionPopupModel can be compared 00:27 +474: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/data/selection_popup_model_test.dart: SelectionPopupModel can be compared 00:27 +474: loading /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/views/cubit/views_cubit_test.dart 00:27 +474: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/views/cubit/views_cubit_test.dart: (setUpAll) 00:27 +474: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/views/cubit/views_cubit_test.dart: ViewsCubit начальное состояние - ViewsCubitState 00:27 +475: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/views/cubit/views_cubit_test.dart: ViewsCubit начальное состояние - ViewsCubitState 00:27 +475: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/views/cubit/views_cubit_test.dart: ViewsCubit handleVisibility - добавляет id когда visibleFraction >= 0.5 00:27 +476: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/views/cubit/views_cubit_test.dart: ViewsCubit handleVisibility - добавляет id когда visibleFraction >= 0.5 00:27 +477: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/views/cubit/views_cubit_test.dart: ViewsCubit handleVisibility - добавляет id когда visibleFraction >= 0.5 00:27 +478: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/views/cubit/views_cubit_test.dart: ViewsCubit handleVisibility - добавляет id когда visibleFraction >= 0.5 00:27 +479: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/views/cubit/views_cubit_test.dart: ViewsCubit handleVisibility - добавляет id когда visibleFraction >= 0.5 00:28 +479: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/views/cubit/views_cubit_test.dart: ViewsCubit handleVisibility - добавляет id когда visibleFraction >= 0.5 00:28 +480: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/widgets/bookmark_button_test.dart: BookmarkButton smoke renders bookmark button 00:28 +481: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/widgets/bookmark_button_test.dart: BookmarkButton smoke renders bookmark button 00:28 +482: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/views/cubit/views_cubit_test.dart: ViewsCubit handleVisibility - работает с разными типами контента 00:28 +483: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/views/cubit/views_cubit_test.dart: ViewsCubit handleVisibility - работает с разными типами контента 00:29 +483: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/views/cubit/views_cubit_test.dart: ViewsCubit handleVisibility - работает с разными типами контента 00:29 +484: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/views/cubit/views_cubit_test.dart: ViewsCubit handleVisibility - работает с разными типами контента 00:29 +485: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/views/cubit/views_cubit_test.dart: ViewsCubit handleVisibility - работает с разными типами контента 00:29 +486: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/views/cubit/views_cubit_test.dart: ViewsCubit handleVisibility - работает с разными типами контента 00:29 +487: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/widgets/item_widget_test.dart: ItemWidget smoke renders item widget 00:29 +488: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/widgets/item_widget_test.dart: ItemWidget smoke renders item widget 00:29 +488: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/views/cubit/views_cubit_test.dart: ViewsCubit close отправляет оставшиеся просмотры 00:29 +489: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/widgets/item_widget_test.dart: ItemWidget smoke renders item widget reversed 00:29 +490: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/views/cubit/views_cubit_test.dart: ViewsCubit handleVisibility - работает с diets типом контента 00:30 +490: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/views/cubit/views_cubit_test.dart: ViewsCubit handleVisibility - работает с diets типом контента 00:30 +491: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/views/cubit/views_cubit_test.dart: ViewsCubit handleVisibility - работает с diets типом контента 00:30 +491: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/views/cubit/views_cubit_test.dart: ViewsCubit handleVisibility - работает с recipes типом контента 00:31 +491: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/views/cubit/views_cubit_test.dart: ViewsCubit handleVisibility - работает с recipes типом контента 00:31 +492: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/views/cubit/views_cubit_test.dart: ViewsCubit handleVisibility - работает с recipes типом контента 00:31 +493: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/views/cubit/views_cubit_test.dart: ViewsCubit handleVisibility - работает с recipes типом контента 00:31 +494: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/views/cubit/views_cubit_test.dart: ViewsCubit handleVisibility - работает с recipes типом контента 00:31 +495: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/views/cubit/views_cubit_test.dart: ViewsCubit handleVisibility - работает с recipes типом контента 00:31 +496: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/views/cubit/views_cubit_test.dart: ViewsCubit handleVisibility - работает с recipes типом контента 00:31 +497: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/views/cubit/views_cubit_test.dart: ViewsCubit handleVisibility - работает с recipes типом контента 00:31 +498: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/views/cubit/views_cubit_test.dart: ViewsCubit handleVisibility - работает с recipes типом контента 00:31 +498: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/widgets/option_widget_test.dart: OptionWidget displays option widget with personal diet 00:31 +499: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/views/cubit/views_cubit_test.dart: ViewsCubit handleVisibility - не добавляет id если он уже отправлен 00:31 +500: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/views/cubit/views_cubit_test.dart: ViewsCubit handleVisibility - не добавляет id если он уже отправлен 00:32 +500: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/views/cubit/views_cubit_test.dart: ViewsCubit handleVisibility - не добавляет id если он уже отправлен 00:32 +501: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/views/cubit/views_cubit_test.dart: ViewsCubit handleVisibility - не добавляет id если он уже отправлен 00:32 +502: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/views/cubit/views_cubit_test.dart: ViewsCubit handleVisibility - не добавляет id если он уже отправлен 00:32 +503: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/views/cubit/views_cubit_test.dart: ViewsCubit handleVisibility - не добавляет id если он уже отправлен 00:32 +504: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/views/cubit/views_cubit_test.dart: ViewsCubit handleVisibility - не добавляет id если он уже отправлен 00:32 +505: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/views/cubit/views_cubit_test.dart: ViewsCubit handleVisibility - не добавляет id если он уже отправлен 00:33 +505: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/views/cubit/views_cubit_test.dart: ViewsCubit handleVisibility - не добавляет id если он уже отправлен 00:33 +506: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/views/cubit/views_cubit_test.dart: ViewsCubit handleVisibility - не добавляет id если он уже отправлен 00:33 +507: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/views/cubit/views_cubit_test.dart: ViewsCubit handleVisibility - не добавляет id если он уже отправлен 00:33 +508: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/views/cubit/views_cubit_test.dart: ViewsCubit handleVisibility - не добавляет id если он уже отправлен 00:34 +508: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/views/cubit/views_cubit_test.dart: ViewsCubit handleVisibility - не добавляет id если он уже отправлен 00:34 +508: /Users/mihailgorodbzaharov/flutter_projects/eatvio_app/test/core/widgets/marketing_bottom_sheet_test.dart: MarketingBottomSheet displays marketing bottom sheet -unhandled element