Merge pull request 'feat(tests)' (#718) from misha/eatvio_app:tests into main

Reviewed-on: eatvio/eatvio_app#718
This commit is contained in:
2025-10-10 11:38:03 +00:00
57 changed files with 358 additions and 231 deletions

View File

@@ -1,3 +1,5 @@
import 'package:eatvio_app/core/app_initializer.dart';
import 'package:eatvio_app/core/env_config.dart';
import 'package:eatvio_app/core/internet_connection_cubit/internet_connection_cubit.dart';
import 'package:eatvio_app/core/locator_service.dart';
import 'package:eatvio_app/core/views/cubit/views_cubit.dart';
@@ -13,8 +15,24 @@ import 'package:flutter_localizations/flutter_localizations.dart';
/// Конфигурация приложения
class AppConfig {
AppConfig({required this.buildType}){
_baseUrl = switch (buildType) {
AppBuildType.prod => EnvConfig.baseUrl,
AppBuildType.tests => EnvConfig.devBaseUrl,
AppBuildType.dev => EnvConfig.devBaseUrl,
};
}
final AppBuildType buildType;
late final String _baseUrl;
String get baseUrl => _baseUrl;
void setBaseUrl(String baseUrls) {
_baseUrl = baseUrls;
}
/// Список глобальных BlocProvider'ов
static List<BlocProvider> get globalBlocProviders => [
List<BlocProvider> get globalBlocProviders => [
BlocProvider<NewMessagesCubit>(create: (context) => sl<NewMessagesCubit>()),
BlocProvider<NotificationsCubit>(create: (context) => sl<NotificationsCubit>()..getNotifications()),
BlocProvider<FeedCubit>(create: (context) => sl<FeedCubit>()..loadFeedBlogPostList()),
@@ -25,7 +43,7 @@ class AppConfig {
];
/// Делегаты локализации
static const List<LocalizationsDelegate> localizationDelegates = [
List<LocalizationsDelegate> get localizationDelegates => [
S.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
@@ -33,5 +51,5 @@ class AppConfig {
];
/// Поддерживаемые локали
static const List<Locale> supportedLocales = [Locale.fromSubtags(languageCode: 'ru')];
List<Locale> get supportedLocales => [const Locale.fromSubtags(languageCode: 'ru')];
}

View File

@@ -1,6 +1,8 @@
import 'dart:io' as io;
import 'package:eatvio_app/core/app_config.dart';
import 'package:eatvio_app/core/env_config.dart';
import 'package:eatvio_app/core/locator_service.dart' as di;
import 'package:eatvio_app/core/locator_service.dart';
import 'package:eatvio_app/firebase_options.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
@@ -13,11 +15,11 @@ import 'package:sentry_flutter/sentry_flutter.dart';
/// Класс для инициализации приложения
class AppInitializer {
/// Инициализация всех сервисов приложения
static Future<void> initialize() async {
await _initializeSentry();
static Future<void> initialize({required AppBuildType buildType}) async {
await _initializeEnvironment();
await _initializeDependencyInjection(buildType: buildType);
await _initializeSentry();
await _initializeFirebase();
await _initializeDependencyInjection();
await _initializeDebugMode();
await _initializeAnalytics();
}
@@ -36,12 +38,14 @@ class AppInitializer {
/// Инициализация Firebase
static Future<void> _initializeFirebase() async {
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
await FirebaseMessaging.instance.requestPermission();
if (sl<AppConfig>().buildType == AppBuildType.prod) {
await FirebaseMessaging.instance.requestPermission();
}
}
/// Инициализация системы внедрения зависимостей
static Future<void> _initializeDependencyInjection() async {
await di.init();
static Future<void> _initializeDependencyInjection({required AppBuildType buildType}) async {
await di.init(buildType: buildType);
}
/// Настройка режима отладки
@@ -66,3 +70,5 @@ class AppInitializer {
}, appRunner: appRunner);
}
}
enum AppBuildType { prod, tests, dev }

View File

@@ -1,6 +1,3 @@
import 'package:eatvio_app/core/env_config.dart';
String baseUrl = EnvConfig.devBaseUrl;
bool hidePlans = const bool.fromEnvironment('HIDE_PLANS', defaultValue: false);
const buildDate = String.fromEnvironment('BUILD_DATE', defaultValue: '');

View File

@@ -0,0 +1,12 @@
import 'package:eatvio_app/core/app_config.dart';
import 'package:eatvio_app/core/app_initializer.dart';
import 'package:get_it/get_it.dart';
class AppConfigRegistration {
static final sl = GetIt.instance;
/// Регистрация AppConfig
static void registerAppConfig({required AppBuildType buildType}) {
sl.registerLazySingleton<AppConfig>(() => AppConfig(buildType: buildType));
}
}

View File

@@ -1,6 +1,6 @@
import 'package:dio/dio.dart';
import 'package:eatvio_app/core/const.dart';
import 'package:eatvio_app/core/dio/phone_verify_error_interceptor.dart';
import 'package:eatvio_app/core/app_config.dart';
import 'package:eatvio_app/core/interceptors/phone_verify_error_interceptor.dart';
import 'package:eatvio_app/core/interceptors/token_interceptor.dart';
import 'package:get_it/get_it.dart';
@@ -18,7 +18,7 @@ class ExternalRegistrations {
sl.registerLazySingleton(
() => Dio(
BaseOptions(
baseUrl: baseUrl,
baseUrl: sl<AppConfig>().baseUrl,
headers: {
'accept': 'application/json',
'content-type': 'application/json',

View File

@@ -1,5 +1,7 @@
import 'dart:developer';
import 'dart:io';
import 'package:eatvio_app/core/app_config.dart';
import 'package:eatvio_app/core/app_initializer.dart';
import 'package:eatvio_app/core/utils/toasts.dart';
import 'package:flutter/services.dart';
@@ -19,7 +21,9 @@ class FirebaseApi {
if (await _isHuaweiDevice()) {
token = await _getHuaweiPushTokenNative();
} else {
await _firebaseMessaging.requestPermission();
if ((sl<AppConfig>().buildType == AppBuildType.prod)) {
await _firebaseMessaging.requestPermission();
}
token = await _firebaseMessaging.getToken();
}
return token;

View File

@@ -1,3 +1,5 @@
import 'package:eatvio_app/core/app_initializer.dart';
import 'package:eatvio_app/core/di/app_config_registration.dart';
import 'package:eatvio_app/core/di/cubit_registrations.dart';
import 'package:eatvio_app/core/di/external_registrations.dart';
import 'package:eatvio_app/core/di/repository_registrations.dart';
@@ -9,19 +11,22 @@ import 'package:get_it/get_it.dart';
final sl = GetIt.instance;
/// Инициализация системы внедрения зависимостей
Future<void> init() async {
Future<void> init({required AppBuildType buildType}) async {
// Регистрация AppConfig
AppConfigRegistration.registerAppConfig(buildType: buildType);
// Регистрация внешних зависимостей (Dio, etc.)
ExternalRegistrations.registerAll();
// Регистрация сервисов
ServiceRegistrations.registerAll();
// Регистрация репозиториев
RepositoryRegistrations.registerAll();
// Регистрация Cubit'ов
CubitRegistrations.registerAll();
// Регистрация WebSocket менеджера
sl.registerLazySingleton<WsManager>(() => WsManager(wsService: sl()));
}
}

View File

@@ -1,5 +1,5 @@
import 'package:dio/dio.dart';
import 'package:eatvio_app/core/const.dart';
import 'package:eatvio_app/core/app_config.dart';
import 'package:eatvio_app/core/firebase_api.dart';
import 'package:eatvio_app/core/locator_service.dart';
import 'package:eatvio_app/core/routing/route_constants.dart';
@@ -76,7 +76,7 @@ class OtherRoutes {
builder: (context, state) => const UpdateScreen(),
redirect: (context, state) async {
try {
final versions = (await sl<Dio>().get('$baseUrl/api/app_versions')).data;
final versions = (await sl<Dio>().get('${sl<AppConfig>().baseUrl}/api/app_versions')).data;
PackageInfo packageInfo = await PackageInfo.fromPlatform();
String version = packageInfo.version;
Version currentVersion = Version.parse(version);

View File

@@ -3,6 +3,7 @@ import 'dart:developer';
import 'package:dart_pusher_channels/dart_pusher_channels.dart';
import 'package:easy_debounce/easy_debounce.dart';
import 'package:eatvio_app/core/app_config.dart';
import 'package:eatvio_app/core/const.dart';
import 'package:eatvio_app/core/locator_service.dart';
import 'package:eatvio_app/core/websockets/ws_service.dart';
@@ -111,7 +112,7 @@ class WsManager {
_privateNotificationChannel = client?.privateChannel(
'$newNotificationsChannelName.$userId',
authorizationDelegate: EndpointAuthorizableChannelTokenAuthorizationDelegate.forPrivateChannel(
authorizationEndpoint: Uri.parse('$baseUrl/broadcasting/auth'),
authorizationEndpoint: Uri.parse('${sl<AppConfig>().baseUrl}/broadcasting/auth'),
headers: {
'Authorization': 'Bearer $token',
},
@@ -124,7 +125,7 @@ class WsManager {
_privateMessagesChannel = client?.privateChannel(
'$newMessagesChannelName.$userId',
authorizationDelegate: EndpointAuthorizableChannelTokenAuthorizationDelegate.forPrivateChannel(
authorizationEndpoint: Uri.parse('$baseUrl/broadcasting/auth'),
authorizationEndpoint: Uri.parse('${sl<AppConfig>().baseUrl}/broadcasting/auth'),
headers: {
'Authorization': 'Bearer $token',
},
@@ -137,7 +138,7 @@ class WsManager {
_privateDirectChatChannel = client?.privateChannel(
'$directChatChannelName.$chatId',
authorizationDelegate: EndpointAuthorizableChannelTokenAuthorizationDelegate.forPrivateChannel(
authorizationEndpoint: Uri.parse('$baseUrl/broadcasting/auth'),
authorizationEndpoint: Uri.parse('${sl<AppConfig>().baseUrl}/broadcasting/auth'),
headers: {
'Authorization': 'Bearer $token',
},

View File

@@ -1,4 +1,4 @@
import 'package:eatvio_app/core/const.dart';
import 'package:eatvio_app/core/app_config.dart';
import 'package:eatvio_app/core/enums/complained_content.dart';
import 'package:eatvio_app/core/locator_service.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
@@ -86,7 +86,7 @@ class CustomPopup extends StatelessWidget {
if (isAuthor) {
addItem('assets/icons/copy2.svg', 'Скопировать ссылку', () {
Clipboard.setData(ClipboardData(text: '$baseUrl/ru/$path'));
Clipboard.setData(ClipboardData(text: '${sl<AppConfig>().baseUrl}/ru/$path'));
showInfoToast(s.link_was_copied);
});
addItem('assets/icons/share2.svg', 'Поделиться', () {
@@ -94,7 +94,7 @@ class CustomPopup extends StatelessWidget {
SharePlus.instance.share(
ShareParams(
uri: Uri.parse(
'$baseUrl/ru/$path',
'${sl<AppConfig>().baseUrl}/ru/$path',
),
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size,
),
@@ -140,7 +140,7 @@ class CustomPopup extends StatelessWidget {
});
} else {
addItem('assets/icons/copy2.svg', 'Скопировать ссылку', () {
Clipboard.setData(ClipboardData(text: '$baseUrl/ru/$path'));
Clipboard.setData(ClipboardData(text: '${sl<AppConfig>().baseUrl}/ru/$path'));
showInfoToast(s.link_was_copied);
});
addItem('assets/icons/share2.svg', 'Поделиться', () {
@@ -148,7 +148,7 @@ class CustomPopup extends StatelessWidget {
SharePlus.instance.share(
ShareParams(
uri: Uri.parse(
'$baseUrl/ru/$path',
'${sl<AppConfig>().baseUrl}/ru/$path',
),
sharePositionOrigin: box!.localToGlobal(Offset.zero) & box.size,
),