feat(tests): test coverage 60%

This commit is contained in:
misha
2025-12-23 10:59:56 +03:00
parent 38118055a5
commit 5b705dfb72
67 changed files with 8391 additions and 42 deletions

View File

@@ -0,0 +1,205 @@
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/features/chat/presentation/bloc/chat_room_cubit/chat_room_cubit.dart';
import 'package:eatvio_app/features/chat/presentation/bloc/chat_room_cubit/chat_room_state.dart';
import 'package:eatvio_app/features/chat/presentation/widgets/chose_plan_date_widget.dart';
import 'package:eatvio_app/features/chat/presentation/widgets/complete_plan_bottom_sheet.dart';
import 'package:eatvio_app/features/chat/presentation/widgets/plan_complain_bottom_sheet.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_portal/flutter_portal.dart';
import 'package:go_router/go_router.dart';
class FakeChatRoomCubit extends Cubit<ChatRoomState> implements ChatRoomCubit {
FakeChatRoomCubit(super.initialState);
bool finishResult = true;
bool complainResult = true;
@override
Future<bool> choseStartPlanDate(DateTime date) async => true;
@override
Future<bool> finishChosenPlan() async => finishResult;
@override
Future<bool> complainAboutConsultation({required String description}) async => complainResult;
@override
dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
class _FakeAssetBundle extends CachingAssetBundle {
_FakeAssetBundle(this.assets);
final Map<String, String> assets;
@override
Future<String> loadString(String key, {bool cache = true}) async {
final value = assets[key];
if (value == null) throw FlutterError('Asset not found: $key');
return value;
}
@override
Future<ByteData> load(String key) async {
final value = assets[key];
if (value == null) throw FlutterError('Asset not found: $key');
return ByteData.sublistView(Uint8List.fromList(value.codeUnits));
}
}
void main() {
group('Chat bottom sheets smoke', () {
const fakeSvg = '''
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="10" viewBox="0 0 10 10">
<rect width="10" height="10" fill="red"/>
</svg>
''';
final bundle = _FakeAssetBundle({'assets/icons/error_icon.svg': fakeSvg, 'assets/icons/question_mark.svg': fakeSvg});
Widget wrap(Widget child) {
final router = GoRouter(
routes: [
GoRoute(
path: '/',
builder: (_, _) => Scaffold(body: SingleChildScrollView(child: child)),
),
],
);
return DefaultAssetBundle(
bundle: bundle,
child: Portal(
child: MaterialApp.router(
theme: ThemeHelper().themeData(),
localizationsDelegates: const [
S.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: const [Locale('ru')],
routerConfig: router,
),
),
);
}
testWidgets('ChosePlanDateWidget renders', (tester) async {
final cubit = FakeChatRoomCubit(const ChatRoomState.loading());
await tester.pumpWidget(wrap(ChosePlanDateWidget(chatCubit: cubit, hasPersonalDiet: false)));
await tester.pump();
expect(find.text(S.current.confirm), findsOneWidget);
});
testWidgets('ChosePlanDateWidget toggles tooltip when hasPersonalDiet', (tester) async {
final cubit = FakeChatRoomCubit(const ChatRoomState.loading());
await tester.pumpWidget(wrap(ChosePlanDateWidget(chatCubit: cubit, hasPersonalDiet: true)));
await tester.pump();
expect(find.text(S.current.chosenPlanStartDateDescr), findsOneWidget);
await tester.tap(find.byType(GestureDetector).first);
await tester.pump();
expect(find.text(S.current.chosenPlanStartDateDescr), findsOneWidget);
});
testWidgets('CompletePlanBottomSheet shows failure UI when finish fails', (tester) async {
final cubit = FakeChatRoomCubit(const ChatRoomState.loading())..finishResult = false;
await tester.pumpWidget(wrap(CompletePlanBottomSheet(cubit: cubit)));
await tester.pump();
await tester.tap(find.text(S.current.confirm));
await tester.pump();
expect(find.text('Произошла ошибка, попробуйте позже'), findsOneWidget);
expect(find.text(S.current.ok), findsOneWidget);
});
testWidgets('CompletePlanBottomSheet closes on success', (tester) async {
final cubit = FakeChatRoomCubit(const ChatRoomState.loading())..finishResult = true;
final router = GoRouter(
routes: [
GoRoute(
path: '/',
builder: (context, state) => Scaffold(
body: Center(
child: TextButton(onPressed: () => context.push('/sheet'), child: const Text('open')),
),
),
),
GoRoute(
path: '/sheet',
builder: (_, _) => Scaffold(
body: SingleChildScrollView(child: CompletePlanBottomSheet(cubit: cubit)),
),
),
],
);
await tester.pumpWidget(
DefaultAssetBundle(
bundle: bundle,
child: Portal(
child: MaterialApp.router(
theme: ThemeHelper().themeData(),
localizationsDelegates: const [
S.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: const [Locale('ru')],
routerConfig: router,
),
),
),
);
await tester.pump();
await tester.tap(find.text('open'));
await tester.pumpAndSettle();
expect(find.byType(CompletePlanBottomSheet), findsOneWidget);
await tester.tap(find.text(S.current.confirm));
await tester.pumpAndSettle();
expect(find.byType(CompletePlanBottomSheet), findsNothing);
});
testWidgets('PlanComplainBottomSheet validates text length', (tester) async {
final cubit = FakeChatRoomCubit(const ChatRoomState.loading());
await tester.pumpWidget(wrap(PlanComplainBottomSheet(cubit: cubit)));
await tester.pump();
await tester.tap(find.text(S.current.confirm));
await tester.pump();
await tester.enterText(find.byType(TextFormField), 'a');
await tester.pump();
expect(find.text(S.current.atLeast3Symbols), findsOneWidget);
});
testWidgets('PlanComplainBottomSheet shows success UI when submit succeeds', (tester) async {
final cubit = FakeChatRoomCubit(const ChatRoomState.loading())..complainResult = true;
await tester.pumpWidget(wrap(PlanComplainBottomSheet(cubit: cubit)));
await tester.pump();
await tester.enterText(find.byType(TextFormField), 'abc');
await tester.pump();
await tester.tap(find.text(S.current.confirm));
await tester.pump();
expect(find.text('Жалоба отправлена на рассмотрение. Обычно это занимает до трёх рабочих дней.'), findsOneWidget);
expect(find.text(S.current.ok), findsOneWidget);
});
testWidgets('PlanComplainBottomSheet shows failure UI when submit fails', (tester) async {
final cubit = FakeChatRoomCubit(const ChatRoomState.loading())..complainResult = false;
await tester.pumpWidget(wrap(PlanComplainBottomSheet(cubit: cubit)));
await tester.pump();
await tester.enterText(find.byType(TextFormField), 'abc');
await tester.pump();
await tester.tap(find.text(S.current.confirm));
await tester.pump();
expect(find.text('Произошла ошибка, попробуйте позже'), findsOneWidget);
expect(find.text(S.current.ok), findsOneWidget);
});
});
}

View File

@@ -0,0 +1,219 @@
import 'dart:async';
import 'dart:io';
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/presentation/widgets/chat_bubble_widget.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
class _FakeHttpClient implements HttpClient {
bool _autoUncompress = true;
@override
bool get autoUncompress => _autoUncompress;
@override
set autoUncompress(bool value) {
_autoUncompress = value;
}
@override
Future<HttpClientRequest> getUrl(Uri url) async => _FakeHttpClientRequest();
@override
dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
class _FakeHttpClientRequest implements HttpClientRequest {
@override
Future<HttpClientResponse> close() async => _FakeHttpClientResponse();
@override
dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
class _FakeHttpClientResponse extends Stream<List<int>> implements HttpClientResponse {
static final List<int> _png = <int>[
0x89,
0x50,
0x4E,
0x47,
0x0D,
0x0A,
0x1A,
0x0A,
0x00,
0x00,
0x00,
0x0D,
0x49,
0x48,
0x44,
0x52,
0x00,
0x00,
0x00,
0x01,
0x00,
0x00,
0x00,
0x01,
0x08,
0x06,
0x00,
0x00,
0x00,
0x1F,
0x15,
0xC4,
0x89,
0x00,
0x00,
0x00,
0x0A,
0x49,
0x44,
0x41,
0x54,
0x78,
0x9C,
0x63,
0x00,
0x01,
0x00,
0x00,
0x05,
0x00,
0x01,
0x0D,
0x0A,
0x2D,
0xB4,
0x00,
0x00,
0x00,
0x00,
0x49,
0x45,
0x4E,
0x44,
0xAE,
0x42,
0x60,
0x82,
];
@override
int get statusCode => 200;
@override
int get contentLength => _png.length;
@override
HttpClientResponseCompressionState get compressionState => HttpClientResponseCompressionState.notCompressed;
@override
StreamSubscription<List<int>> listen(void Function(List<int>)? onData, {Function? onError, void Function()? onDone, bool? cancelOnError}) {
return Stream<List<int>>.fromIterable([_png]).listen(onData, onError: onError, onDone: onDone, cancelOnError: cancelOnError);
}
@override
dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
class _FakeAssetBundle extends CachingAssetBundle {
_FakeAssetBundle(this.assets);
final Map<String, String> assets;
@override
Future<String> loadString(String key, {bool cache = true}) async {
final value = assets[key];
if (value == null) throw FlutterError('Asset not found: $key');
return value;
}
@override
Future<ByteData> load(String key) async {
final value = assets[key];
if (value == null) throw FlutterError('Asset not found: $key');
return ByteData.sublistView(Uint8List.fromList(value.codeUnits));
}
}
void main() {
group('ChatBubbleWidget smoke', () {
const fakeSvg = '''
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="10" viewBox="0 0 10 10">
<rect width="10" height="10" fill="red"/>
</svg>
''';
final bundle = _FakeAssetBundle({'assets/icons/error_icon.svg': fakeSvg});
Widget wrap(Widget child) {
return DefaultAssetBundle(
bundle: bundle,
child: MaterialApp(
theme: ThemeHelper().themeData(),
home: MediaQuery(
data: const MediaQueryData(size: Size(1200, 800)),
child: Scaffold(body: child),
),
),
);
}
testWidgets('renders with network image attachment', (tester) async {
tester.view.devicePixelRatio = 1.0;
tester.view.physicalSize = const Size(2000, 1200);
addTearDown(tester.view.reset);
await HttpOverrides.runZoned(() async {
await tester.pumpWidget(
wrap(
ChatBubbleWidget(
sendTime: DateTime(2025, 1, 1, 10, 0),
text: 'text',
isCurrentUser: true,
isRead: true,
mediaData: const ['https://example.com/a.png'],
messageState: MessageState.sent,
messageUuid: 'u',
userSlug: 'slug',
),
),
);
await tester.pump(const Duration(milliseconds: 200));
expect(find.byType(ChatBubbleWidget), findsOneWidget);
expect(find.text('text'), findsOneWidget);
}, createHttpClient: (_) => _FakeHttpClient());
});
testWidgets('renders error state icon for current user', (tester) async {
tester.view.devicePixelRatio = 1.0;
tester.view.physicalSize = const Size(2000, 1200);
addTearDown(tester.view.reset);
await tester.pumpWidget(
wrap(
ChatBubbleWidget(
sendTime: DateTime(2025, 1, 1, 10, 0),
text: 'tap to retry',
isCurrentUser: true,
isRead: true,
mediaData: const [],
messageState: MessageState.error,
messageUuid: 'u',
userSlug: 'slug',
),
),
);
await tester.pump();
expect(find.byType(ChatBubbleWidget), findsOneWidget);
expect(find.text('tap to retry'), findsOneWidget);
});
});
}

View File

@@ -0,0 +1,95 @@
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/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/features/chat/presentation/widgets/chat_search_bar_widget.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.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() {
group('Chat list widgets smoke', () {
setUpAll(() async {
dotenv.loadFromString(envString: 'DEV_BASE_URL=http://localhost\nBASE_URL=http://localhost');
});
setUp(() async {
await sl.reset();
sl.registerLazySingleton<AppConfig>(() => AppConfig(buildType: AppBuildType.tests)..setBaseUrl('http://localhost'));
});
Widget wrap(Widget child) {
final router = GoRouter(
routes: [
GoRoute(
path: '/',
builder: (_, _) => Scaffold(body: child),
),
GoRoute(path: '/chat', name: 'chatScreen', builder: (_, _) => const SizedBox.shrink()),
],
);
return MaterialApp.router(
theme: ThemeHelper().themeData(),
localizationsDelegates: const [
S.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: const [Locale('ru')],
routerConfig: router,
);
}
testWidgets('ChatSearchBarWidget clears text and calls onSuffixIconTap', (tester) async {
final controller = TextEditingController(text: 'abc');
var called = 0;
await tester.pumpWidget(wrap(ChatSearchBarWidget(controller: controller, hintText: 'Search', onSuffixIconTap: () => called++)));
await tester.pump();
expect(find.byType(TextFormField), findsOneWidget);
await tester.tap(find.byIcon(Icons.clear));
await tester.pump();
expect(controller.text, '');
expect(called, 1);
});
testWidgets('ChatEntryWidget renders unread badge and attachment preview', (tester) async {
final chatRoom = ChatRoom(
id: '1',
lastMessage: ChatMessageModel(
id: 1,
data: 'ignored',
mediaData: const ['chat_files/file.pdf'],
authorId: 2,
updatedAt: DateTime(2025, 1, 1),
isRead: false,
isCurrentUserAuthor: false,
roomId: '1',
),
users: const Users(
fromChatUser: ChatUser(id: 1, name: 'Me', profilePhotoPath: null, profilePhotoUrl: 'http://localhost/a.png'),
toChatUser: ChatUser(id: 2, name: 'Other User', profilePhotoPath: null, profilePhotoUrl: 'http://localhost/b.png'),
),
unreadMessagesCount: 3,
chosenPlan: null,
);
await mockNetworkImagesFor(() async {
await tester.pumpWidget(wrap(ChatEntryWidget(chatRoom: chatRoom)));
await tester.pump(const Duration(milliseconds: 200));
expect(find.text('Other User'), findsOneWidget);
expect(find.text('3'), findsOneWidget);
expect(find.text('📎 Вложение'), findsOneWidget);
});
});
});
}

View File

@@ -0,0 +1,105 @@
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/features/chat/presentation/widgets/about_order_button.dart';
import 'package:eatvio_app/features/chat/presentation/widgets/message_action_bottom_sheet.dart';
import 'package:eatvio_app/features/chat/presentation/widgets/notification_banner.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_test/flutter_test.dart';
class _FakeAssetBundle extends CachingAssetBundle {
_FakeAssetBundle(this.assets);
final Map<String, String> assets;
@override
Future<String> loadString(String key, {bool cache = true}) async {
final value = assets[key];
if (value == null) throw FlutterError('Asset not found: $key');
return value;
}
@override
Future<ByteData> load(String key) async {
final value = assets[key];
if (value == null) throw FlutterError('Asset not found: $key');
return ByteData.sublistView(Uint8List.fromList(value.codeUnits));
}
}
void main() {
group('Chat misc widgets smoke', () {
const fakeSvg = '''
<svg xmlns="http://www.w3.org/2000/svg" width="10" height="10" viewBox="0 0 10 10">
<rect width="10" height="10" fill="red"/>
</svg>
''';
final bundle = _FakeAssetBundle({
'assets/icons/right_arrow.svg': fakeSvg,
'assets/icons/resend.svg': fakeSvg,
'assets/icons/trash_can.svg': fakeSvg,
'assets/icons/reversed_exclamation_mark.svg': fakeSvg,
'assets/icons/star.svg': fakeSvg,
});
Widget wrap(Widget child) {
return DefaultAssetBundle(
bundle: bundle,
child: MaterialApp(
theme: ThemeHelper().themeData(),
localizationsDelegates: const [
S.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: const [Locale('ru')],
home: MediaQuery(
data: const MediaQueryData(size: Size(500, 900)),
child: Scaffold(body: child),
),
),
);
}
testWidgets('AboutOrderButton renders and taps', (tester) async {
var tapped = false;
await tester.pumpWidget(wrap(AboutOrderButton(onTap: () => tapped = true)));
await tester.pump();
expect(find.byType(AboutOrderButton), findsOneWidget);
await tester.tap(find.byType(AboutOrderButton));
await tester.pump();
expect(tapped, isTrue);
});
testWidgets('MessageActionsBottomSheet renders', (tester) async {
await tester.pumpWidget(wrap(MessageActionsBottomSheet(resend: () {}, delete: () {})));
await tester.pump();
expect(find.byType(MessageActionsBottomSheet), findsOneWidget);
expect(find.text(S.current.resend), findsOneWidget);
expect(find.text(S.current.delete), findsOneWidget);
});
testWidgets('NotificationBanner renders for multiple banner types', (tester) async {
await tester.pumpWidget(
wrap(
const SingleChildScrollView(
child: Column(
children: [
NotificationBanner(bannerType: BannerType.date, hasPersonalDiet: false),
NotificationBanner(bannerType: BannerType.card),
NotificationBanner(bannerType: BannerType.collectCharge, planName: 'Plan', planPrice: 100, chosenPlanId: 1),
NotificationBanner(bannerType: BannerType.finishPlan, planName: 'Plan', planPrice: 100, chosenPlanId: 1),
NotificationBanner(bannerType: BannerType.setPersonalDiet, hasPersonalDiet: true),
],
),
),
),
);
await tester.pump();
expect(find.byType(NotificationBanner), findsWidgets);
});
});
}

View File

@@ -0,0 +1,204 @@
import 'dart:async';
import 'dart:io';
import 'package:eatvio_app/core/app_config.dart';
import 'package:eatvio_app/core/app_initializer.dart';
import 'package:eatvio_app/core/locator_service.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/features/chat/presentation/widgets/order_card.dart';
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
import 'package:eatvio_app/features/feed/models/plan_pagination_model.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_test/flutter_test.dart';
class _FakeHttpClient implements HttpClient {
bool _autoUncompress = true;
@override
bool get autoUncompress => _autoUncompress;
@override
set autoUncompress(bool value) {
_autoUncompress = value;
}
@override
Future<HttpClientRequest> getUrl(Uri url) async => _FakeHttpClientRequest();
@override
dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
class _FakeHttpClientRequest implements HttpClientRequest {
@override
Future<HttpClientResponse> close() async => _FakeHttpClientResponse();
@override
dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
class _FakeHttpClientResponse extends Stream<List<int>> implements HttpClientResponse {
static final List<int> _png = <int>[
0x89,
0x50,
0x4E,
0x47,
0x0D,
0x0A,
0x1A,
0x0A,
0x00,
0x00,
0x00,
0x0D,
0x49,
0x48,
0x44,
0x52,
0x00,
0x00,
0x00,
0x01,
0x00,
0x00,
0x00,
0x01,
0x08,
0x06,
0x00,
0x00,
0x00,
0x1F,
0x15,
0xC4,
0x89,
0x00,
0x00,
0x00,
0x0A,
0x49,
0x44,
0x41,
0x54,
0x78,
0x9C,
0x63,
0x00,
0x01,
0x00,
0x00,
0x05,
0x00,
0x01,
0x0D,
0x0A,
0x2D,
0xB4,
0x00,
0x00,
0x00,
0x00,
0x49,
0x45,
0x4E,
0x44,
0xAE,
0x42,
0x60,
0x82,
];
@override
int get statusCode => 200;
@override
int get contentLength => _png.length;
@override
HttpClientResponseCompressionState get compressionState => HttpClientResponseCompressionState.notCompressed;
@override
StreamSubscription<List<int>> listen(void Function(List<int>)? onData, {Function? onError, void Function()? onDone, bool? cancelOnError}) {
return Stream<List<int>>.fromIterable([_png]).listen(onData, onError: onError, onDone: onDone, cancelOnError: cancelOnError);
}
@override
dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
void main() {
group('OrderCard smoke', () {
setUpAll(() async {
dotenv.loadFromString(envString: 'DEV_BASE_URL=http://localhost\nBASE_URL=http://localhost');
});
setUp(() async {
await sl.reset();
sl.registerLazySingleton<AppConfig>(() => AppConfig(buildType: AppBuildType.tests)..setBaseUrl('http://localhost'));
});
Widget wrap(Widget child) {
return MaterialApp(
theme: ThemeHelper().themeData(),
localizationsDelegates: const [
S.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: const [Locale('ru')],
home: Scaffold(body: SingleChildScrollView(child: child)),
);
}
const author = Author(
authorSlug: 'a',
authorName: 'Author',
authorProfileIcon: 'http://localhost/a.png',
authorIsMe: false,
isEntitySubscribedByMe: false,
authorPlaceInRating: 1,
);
const plan = PlanModel(
id: 1,
slug: 'slug',
name: 'Plan',
image: 'img.png',
duration: 7,
state: 'active',
type: PlanType(fit: false, slim: false, behavior: false, wellness: false),
details: '',
packetsCount: 1,
packet1Options: PacketOptions(items: ['a'], price: 100, short: 'S', description: '', hasPersonalDiet: false),
packet2Options: null,
packet3Options: null,
author: author,
totalLikes: 0,
totalViews: 0,
totalComments: 0,
isFavorite: false,
minPriceOfOptions: 100,
maxPriceOfOptions: 100,
wasLikedByMe: false,
);
testWidgets('renders and runs cancel action', (tester) async {
var cancelled = 0;
await HttpOverrides.runZoned(() async {
await tester.pumpWidget(wrap(OrderCard(plan: plan, isCustomerView: true, optionType: 'options1', onCancel: () async => cancelled++)));
await tester.pump(const Duration(milliseconds: 200));
expect(find.text('Plan'), findsOneWidget);
expect(find.text(S.current.cancel_order), findsOneWidget);
await tester.tap(find.text(S.current.cancel_order));
await tester.pump();
await tester.pump(const Duration(milliseconds: 50));
expect(cancelled, 1);
}, createHttpClient: (_) => _FakeHttpClient());
});
});
}