feat(tests): test coverage is 69.2

This commit is contained in:
misha
2026-01-21 09:19:41 +03:00
parent 775d4fb80f
commit f6809a06b3
16 changed files with 5274 additions and 333 deletions

View File

@@ -61,5 +61,26 @@ void main() {
expect(routes.length, greaterThan(0));
});
test('appRouter имеет правильную конфигурацию маршрутов', () {
expect(AppRouter.appRouter.configuration.routes, isNotEmpty);
});
test('navigateToRegisterViaSocial - создает правильный extra map', () {
const authResult = AuthResultEntity(accessToken: 'test_token', refreshToken: 'refresh_token', userId: '1', clientId: 'test_client_id');
const provider = 'yandex';
expect(() => AppRouter.navigateToRegisterViaSocial(authResult: authResult, provider: provider), returnsNormally);
});
test('appRouter содержит все типы маршрутов', () {
final routes = AppRouter.appRouter.configuration.routes;
// Проверяем, что есть маршруты разных типов
expect(routes, isNotEmpty);
// Проверяем, что есть хотя бы один маршрут
expect(routes.length, greaterThan(0));
});
});
}

View File

@@ -208,5 +208,223 @@ void main() {
}
}
});
test('compressFilesList - обрабатывает список с изображениями разных форматов', () async {
final jpegFile = File('test_file.jpg');
final pngFile = File('test_file.png');
final gifFile = File('test_file.gif');
final nonImageFile = File('test_file.txt');
try {
// Создаем минимальные файлы
await jpegFile.writeAsBytes([0xFF, 0xD8, 0xFF, 0xE0]);
await pngFile.writeAsBytes([0x89, 0x50, 0x4E, 0x47]);
await gifFile.writeAsBytes([0x47, 0x49, 0x46, 0x38]);
await nonImageFile.writeAsBytes([0x54, 0x45, 0x58, 0x54]);
final testFiles = [jpegFile, pngFile, gifFile, nonImageFile];
final result = await ImageCompressUtility.compressFilesList(testFiles);
expect(result, isA<List<File>>());
expect(result.length, 4);
// В тестовом окружении все файлы возвращаются как есть
expect(result[0].path, jpegFile.path);
expect(result[1].path, pngFile.path);
expect(result[2].path, gifFile.path);
expect(result[3].path, nonImageFile.path);
} finally {
for (final file in [jpegFile, pngFile, gifFile, nonImageFile]) {
if (await file.exists()) {
await file.delete();
}
}
}
});
test('compressFilesList - обрабатывает пустой список', () async {
final result = await ImageCompressUtility.compressFilesList([]);
expect(result, isA<List<File>>());
expect(result.length, 0);
});
test('compressFilesList - обрабатывает список только с не-изображениями', () async {
final txtFile = File('test_file.txt');
final pdfFile = File('test_file.pdf');
try {
await txtFile.writeAsBytes([0x54, 0x45, 0x58, 0x54]);
await pdfFile.writeAsBytes([0x25, 0x50, 0x44, 0x46]);
final testFiles = [txtFile, pdfFile];
final result = await ImageCompressUtility.compressFilesList(testFiles);
expect(result, isA<List<File>>());
expect(result.length, 2);
expect(result[0].path, txtFile.path);
expect(result[1].path, pdfFile.path);
} finally {
for (final file in [txtFile, pdfFile]) {
if (await file.exists()) {
await file.delete();
}
}
}
});
test('compressFilesList - обрабатывает файлы с разными расширениями изображений', () async {
final jpgFile = File('test_file.jpg');
final jpegFile = File('test_file.jpeg');
final pngFile = File('test_file.PNG'); // uppercase
final bmpFile = File('test_file.bmp');
final webpFile = File('test_file.webp');
try {
await jpgFile.writeAsBytes([0xFF, 0xD8]);
await jpegFile.writeAsBytes([0xFF, 0xD8]);
await pngFile.writeAsBytes([0x89, 0x50]);
await bmpFile.writeAsBytes([0x42, 0x4D]);
await webpFile.writeAsBytes([0x52, 0x49, 0x46, 0x46]);
final testFiles = [jpgFile, jpegFile, pngFile, bmpFile, webpFile];
final result = await ImageCompressUtility.compressFilesList(testFiles);
expect(result, isA<List<File>>());
expect(result.length, 5);
} finally {
for (final file in [jpgFile, jpegFile, pngFile, bmpFile, webpFile]) {
if (await file.exists()) {
await file.delete();
}
}
}
});
test('compressFile - обрабатывает PNG файл', () async {
final pngFile = File('test_file.png');
try {
// Минимальный валидный PNG
final pngBytes = [
0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, // PNG signature
0x00, 0x00, 0x00, 0x0D, // IHDR chunk length
0x49, 0x48, 0x44, 0x52, // IHDR
0x00, 0x00, 0x00, 0x01, // width
0x00, 0x00, 0x00, 0x01, // height
0x08, 0x02, 0x00, 0x00, 0x00, // bit depth, color type, etc.
0x90, 0x77, 0x53, 0xDE, // CRC
0x00, 0x00, 0x00, 0x00, // IEND chunk length
0x49, 0x45, 0x4E, 0x44, // IEND
0xAE, 0x42, 0x60, 0x82, // CRC
];
await pngFile.writeAsBytes(pngBytes);
final result = await ImageCompressUtility.compressFile(pngFile);
expect(result, isA<File>());
expect(result.path, pngFile.path);
} finally {
if (await pngFile.exists()) {
await pngFile.delete();
}
}
});
test('compressFile - обрабатывает файл с расширением в верхнем регистре', () async {
final jpgFile = File('test_file.JPG');
try {
final jpegBytes = [0xFF, 0xD8, 0xFF, 0xE0];
await jpgFile.writeAsBytes(jpegBytes);
final result = await ImageCompressUtility.compressFile(jpgFile);
expect(result, isA<File>());
expect(result.path, jpgFile.path);
} finally {
if (await jpgFile.exists()) {
await jpgFile.delete();
}
}
});
test('compressFilesList - обрабатывает смешанный список изображений и не-изображений', () async {
final jpgFile = File('test_file.jpg');
final txtFile = File('test_file.txt');
final pngFile = File('test_file.png');
try {
await jpgFile.writeAsBytes([0xFF, 0xD8]);
await txtFile.writeAsBytes([0x54, 0x45, 0x58, 0x54]);
await pngFile.writeAsBytes([0x89, 0x50]);
final testFiles = [jpgFile, txtFile, pngFile];
final result = await ImageCompressUtility.compressFilesList(testFiles);
expect(result, isA<List<File>>());
expect(result.length, 3);
expect(result[0].path, jpgFile.path);
expect(result[1].path, txtFile.path);
expect(result[2].path, pngFile.path);
} finally {
for (final file in [jpgFile, txtFile, pngFile]) {
if (await file.exists()) {
await file.delete();
}
}
}
});
test('compressFilesList - обрабатывает файлы с длинными путями', () async {
final longPathFile = File('very_long_path_name_for_testing_image_compression.jpg');
try {
await longPathFile.writeAsBytes([0xFF, 0xD8]);
final testFiles = [longPathFile];
final result = await ImageCompressUtility.compressFilesList(testFiles);
expect(result, isA<List<File>>());
expect(result.length, 1);
expect(result[0].path, longPathFile.path);
} finally {
if (await longPathFile.exists()) {
await longPathFile.delete();
}
}
});
test('compressFilesList - обрабатывает файлы с точками в имени', () async {
final fileWithDots = File('test.file.name.jpg');
try {
await fileWithDots.writeAsBytes([0xFF, 0xD8]);
final testFiles = [fileWithDots];
final result = await ImageCompressUtility.compressFilesList(testFiles);
expect(result, isA<List<File>>());
expect(result.length, 1);
expect(result[0].path, fileWithDots.path);
} finally {
if (await fileWithDots.exists()) {
await fileWithDots.delete();
}
}
});
test('compressFilesList - обрабатывает файлы без расширения', () async {
final fileNoExt = File('test_file_no_extension');
try {
await fileNoExt.writeAsBytes([0xFF, 0xD8]);
final testFiles = [fileNoExt];
final result = await ImageCompressUtility.compressFilesList(testFiles);
expect(result, isA<List<File>>());
expect(result.length, 1);
expect(result[0].path, fileNoExt.path);
} finally {
if (await fileNoExt.exists()) {
await fileNoExt.delete();
}
}
});
});
}

