chore(version): update app version to 1.3.5+85 and improve test coverage reporting in CI

This commit is contained in:
mso7nueh
2025-12-08 11:19:08 +03:00
parent abe4985f0e
commit c2a506cdbf
148 changed files with 12395 additions and 1889 deletions

View File

@@ -0,0 +1,258 @@
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/features/chat/models/chat_message_model.dart';
import 'package:eatvio_app/features/chat/models/chat_room_pagination_model.dart';
import 'package:eatvio_app/features/chat/presentation/widgets/chat_entry_widget.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:go_router/go_router.dart';
import 'package:network_image_mock/network_image_mock.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
group('ChatEntryWidget', () {
Widget createTestWidget(ChatRoom chatRoom) {
return MaterialApp.router(
theme: ThemeHelper().themeData(),
localizationsDelegates: [
S.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: const [Locale('ru'), Locale('en')],
locale: const Locale('ru'),
routerConfig: GoRouter(
initialLocation: '/',
routes: [
GoRoute(
path: '/',
builder: (context, state) => Scaffold(
body: ChatEntryWidget(chatRoom: chatRoom),
),
),
GoRoute(
path: '/chatScreen',
name: 'chatScreen',
builder: (context, state) => Scaffold(
appBar: AppBar(title: const Text('Chat')),
body: Center(child: Text('Chat: ${state.extra}')),
),
),
],
),
);
}
testWidgets('displays chat room with user name', (tester) async {
final chatRoom = ChatRoom(
id: 'test-id',
users: Users(
fromChatUser: ChatUser(
id: 1,
name: 'From User',
profilePhotoPath: null,
profilePhotoUrl: 'http://localhost/photo.jpg',
),
toChatUser: ChatUser(
id: 2,
name: 'Test User',
profilePhotoPath: null,
profilePhotoUrl: 'http://localhost/photo.jpg',
),
),
unreadMessagesCount: 0,
lastMessage: null,
chosenPlan: null,
);
mockNetworkImagesFor(() async {
await tester.pumpWidget(createTestWidget(chatRoom));
await tester.pumpAndSettle();
expect(find.text('Test User'), findsOneWidget);
});
});
testWidgets('displays chat room with last message', (tester) async {
final chatRoom = ChatRoom(
id: 'test-id',
users: Users(
fromChatUser: ChatUser(
id: 1,
name: 'From User',
profilePhotoPath: null,
profilePhotoUrl: 'http://localhost/photo.jpg',
),
toChatUser: ChatUser(
id: 2,
name: 'Test User',
profilePhotoPath: null,
profilePhotoUrl: 'http://localhost/photo.jpg',
),
),
unreadMessagesCount: 0,
lastMessage: ChatMessageModel(
id: 1,
data: 'Test message',
updatedAt: DateTime.now(),
isCurrentUserAuthor: false,
mediaData: null,
authorId: 1,
isRead: false,
roomId: 'test-id',
),
chosenPlan: null,
);
mockNetworkImagesFor(() async {
await tester.pumpWidget(createTestWidget(chatRoom));
await tester.pumpAndSettle();
expect(find.text('Test message'), findsOneWidget);
});
});
testWidgets('displays chat room with unread messages count', (tester) async {
final chatRoom = ChatRoom(
id: 'test-id',
users: Users(
fromChatUser: ChatUser(
id: 1,
name: 'From User',
profilePhotoPath: null,
profilePhotoUrl: 'http://localhost/photo.jpg',
),
toChatUser: ChatUser(
id: 2,
name: 'Test User',
profilePhotoPath: null,
profilePhotoUrl: 'http://localhost/photo.jpg',
),
),
unreadMessagesCount: 5,
lastMessage: null,
chosenPlan: null,
);
mockNetworkImagesFor(() async {
await tester.pumpWidget(createTestWidget(chatRoom));
await tester.pumpAndSettle();
expect(find.text('5'), findsOneWidget);
});
});
testWidgets('displays message from current user', (tester) async {
final chatRoom = ChatRoom(
id: 'test-id',
users: Users(
fromChatUser: ChatUser(
id: 1,
name: 'From User',
profilePhotoPath: null,
profilePhotoUrl: 'http://localhost/photo.jpg',
),
toChatUser: ChatUser(
id: 2,
name: 'Test User',
profilePhotoPath: null,
profilePhotoUrl: 'http://localhost/photo.jpg',
),
),
unreadMessagesCount: 0,
lastMessage: ChatMessageModel(
id: 1,
data: 'Test message',
updatedAt: DateTime.now(),
isCurrentUserAuthor: true,
mediaData: null,
authorId: 1,
isRead: false,
roomId: 'test-id',
),
chosenPlan: null,
);
mockNetworkImagesFor(() async {
await tester.pumpWidget(createTestWidget(chatRoom));
await tester.pumpAndSettle();
expect(find.textContaining('Вы:'), findsOneWidget);
});
});
testWidgets('displays media attachment message', (tester) async {
final chatRoom = ChatRoom(
id: 'test-id',
users: Users(
fromChatUser: ChatUser(
id: 1,
name: 'From User',
profilePhotoPath: null,
profilePhotoUrl: 'http://localhost/photo.jpg',
),
toChatUser: ChatUser(
id: 2,
name: 'Test User',
profilePhotoPath: null,
profilePhotoUrl: 'http://localhost/photo.jpg',
),
),
unreadMessagesCount: 0,
lastMessage: ChatMessageModel(
id: 1,
data: 'Test message',
updatedAt: DateTime.now(),
isCurrentUserAuthor: false,
mediaData: [{'type': 'image', 'url': 'https://example.com/image.jpg'}],
authorId: 1,
isRead: false,
roomId: 'test-id',
),
chosenPlan: null,
);
mockNetworkImagesFor(() async {
await tester.pumpWidget(createTestWidget(chatRoom));
await tester.pumpAndSettle();
expect(find.textContaining('📎 Вложение'), findsOneWidget);
});
});
testWidgets('truncates long user names', (tester) async {
final longName = 'A' * 30;
final chatRoom = ChatRoom(
id: 'test-id',
users: Users(
fromChatUser: ChatUser(
id: 1,
name: 'From User',
profilePhotoPath: null,
profilePhotoUrl: 'http://localhost/photo.jpg',
),
toChatUser: ChatUser(
id: 2,
name: longName,
profilePhotoPath: null,
profilePhotoUrl: 'http://localhost/photo.jpg',
),
),
unreadMessagesCount: 0,
lastMessage: null,
chosenPlan: null,
);
mockNetworkImagesFor(() async {
await tester.pumpWidget(createTestWidget(chatRoom));
await tester.pumpAndSettle();
expect(find.textContaining('...'), findsOneWidget);
});
});
});
}