View File

@@ -135,6 +135,91 @@ void main() {
expect(blogPostRoute.builder, isNotNull);
});
test('aboutPlanScreen builder обрабатывает slug из query параметров с navigateToComments false', () {
final routes = ContentRoutes.createRoutes();
final planRoute = routes.firstWhere(
(route) => route is GoRoute && route.path == '/about-plan-screen',
) as GoRoute;
expect(planRoute.builder, isNotNull);
expect(planRoute.name, 'aboutPlanScreen');
});
test('aboutRecipesScreen builder обрабатывает slug из query параметров с navigateToComments false', () {
final routes = ContentRoutes.createRoutes();
final recipeRoute = routes.firstWhere(
(route) => route is GoRoute && route.path == '/about-recipes-screen',
) as GoRoute;
expect(recipeRoute.builder, isNotNull);
expect(recipeRoute.name, 'aboutRecipesScreen');
});
test('aboutDietScreen builder обрабатывает slug из query параметров с index null', () {
final routes = ContentRoutes.createRoutes();
final dietRoute = routes.firstWhere(
(route) => route is GoRoute && route.path == '/about-diet-screen',
) as GoRoute;
expect(dietRoute.builder, isNotNull);
expect(dietRoute.name, 'aboutDietScreen');
});
test('blogPostScreen builder обрабатывает id из query параметров как int', () {
final routes = ContentRoutes.createRoutes();
final blogPostRoute = routes.firstWhere(
(route) => route is GoRoute && route.path == '/blog-post-screen',
) as GoRoute;
expect(blogPostRoute.builder, isNotNull);
expect(blogPostRoute.name, 'blogPostScreen');
});
test('blogPostScreen builder обрабатывает id из extra как int', () {
final routes = ContentRoutes.createRoutes();
final blogPostRoute = routes.firstWhere(
(route) => route is GoRoute && route.path == '/blog-post-screen',
) as GoRoute;
expect(blogPostRoute.builder, isNotNull);
});
test('aboutPlanScreen builder обрабатывает extra данные с index', () {
final routes = ContentRoutes.createRoutes();
final planRoute = routes.firstWhere(
(route) => route is GoRoute && route.path == '/about-plan-screen',
) as GoRoute;
expect(planRoute.builder, isNotNull);
});
test('aboutRecipesScreen builder обрабатывает extra данные с index', () {
final routes = ContentRoutes.createRoutes();
final recipeRoute = routes.firstWhere(
(route) => route is GoRoute && route.path == '/about-recipes-screen',
) as GoRoute;
expect(recipeRoute.builder, isNotNull);
});
test('aboutDietScreen builder обрабатывает extra данные с id и slug', () {
final routes = ContentRoutes.createRoutes();
final dietRoute = routes.firstWhere(
(route) => route is GoRoute && route.path == '/about-diet-screen',
) as GoRoute;
expect(dietRoute.builder, isNotNull);
});
test('blogPostCreateScreen builder обрабатывает null extra', () {
final routes = ContentRoutes.createRoutes();
final blogPostCreateRoute = routes.firstWhere(
(route) => route is GoRoute && route.path == '/blog-post-create-screen',
) as GoRoute;
expect(blogPostCreateRoute.builder, isNotNull);
});
});
}

View File

@@ -14,6 +14,109 @@ void main() {
final routes = MainNavigationRoutes.createRoutes();
expect(routes.first, isA<ShellRoute>());
});
test('ShellRoute содержит маршрут myProfileScreen', () {
final routes = MainNavigationRoutes.createRoutes();
final shellRoute = routes.first as ShellRoute;
final myProfileRoute = shellRoute.routes.firstWhere(
(route) => route is GoRoute && route.path == '/my-profile-screen',
orElse: () => throw Exception('Route not found'),
);
expect(myProfileRoute, isA<GoRoute>());
expect((myProfileRoute as GoRoute).name, 'myProfileScreen');
});
test('ShellRoute содержит маршрут feedScreen', () {
final routes = MainNavigationRoutes.createRoutes();
final shellRoute = routes.first as ShellRoute;
final feedRoute = shellRoute.routes.firstWhere(
(route) => route is GoRoute && route.path == '/feed-screen',
orElse: () => throw Exception('Route not found'),
);
expect(feedRoute, isA<GoRoute>());
expect((feedRoute as GoRoute).name, 'feedScreen');
});
test('ShellRoute содержит маршрут diaryScreen', () {
final routes = MainNavigationRoutes.createRoutes();
final shellRoute = routes.first as ShellRoute;
final diaryRoute = shellRoute.routes.firstWhere(
(route) => route is GoRoute && route.path == '/diary-screen',
orElse: () => throw Exception('Route not found'),
);
expect(diaryRoute, isA<GoRoute>());
expect((diaryRoute as GoRoute).name, 'diaryScreen');
});
test('ShellRoute содержит маршрут consultationsScreen', () {
final routes = MainNavigationRoutes.createRoutes();
final shellRoute = routes.first as ShellRoute;
final consultationsRoute = shellRoute.routes.firstWhere(
(route) => route is GoRoute && route.path == '/consultations-screen',
orElse: () => throw Exception('Route not found'),
);
expect(consultationsRoute, isA<GoRoute>());
});
test('ShellRoute содержит маршрут chatSelectScreen', () {
final routes = MainNavigationRoutes.createRoutes();
final shellRoute = routes.first as ShellRoute;
final chatSelectRoute = shellRoute.routes.firstWhere(
(route) => route is GoRoute && route.path == '/chat-select-screen',
orElse: () => throw Exception('Route not found'),
);
expect(chatSelectRoute, isA<GoRoute>());
expect((chatSelectRoute as GoRoute).name, 'chatSelectScreen');
});
test('ShellRoute содержит маршрут notificationsScreen', () {
final routes = MainNavigationRoutes.createRoutes();
final shellRoute = routes.first as ShellRoute;
final notificationsRoute = shellRoute.routes.firstWhere(
(route) => route is GoRoute && route.path == '/notifications-screen',
orElse: () => throw Exception('Route not found'),
);
expect(notificationsRoute, isA<GoRoute>());
expect((notificationsRoute as GoRoute).name, 'notificationsScreen');
});
test('ShellRoute содержит маршрут menuScreen', () {
final routes = MainNavigationRoutes.createRoutes();
final shellRoute = routes.first as ShellRoute;
final menuRoute = shellRoute.routes.firstWhere(
(route) => route is GoRoute && route.path == '/menu-screen',
orElse: () => throw Exception('Route not found'),
);
expect(menuRoute, isA<GoRoute>());
expect((menuRoute as GoRoute).name, 'menuScreen');
});
test('feedScreen имеет redirect функцию', () {
final routes = MainNavigationRoutes.createRoutes();
final shellRoute = routes.first as ShellRoute;
final feedRoute = shellRoute.routes.firstWhere(
(route) => route is GoRoute && route.path == '/feed-screen',
) as GoRoute;
expect(feedRoute.redirect, isNotNull);
});
test('menuScreen использует BlocBuilder для ProfileAvatarCubit', () {
final routes = MainNavigationRoutes.createRoutes();
final shellRoute = routes.first as ShellRoute;
final menuRoute = shellRoute.routes.firstWhere(
(route) => route is GoRoute && route.path == '/menu-screen',
) as GoRoute;
expect(menuRoute.pageBuilder, isNotNull);
});
test('ShellRoute builder инициализирует ProfileAvatarCubit', () {
final routes = MainNavigationRoutes.createRoutes();
final shellRoute = routes.first as ShellRoute;
expect(shellRoute.builder, isNotNull);
});
});
}