View File

@@ -1,117 +1,108 @@
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/features/chat/presentation/widgets/chat_search_bar_widget.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
group('ChatSearchBarWidget Tests', () {
Widget createTestWidget(Widget child) {
return MaterialApp(
theme: ThemeHelper().themeData(),
localizationsDelegates: const [
S.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: const [Locale('ru')],
home: Scaffold(body: child),
group('ChatSearchBarWidget', () {
testWidgets('displays search bar with default icon', (tester) async {
await tester.pumpWidget(
MaterialApp(
theme: ThemeHelper().themeData(),
home: Scaffold(
body: ChatSearchBarWidget(
onSuffixIconTap: () {},
),
),
),
);
}
testWidgets('ChatSearchBarWidget - отображается с дефолтными значениями', (WidgetTester tester) async {
final widget = ChatSearchBarWidget(onSuffixIconTap: () {});
await tester.pumpWidget(createTestWidget(widget));
await tester.pumpAndSettle();
expect(find.byType(TextFormField), findsOneWidget);
expect(find.byType(ChatSearchBarWidget), findsOneWidget);
expect(find.byIcon(Icons.search), findsOneWidget);
});
testWidgets('ChatSearchBarWidget - вызывает onSuffixIconTap при нажатии на иконку очистки', (WidgetTester tester) async {
testWidgets('displays custom hint text', (tester) async {
await tester.pumpWidget(
MaterialApp(
theme: ThemeHelper().themeData(),
home: Scaffold(
body: ChatSearchBarWidget(
hintText: 'Search messages',
onSuffixIconTap: () {},
),
),
),
);
expect(find.byType(ChatSearchBarWidget), findsOneWidget);
});
testWidgets('calls onSuffixIconTap when clear button is tapped', (tester) async {
bool suffixTapped = false;
final controller = TextEditingController(text: 'test');
final widget = ChatSearchBarWidget(
controller: controller,
onSuffixIconTap: () {
suffixTapped = true;
},
await tester.pumpWidget(
MaterialApp(
theme: ThemeHelper().themeData(),
home: Scaffold(
body: ChatSearchBarWidget(
controller: controller,
onSuffixIconTap: () {
suffixTapped = true;
},
),
),
),
);
await tester.pumpWidget(createTestWidget(widget));
await tester.pumpAndSettle();
// Находим иконку очистки и нажимаем на неё
final clearIcon = find.byIcon(Icons.clear);
expect(clearIcon, findsOneWidget);
await tester.tap(clearIcon);
await tester.pumpAndSettle();
await tester.tap(find.byIcon(Icons.clear));
await tester.pump();
expect(suffixTapped, isTrue);
expect(controller.text, isEmpty);
});
testWidgets('ChatSearchBarWidget - вызывает onChanged при вводе текста', (WidgetTester tester) async {
testWidgets('clears controller when clear button is tapped', (tester) async {
final controller = TextEditingController(text: 'test');
await tester.pumpWidget(
MaterialApp(
theme: ThemeHelper().themeData(),
home: Scaffold(
body: ChatSearchBarWidget(
controller: controller,
onSuffixIconTap: () {},
),
),
),
);
expect(controller.text, 'test');
await tester.tap(find.byIcon(Icons.clear));
await tester.pump();
expect(controller.text, '');
});
testWidgets('calls onChanged when text is entered', (tester) async {
String? changedValue;
final controller = TextEditingController();
final widget = ChatSearchBarWidget(
controller: controller,
onChanged: (value) {
changedValue = value;
},
onSuffixIconTap: () {},
await tester.pumpWidget(
MaterialApp(
theme: ThemeHelper().themeData(),
home: Scaffold(
body: ChatSearchBarWidget(
controller: controller,
onChanged: (value) {
changedValue = value;
},
onSuffixIconTap: () {},
),
),
),
);
await tester.pumpWidget(createTestWidget(widget));
await tester.pumpAndSettle();
await tester.enterText(find.byType(TextFormField), 'test');
await tester.pump();
await tester.enterText(find.byType(TextFormField), 'test query');
await tester.pumpAndSettle();
expect(changedValue, equals('test query'));
});
testWidgets('ChatSearchBarWidget - отображает кастомный hintText', (WidgetTester tester) async {
const hintText = 'Search chats...';
final widget = ChatSearchBarWidget(hintText: hintText, onSuffixIconTap: () {});
await tester.pumpWidget(createTestWidget(widget));
await tester.pumpAndSettle();
expect(find.text(hintText), findsOneWidget);
});
testWidgets('ChatSearchBarWidget - использует кастомный prefix', (WidgetTester tester) async {
const customPrefix = Icon(Icons.filter_list);
final widget = ChatSearchBarWidget(prefix: customPrefix, onSuffixIconTap: () {});
await tester.pumpWidget(createTestWidget(widget));
await tester.pumpAndSettle();
expect(find.byIcon(Icons.filter_list), findsOneWidget);
expect(find.byIcon(Icons.search), findsNothing);
});
testWidgets('ChatSearchBarWidget - использует кастомный suffix', (WidgetTester tester) async {
bool suffixTapped = false;
final customSuffix = IconButton(
icon: const Icon(Icons.close),
onPressed: () {
suffixTapped = true;
},
);
final widget = ChatSearchBarWidget(suffix: customSuffix, onSuffixIconTap: () {});
await tester.pumpWidget(createTestWidget(widget));
await tester.pumpAndSettle();
await tester.tap(find.byIcon(Icons.close));
await tester.pumpAndSettle();
expect(suffixTapped, isTrue);
expect(changedValue, 'test');
});
});
}