View File

@@ -1,4 +1,5 @@
import 'package:eatvio_app/core/routing/other_routes.dart';
import 'package:eatvio_app/core/routing/route_constants.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:go_router/go_router.dart';
@@ -77,5 +78,149 @@ void main() {
routes.firstWhere((route) => route is GoRoute && route.path == '/update-screen', orElse: () => throw Exception('Route not found')) as GoRoute;
expect(updateRoute.redirect, isNotNull);
});
test('createRoutes содержит маршрут chatScreen', () {
final routes = OtherRoutes.createRoutes();
final chatRoute = routes.firstWhere((route) => route is GoRoute && route.path == '/chat-screen', orElse: () => throw Exception('Route not found'));
expect(chatRoute, isA<GoRoute>());
});
test('createRoutes содержит маршрут emptyChatScreen', () {
final routes = OtherRoutes.createRoutes();
final emptyChatRoute = routes.firstWhere(
(route) => route is GoRoute && route.path == '/empty-chat-screen',
orElse: () => throw Exception('Route not found'),
);
expect(emptyChatRoute, isA<GoRoute>());
});
test('createRoutes содержит маршрут createDietScreen', () {
final routes = OtherRoutes.createRoutes();
final createDietRoute = routes.firstWhere(
(route) => route is GoRoute && route.path == '/create-diet-screen',
orElse: () => throw Exception('Route not found'),
);
expect(createDietRoute, isA<GoRoute>());
});
test('createRoutes содержит маршрут dietPreviewScreen', () {
final routes = OtherRoutes.createRoutes();
final dietPreviewRoute = routes.firstWhere(
(route) => route is GoRoute && route.path == '/create-diet-screen/preview',
orElse: () => throw Exception('Route not found'),
);
expect(dietPreviewRoute, isA<GoRoute>());
});
test('createRoutes содержит маршрут myDietsScreen', () {
final routes = OtherRoutes.createRoutes();
final myDietsRoute = routes.firstWhere((route) => route is GoRoute && route.path == '/my-diets-screen', orElse: () => throw Exception('Route not found'));
expect(myDietsRoute, isA<GoRoute>());
});
test('createRoutes содержит маршрут myPlansScreen', () {
final routes = OtherRoutes.createRoutes();
final myPlansRoute = routes.firstWhere((route) => route is GoRoute && route.path == '/my-plans-screen', orElse: () => throw Exception('Route not found'));
expect(myPlansRoute, isA<GoRoute>());
});
test('createRoutes содержит маршрут myRecipesScreen', () {
final routes = OtherRoutes.createRoutes();
final myRecipesRoute = routes.firstWhere(
(route) => route is GoRoute && route.path == '/my-recipes-screen',
orElse: () => throw Exception('Route not found'),
);
expect(myRecipesRoute, isA<GoRoute>());
});
test('createRoutes содержит маршрут userFinanceScreen', () {
final routes = OtherRoutes.createRoutes();
final userFinanceRoute = routes.firstWhere(
(route) => route is GoRoute && route.path == '/user-finance-screen',
orElse: () => throw Exception('Route not found'),
);
expect(userFinanceRoute, isA<GoRoute>());
});
test('createRoutes содержит маршрут consultationsArchiveScreen', () {
final routes = OtherRoutes.createRoutes();
final consultationsArchiveRoute = routes.firstWhere(
(route) => route is GoRoute && route.path == '/consultations-archive-screen',
orElse: () => throw Exception('Route not found'),
);
expect(consultationsArchiveRoute, isA<GoRoute>());
});
test('createRoutes содержит маршрут consultantFinancesScreen', () {
final routes = OtherRoutes.createRoutes();
final consultantFinancesRoute = routes.firstWhere(
(route) => route is GoRoute && route.path == '/consultant-finances-screen',
orElse: () => throw Exception('Route not found'),
);
expect(consultantFinancesRoute, isA<GoRoute>());
});
test('createRoutes содержит маршрут userPurchasesScreen', () {
final routes = OtherRoutes.createRoutes();
final userPurchasesRoute = routes.firstWhere(
(route) => route is GoRoute && route.path == '/user-purchases-screen',
orElse: () => throw Exception('Route not found'),
);
expect(userPurchasesRoute, isA<GoRoute>());
});
test('createRoutes содержит маршрут stepperBecomeConsultantScreen', () {
final routes = OtherRoutes.createRoutes();
final stepperBecomeConsultantRoute = routes.firstWhere(
(route) => route is GoRoute && route.path == '/stepper-become-consultant-screen',
orElse: () => throw Exception('Route not found'),
);
expect(stepperBecomeConsultantRoute, isA<GoRoute>());
});
test('createRoutes содержит маршрут photoViewScreen', () {
final routes = OtherRoutes.createRoutes();
final photoViewRoute = routes.firstWhere(
(route) => route is GoRoute && route.path == RouteConstants.photoViewScreen,
orElse: () => throw Exception('Route not found'),
);
expect(photoViewRoute, isA<GoRoute>());
});
test('createRoutes содержит маршрут userDetailsScreen', () {
final routes = OtherRoutes.createRoutes();
final userDetailsRoute = routes.firstWhere(
(route) => route is GoRoute && route.path == '/user-details-screen',
orElse: () => throw Exception('Route not found'),
);
expect(userDetailsRoute, isA<GoRoute>());
});
test('createRoutes содержит маршрут personalDietSuggestionScreen', () {
final routes = OtherRoutes.createRoutes();
final personalDietSuggestionRoute = routes.firstWhere(
(route) => route is GoRoute && route.path == '/personal-diet-suggestion',
orElse: () => throw Exception('Route not found'),
);
expect(personalDietSuggestionRoute, isA<GoRoute>());
});
test('createRoutes содержит маршрут blockedUsersScreen', () {
final routes = OtherRoutes.createRoutes();
final blockedUsersRoute = routes.firstWhere(
(route) => route is GoRoute && route.path == '/blocked-users-screen',
orElse: () => throw Exception('Route not found'),
);
expect(blockedUsersRoute, isA<GoRoute>());
});
test('createRoutes содержит маршрут notificationsSettingsScreen', () {
final routes = OtherRoutes.createRoutes();
final notificationsSettingsRoute = routes.firstWhere(
(route) => route is GoRoute && route.path == '/notifications-settings-screen',
orElse: () => throw Exception('Route not found'),
);
expect(notificationsSettingsRoute, isA<GoRoute>());
});
});
}

View File

@@ -0,0 +1,139 @@
import 'package:eatvio_app/core/utils/markdown_utils.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
group('MarkdownUtils', () {
test('processMarkdown - обрабатывает простой текст без изменений', () {
const text = 'Простой текст без форматирования';
final result = MarkdownUtils.processMarkdown(text);
expect(result, text);
});
test('processMarkdown - преобразует URL в markdown ссылки', () {
const text = 'Посетите https://example.com для получения информации';
final result = MarkdownUtils.processMarkdown(text);
expect(result, contains('[https://example.com](https://example.com)'));
});
test('processMarkdown - преобразует www URL в markdown ссылки', () {
const text = 'Посетите www.example.com для получения информации';
final result = MarkdownUtils.processMarkdown(text);
expect(result, contains('[www.example.com](https://www.example.com)'));
});
test('processMarkdown - преобразует http URL в markdown ссылки', () {
const text = 'Посетите http://example.com для получения информации';
final result = MarkdownUtils.processMarkdown(text);
expect(result, contains('[http://example.com](http://example.com)'));
});
test('processMarkdown - не преобразует URL внутри существующих markdown ссылок', () {
const text = 'Ссылка [https://example.com](https://example.com) уже в формате markdown';
final result = MarkdownUtils.processMarkdown(text);
// URL не должен быть преобразован дважды
expect(result, contains('[https://example.com](https://example.com)'));
// Проверяем, что нет дублирования
final matches = RegExp(r'\[https://example\.com\]\(https://example\.com\)').allMatches(result);
expect(matches.length, 1);
});
test('processMarkdown - исправляет неправильный синтаксис markdown ссылок', () {
const text = 'Неправильная ссылка [https://example.com](url)';
final result = MarkdownUtils.processMarkdown(text);
expect(result, contains('[https://example.com](https://example.com)'));
expect(result, isNot(contains('(url)')));
});
test('processMarkdown - исправляет неправильный синтаксис с www', () {
const text = 'Неправильная ссылка [www.example.com](url)';
final result = MarkdownUtils.processMarkdown(text);
expect(result, contains('[www.example.com](https://www.example.com)'));
});
test('processMarkdown - не исправляет ссылки без http или www', () {
const text = 'Ссылка [example.com](url) не должна быть исправлена';
final result = MarkdownUtils.processMarkdown(text);
expect(result, contains('[example.com](url)'));
});
test('processMarkdown - преобразует жирный текст **текст** в <b>текст</b>', () {
const text = 'Это **жирный текст** в markdown';
final result = MarkdownUtils.processMarkdown(text);
expect(result, contains('<b>жирный текст</b>'));
expect(result, isNot(contains('**жирный текст**')));
});
test('processMarkdown - обрабатывает несколько жирных фрагментов', () {
const text = 'Это **первый** и **второй** жирный текст';
final result = MarkdownUtils.processMarkdown(text);
expect(result, contains('<b>первый</b>'));
expect(result, contains('<b>второй</b>'));
});
test('processMarkdown - обрабатывает URL и жирный текст вместе', () {
const text = 'Посетите https://example.com для **важной** информации';
final result = MarkdownUtils.processMarkdown(text);
expect(result, contains('[https://example.com](https://example.com)'));
expect(result, contains('<b>важной</b>'));
});
test('processMarkdown - обрабатывает несколько URL в тексте', () {
const text = 'Ссылки: https://example.com и www.test.com';
final result = MarkdownUtils.processMarkdown(text);
expect(result, contains('[https://example.com](https://example.com)'));
expect(result, contains('[www.test.com](https://www.test.com)'));
});
test('processMarkdown - обрабатывает URL с параметрами', () {
const text = 'Ссылка https://example.com?param=value&other=test';
final result = MarkdownUtils.processMarkdown(text);
expect(result, contains('[https://example.com?param=value&other=test](https://example.com?param=value&other=test)'));
});
test('processMarkdown - обрабатывает URL в конце предложения', () {
const text = 'Посетите https://example.com.';
final result = MarkdownUtils.processMarkdown(text);
// Точка включается в URL, так как регулярное выражение захватывает её
expect(result, contains('[https://example.com.](https://example.com.)'));
});
test('processMarkdown - обрабатывает URL в скобках', () {
const text = 'Ссылка (https://example.com) в скобках';
final result = MarkdownUtils.processMarkdown(text);
expect(result, contains('[https://example.com](https://example.com)'));
});
test('processMarkdown - обрабатывает пустую строку', () {
const text = '';
final result = MarkdownUtils.processMarkdown(text);
expect(result, '');
});
test('processMarkdown - обрабатывает текст только с пробелами', () {
const text = ' ';
final result = MarkdownUtils.processMarkdown(text);
expect(result, ' ');
});
test('processMarkdown - обрабатывает URL с портом', () {
const text = 'Ссылка https://example.com:8080/path';
final result = MarkdownUtils.processMarkdown(text);
expect(result, contains('[https://example.com:8080/path](https://example.com:8080/path)'));
});
test('processMarkdown - обрабатывает URL с якорем', () {
const text = 'Ссылка https://example.com#section';
final result = MarkdownUtils.processMarkdown(text);
expect(result, contains('[https://example.com#section](https://example.com#section)'));
});
test('processMarkdown - обрабатывает сложный текст с множеством элементов', () {
const text = 'Это **важный** текст с https://example.com и **другим** жирным текстом. Также www.test.com';
final result = MarkdownUtils.processMarkdown(text);
expect(result, contains('<b>важный</b>'));
expect(result, contains('<b>другим</b>'));
expect(result, contains('[https://example.com](https://example.com)'));
expect(result, contains('[www.test.com](https://www.test.com)'));
});
});
}

View File

@@ -1,11 +1,15 @@
import 'package:eatvio_app/core/app_config.dart';
import 'package:eatvio_app/core/app_initializer.dart';
import 'package:eatvio_app/core/locator_service.dart';
import 'package:eatvio_app/core/token_storage.dart';
import 'package:eatvio_app/core/websockets/ws_manager.dart';
import 'package:eatvio_app/core/websockets/ws_service.dart';
import 'package:eatvio_app/features/chat/models/websocket_info_model.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'ws_manager_test.mocks.dart';
@@ -17,6 +21,8 @@ void main() {
});
setUp(() async {
SharedPreferences.setMockInitialValues({});
TokenStorage.clearCache();
await sl.reset();
sl.registerLazySingleton<AppConfig>(() => AppConfig(buildType: AppBuildType.tests)..setBaseUrl('http://localhost'));
});
@@ -25,7 +31,6 @@ void main() {
final wsService = MockWsService();
final manager = WsManager(wsService: wsService);
await manager.initialize();
expect(manager, isNotNull);
@@ -86,5 +91,165 @@ void main() {
expect(manager.client, isNull);
expect(manager.userId, isNull);
});
test('initPrivateDirectChatChannel - создает канал для прямого чата', () async {
final wsService = MockWsService();
final manager = WsManager(wsService: wsService);
const chatId = 'chat-123';
const token = 'test-token';
await TokenStorage.saveTokenInStorage(token);
// initPrivateDirectChatChannel требует, чтобы client был инициализирован
// Но так как мы не можем легко мокировать PusherChannelsClient,
// этот тест проверяет, что метод не выбрасывает исключение
// когда client равен null
expect(() => manager.initPrivateDirectChatChannel(chatId), returnsNormally);
});
test('initialize - обрабатывает ошибку при отсутствии токена', () async {
final wsService = MockWsService();
final manager = WsManager(wsService: wsService);
// Убеждаемся, что токена нет
await TokenStorage.removeTokenInStorage();
// initialize должен обработать ошибку gracefully
await manager.initialize();
expect(manager.client, isNull);
});
test('initialize - обрабатывает ошибку при ошибке getWebsocket', () async {
final wsService = MockWsService();
final manager = WsManager(wsService: wsService);
const token = 'test-token';
await TokenStorage.saveTokenInStorage(token);
when(wsService.getWebsocket()).thenThrow(Exception('Network error'));
// initialize должен обработать ошибку gracefully
await manager.initialize();
expect(manager.client, isNull);
});
test('initialize - устанавливает userId из WebsocketInfoModel', () async {
final wsService = MockWsService();
final manager = WsManager(wsService: wsService);
const token = 'test-token';
const userId = 42;
await TokenStorage.saveTokenInStorage(token);
when(
wsService.getWebsocket(),
).thenAnswer((_) async => const WebsocketInfoModel(key: 'test-key', host: 'test-host', port: '443', cluster: 'test-cluster', userId: userId));
// initialize должен установить userId, но так как мы не можем
// полностью мокировать PusherChannelsClient, этот тест проверяет,
// что метод не выбрасывает исключение
await manager.initialize();
// userId может быть установлен только если client успешно создан
// В данном случае, так как мы не можем полностью мокировать client,
// мы просто проверяем, что метод не выбрасывает исключение
expect(manager, isNotNull);
});
test('clear - отписывается от всех каналов', () async {
final wsService = MockWsService();
final manager = WsManager(wsService: wsService);
manager.userId = 1;
// clear должен отписаться от всех каналов
await manager.clear();
expect(manager.client, isNull);
expect(manager.userId, isNull);
});
test('initialize - не инициализирует повторно если client уже существует', () async {
final wsService = MockWsService();
final manager = WsManager(wsService: wsService);
const token = 'test-token';
await TokenStorage.saveTokenInStorage(token);
when(
wsService.getWebsocket(),
).thenAnswer((_) async => const WebsocketInfoModel(key: 'test-key', host: 'test-host', port: '443', cluster: 'test-cluster', userId: 1));
// Первый вызов initialize
await manager.initialize();
// Второй вызов initialize не должен вызывать getWebsocket снова
// если client уже инициализирован
final initialClient = manager.client;
await manager.initialize();
// client должен остаться тем же (или null, если инициализация не удалась)
expect(manager.client, initialClient);
});
test('subscribeDirectChatChannel - подписывается на канал когда он существует', () {
final wsService = MockWsService();
final manager = WsManager(wsService: wsService);
// Когда канал null, метод не должен выбрасывать исключение
manager.subscribeDirectChatChannel();
expect(manager, isNotNull);
});
test('unsubscribeDirectChatChannel - отписывается от канала когда он существует', () {
final wsService = MockWsService();
final manager = WsManager(wsService: wsService);
// Когда канал null, метод не должен выбрасывать исключение
manager.unsubscribeDirectChatChannel();
expect(manager, isNotNull);
});
test('newMessagesStream - возвращает null когда канал не инициализирован', () {
final wsService = MockWsService();
final manager = WsManager(wsService: wsService);
expect(manager.newMessagesStream, isNull);
});
test('newNotificationsStream - возвращает null когда канал не инициализирован', () {
final wsService = MockWsService();
final manager = WsManager(wsService: wsService);
expect(manager.newNotificationsStream, isNull);
});
test('directChatStream - возвращает null когда канал не инициализирован', () {
final wsService = MockWsService();
final manager = WsManager(wsService: wsService);
expect(manager.directChatStream, isNull);
});
test('clientTypingStream - возвращает null когда канал не инициализирован', () {
final wsService = MockWsService();
final manager = WsManager(wsService: wsService);
expect(manager.clientTypingStream, isNull);
});
test('initialize - обрабатывает случай когда TokenStorage.haveTokenInStorage возвращает false', () async {
final wsService = MockWsService();
final manager = WsManager(wsService: wsService);
// Убеждаемся, что токена нет
await TokenStorage.removeTokenInStorage();
// initialize должен обработать отсутствие токена gracefully
await manager.initialize();
expect(manager.client, isNull);
});
});
}