diff --git a/test/core/widgets/add_item_button_test.dart b/test/core/widgets/add_item_button_test.dart index a8a9f928..720f4715 100644 --- a/test/core/widgets/add_item_button_test.dart +++ b/test/core/widgets/add_item_button_test.dart @@ -26,6 +26,26 @@ void main() { await tester.pump(); expect(find.byType(AddItemButton), findsOneWidget); + expect(find.byIcon(Icons.add_rounded), findsOneWidget); + }); + + testWidgets('calls onTap when provided', (tester) async { + bool tapped = false; + await tester.pumpWidget( + wrap( + AddItemButton( + onTap: () { + tapped = true; + }, + ), + ), + ); + await tester.pump(); + + await tester.tap(find.byType(AddItemButton)); + await tester.pump(); + + expect(tapped, true); }); }); } diff --git a/test/core/widgets/animated_option_button_test.dart b/test/core/widgets/animated_option_button_test.dart index 0f2efb9b..d5f27e02 100644 --- a/test/core/widgets/animated_option_button_test.dart +++ b/test/core/widgets/animated_option_button_test.dart @@ -27,5 +27,43 @@ void main() { expect(find.byType(AnimatedOptionButton), findsOneWidget); expect(find.text('Option 2'), findsOneWidget); }); + + testWidgets('renders animated option button with custom text style', (tester) async { + await tester.pumpWidget( + wrap( + AnimatedOptionButton( + text: 'Option 3', + isSelected: false, + onTap: () {}, + textStyle: const TextStyle(fontSize: 18), + ), + ), + ); + await tester.pump(); + + expect(find.byType(AnimatedOptionButton), findsOneWidget); + expect(find.text('Option 3'), findsOneWidget); + }); + + testWidgets('calls onTap when button is tapped', (tester) async { + bool tapped = false; + await tester.pumpWidget( + wrap( + AnimatedOptionButton( + text: 'Option 4', + isSelected: false, + onTap: () { + tapped = true; + }, + ), + ), + ); + await tester.pump(); + + await tester.tap(find.byType(AnimatedOptionButton)); + await tester.pump(); + + expect(tapped, true); + }); }); } diff --git a/test/core/widgets/bookmark_button_test.dart b/test/core/widgets/bookmark_button_test.dart index 20428a2f..e44383ad 100644 --- a/test/core/widgets/bookmark_button_test.dart +++ b/test/core/widgets/bookmark_button_test.dart @@ -24,6 +24,69 @@ void main() { await tester.pump(); expect(find.byType(BookmarkButton), findsOneWidget); + expect(find.byIcon(Icons.bookmark), findsOneWidget); + }); + + testWidgets('renders bookmark button with custom shape', (tester) async { + await tester.pumpWidget( + wrap( + BookmarkButton( + onTap: () {}, + color: Colors.white, + isFavorite: false, + width: 40, + height: 40, + size: 20, + shape: BoxShape.circle, + ), + ), + ); + await tester.pump(); + + expect(find.byType(BookmarkButton), findsOneWidget); + }); + + testWidgets('renders bookmark button with borderRadius', (tester) async { + await tester.pumpWidget( + wrap( + BookmarkButton( + onTap: () {}, + color: Colors.white, + isFavorite: false, + width: 40, + height: 40, + size: 20, + borderRadius: BorderRadius.circular(8), + ), + ), + ); + await tester.pump(); + + expect(find.byType(BookmarkButton), findsOneWidget); + }); + + testWidgets('calls onTap when button is tapped', (tester) async { + bool tapped = false; + await tester.pumpWidget( + wrap( + BookmarkButton( + onTap: () { + tapped = true; + }, + color: Colors.white, + isFavorite: false, + width: 40, + height: 40, + size: 20, + ), + ), + ); + await tester.pump(); + + await tester.tap(find.byType(BookmarkButton)); + await tester.pump(); + + expect(tapped, true); }); }); } diff --git a/test/core/widgets/bottom_sheet_action_widget_test.dart b/test/core/widgets/bottom_sheet_action_widget_test.dart index 4a8eab77..af9a861f 100644 --- a/test/core/widgets/bottom_sheet_action_widget_test.dart +++ b/test/core/widgets/bottom_sheet_action_widget_test.dart @@ -45,5 +45,44 @@ void main() { expect(find.byType(BottomSheetActionWidget), findsOneWidget); expect(find.text('Test Action'), findsOneWidget); }); + + testWidgets('renders bottom sheet action widget with custom color', (tester) async { + await tester.pumpWidget( + wrap( + BottomSheetActionWidget( + text: 'Test Action', + iconPath: 'assets/icons/ok.svg', + onTap: () {}, + color: Colors.red, + ), + ), + ); + await tester.pump(); + + expect(find.byType(BottomSheetActionWidget), findsOneWidget); + expect(find.text('Test Action'), findsOneWidget); + }); + + testWidgets('calls onTap when widget is tapped', (tester) async { + bool tapped = false; + await tester.pumpWidget( + wrap( + BottomSheetActionWidget( + text: 'Test Action', + iconPath: 'assets/icons/ok.svg', + onTap: () { + tapped = true; + }, + needToPop: false, + ), + ), + ); + await tester.pump(); + + await tester.tap(find.byType(BottomSheetActionWidget)); + await tester.pump(); + + expect(tapped, true); + }); }); } diff --git a/test/core/widgets/bottom_sheet_handler_bar_test.dart b/test/core/widgets/bottom_sheet_handler_bar_test.dart index 575e8192..7feff6da 100644 --- a/test/core/widgets/bottom_sheet_handler_bar_test.dart +++ b/test/core/widgets/bottom_sheet_handler_bar_test.dart @@ -17,6 +17,16 @@ void main() { await tester.pump(); expect(find.byType(BottomSheetHandlerBar), findsOneWidget); + expect(find.byType(Container), findsWidgets); + }); + + testWidgets('renders handler bar with correct dimensions', (tester) async { + await tester.pumpWidget(wrap(const BottomSheetHandlerBar())); + await tester.pump(); + + final container = tester.widget(find.byType(Container).first); + final boxDecoration = container.decoration as BoxDecoration?; + expect(boxDecoration?.borderRadius, isNotNull); }); }); } diff --git a/test/core/widgets/cpfc_container_test.dart b/test/core/widgets/cpfc_container_test.dart index 5f2aa2a4..6babc283 100644 --- a/test/core/widgets/cpfc_container_test.dart +++ b/test/core/widgets/cpfc_container_test.dart @@ -27,5 +27,33 @@ void main() { expect(find.byType(CpfcContainer), findsOneWidget); }); + + testWidgets('displays calories value', (tester) async { + await tester.pumpWidget(wrap(const CpfcContainer(calories: 2000.0, proteins: 150.0, fats: 80.0, carbohydrates: 250.0))); + await tester.pump(); + + expect(find.text('2000.0'), findsOneWidget); + }); + + testWidgets('displays proteins value', (tester) async { + await tester.pumpWidget(wrap(const CpfcContainer(calories: 2000.0, proteins: 150.0, fats: 80.0, carbohydrates: 250.0))); + await tester.pump(); + + expect(find.text('150.0'), findsOneWidget); + }); + + testWidgets('displays fats value', (tester) async { + await tester.pumpWidget(wrap(const CpfcContainer(calories: 2000.0, proteins: 150.0, fats: 80.0, carbohydrates: 250.0))); + await tester.pump(); + + expect(find.text('80.0'), findsOneWidget); + }); + + testWidgets('displays carbohydrates value', (tester) async { + await tester.pumpWidget(wrap(const CpfcContainer(calories: 2000.0, proteins: 150.0, fats: 80.0, carbohydrates: 250.0))); + await tester.pump(); + + expect(find.text('250.0'), findsOneWidget); + }); }); } diff --git a/test/core/widgets/custom_divider_test.dart b/test/core/widgets/custom_divider_test.dart index 2b82352d..5bf9e0ec 100644 --- a/test/core/widgets/custom_divider_test.dart +++ b/test/core/widgets/custom_divider_test.dart @@ -17,6 +17,15 @@ void main() { await tester.pump(); expect(find.byType(CustomDivider), findsOneWidget); + expect(find.byType(Divider), findsOneWidget); + }); + + testWidgets('renders divider with correct properties', (tester) async { + await tester.pumpWidget(wrap(const CustomDivider())); + await tester.pump(); + + final divider = tester.widget(find.byType(Divider)); + expect(divider.height, 1); }); }); } diff --git a/test/core/widgets/custom_drop_down_button_test.dart b/test/core/widgets/custom_drop_down_button_test.dart index aec0d931..c456bcce 100644 --- a/test/core/widgets/custom_drop_down_button_test.dart +++ b/test/core/widgets/custom_drop_down_button_test.dart @@ -29,5 +29,34 @@ void main() { expect(find.byType(CustomDropDownButton), findsOneWidget); }); + + testWidgets('renders custom drop down button with hasError', (tester) async { + const items = [DropdownMenuItem(value: 'option1', child: Text('Option 1')), DropdownMenuItem(value: 'option2', child: Text('Option 2'))]; + + await tester.pumpWidget(wrap(const CustomDropDownButton(items: items, hint: Text('Select option'), hasError: true, onChanged: null))); + await tester.pump(); + + expect(find.byType(CustomDropDownButton), findsOneWidget); + }); + + testWidgets('renders custom drop down button with isExpanded', (tester) async { + const items = [DropdownMenuItem(value: 'option1', child: Text('Option 1')), DropdownMenuItem(value: 'option2', child: Text('Option 2'))]; + + await tester.pumpWidget(wrap(const CustomDropDownButton(items: items, hint: Text('Select option'), isExpanded: true, onChanged: null))); + await tester.pump(); + + expect(find.byType(CustomDropDownButton), findsOneWidget); + }); + + testWidgets('calls onChanged when value is selected', (tester) async { + const items = [DropdownMenuItem(value: 'option1', child: Text('Option 1')), DropdownMenuItem(value: 'option2', child: Text('Option 2'))]; + + await tester.pumpWidget(wrap(CustomDropDownButton(items: items, hint: const Text('Select option'), onChanged: (value) {}))); + await tester.pump(); + + // Note: Testing dropdown selection in Flutter tests is complex + // This test verifies the widget renders correctly + expect(find.byType(CustomDropDownButton), findsOneWidget); + }); }); } diff --git a/test/core/widgets/custom_drop_down_test.dart b/test/core/widgets/custom_drop_down_test.dart index 6a8ec239..50759f01 100644 --- a/test/core/widgets/custom_drop_down_test.dart +++ b/test/core/widgets/custom_drop_down_test.dart @@ -32,5 +32,24 @@ void main() { expect(find.byType(CustomDropDown), findsOneWidget); }); + + testWidgets('renders custom drop down with alignment', (tester) async { + const items = [SelectionPopupModel(id: 1, title: 'Option 1'), SelectionPopupModel(id: 2, title: 'Option 2')]; + + await tester.pumpWidget(wrap(const CustomDropDown(items: items, hintText: 'Select option', alignment: Alignment.centerLeft))); + await tester.pump(); + + expect(find.byType(CustomDropDown), findsOneWidget); + }); + + testWidgets('renders custom drop down with prefix', (tester) async { + const items = [SelectionPopupModel(id: 1, title: 'Option 1'), SelectionPopupModel(id: 2, title: 'Option 2')]; + + await tester.pumpWidget(wrap(const CustomDropDown(items: items, hintText: 'Select option', prefix: Icon(Icons.search)))); + await tester.pump(); + + expect(find.byType(CustomDropDown), findsOneWidget); + expect(find.byIcon(Icons.search), findsOneWidget); + }); }); } diff --git a/test/core/widgets/custom_outlined_button_test.dart b/test/core/widgets/custom_outlined_button_test.dart index 3594d092..b2769b88 100644 --- a/test/core/widgets/custom_outlined_button_test.dart +++ b/test/core/widgets/custom_outlined_button_test.dart @@ -34,5 +34,40 @@ void main() { expect(find.byType(CustomOutlinedButton), findsOneWidget); }); + + testWidgets('renders custom outlined button with right icon', (tester) async { + await tester.pumpWidget(wrap(const CustomOutlinedButton(text: 'Test Button', rightIcon: Icon(Icons.arrow_forward)))); + await tester.pump(); + + expect(find.byType(CustomOutlinedButton), findsOneWidget); + expect(find.byIcon(Icons.arrow_forward), findsOneWidget); + }); + + testWidgets('renders custom outlined button with custom alignment', (tester) async { + await tester.pumpWidget(wrap(const CustomOutlinedButton(text: 'Test Button', alignment: Alignment.centerLeft))); + await tester.pump(); + + expect(find.byType(CustomOutlinedButton), findsOneWidget); + }); + + testWidgets('calls onPressed when button is tapped', (tester) async { + bool pressed = false; + await tester.pumpWidget( + wrap( + CustomOutlinedButton( + text: 'Test Button', + onPressed: () { + pressed = true; + }, + ), + ), + ); + await tester.pump(); + + await tester.tap(find.text('Test Button')); + await tester.pump(); + + expect(pressed, true); + }); }); } diff --git a/test/core/widgets/custom_popup_test.dart b/test/core/widgets/custom_popup_test.dart index 19aa5222..91a91128 100644 --- a/test/core/widgets/custom_popup_test.dart +++ b/test/core/widgets/custom_popup_test.dart @@ -86,5 +86,32 @@ void main() { expect(find.byType(CustomPopup), findsOneWidget); expect(find.byIcon(Icons.more_horiz_rounded), findsOneWidget); }); + + testWidgets('renders custom popup for author with recipe', (tester) async { + await tester.pumpWidget( + wrap(const CustomPopup(isAuthor: true, path: 'test/path', userSlug: 'test-user', complainedContentType: ComplainedContent.recipe, slug: 'test-recipe')), + ); + await tester.pump(); + expect(find.byType(CustomPopup), findsOneWidget); + expect(find.byIcon(Icons.more_horiz_rounded), findsOneWidget); + }); + + testWidgets('renders custom popup for author with diet', (tester) async { + await tester.pumpWidget( + wrap(const CustomPopup(isAuthor: true, path: 'test/path', userSlug: 'test-user', complainedContentType: ComplainedContent.diet, id: 1)), + ); + await tester.pump(); + expect(find.byType(CustomPopup), findsOneWidget); + expect(find.byIcon(Icons.more_horiz_rounded), findsOneWidget); + }); + + testWidgets('renders custom popup for author with plan', (tester) async { + await tester.pumpWidget( + wrap(const CustomPopup(isAuthor: true, path: 'test/path', userSlug: 'test-user', complainedContentType: ComplainedContent.plan, slug: 'test-plan')), + ); + await tester.pump(); + expect(find.byType(CustomPopup), findsOneWidget); + expect(find.byIcon(Icons.more_horiz_rounded), findsOneWidget); + }); }); } diff --git a/test/core/widgets/custom_text_form_field_test.dart b/test/core/widgets/custom_text_form_field_test.dart index 470159e6..cda05148 100644 --- a/test/core/widgets/custom_text_form_field_test.dart +++ b/test/core/widgets/custom_text_form_field_test.dart @@ -43,5 +43,55 @@ void main() { expect(find.byType(CustomTextFormField), findsOneWidget); }); + + testWidgets('renders custom text form field with prefix', (tester) async { + await tester.pumpWidget(wrap(const CustomTextFormField(hintText: 'Enter text', prefix: Icon(Icons.search)))); + await tester.pump(); + + expect(find.byType(CustomTextFormField), findsOneWidget); + expect(find.byIcon(Icons.search), findsOneWidget); + }); + + testWidgets('renders custom text form field with suffix', (tester) async { + await tester.pumpWidget(wrap(const CustomTextFormField(hintText: 'Enter text', suffix: Icon(Icons.clear)))); + await tester.pump(); + + expect(find.byType(CustomTextFormField), findsOneWidget); + expect(find.byIcon(Icons.clear), findsOneWidget); + }); + + testWidgets('renders custom text form field with hasError', (tester) async { + await tester.pumpWidget(wrap(const CustomTextFormField(hintText: 'Enter text', hasError: true))); + await tester.pump(); + + expect(find.byType(CustomTextFormField), findsOneWidget); + }); + + testWidgets('renders custom text form field with readOnly', (tester) async { + await tester.pumpWidget(wrap(const CustomTextFormField(hintText: 'Enter text', readOnly: true))); + await tester.pump(); + + expect(find.byType(CustomTextFormField), findsOneWidget); + }); + + testWidgets('calls onChanged when text is entered', (tester) async { + String? changedValue; + await tester.pumpWidget( + wrap( + CustomTextFormField( + hintText: 'Enter text', + onChanged: (value) { + changedValue = value; + }, + ), + ), + ); + await tester.pump(); + + await tester.enterText(find.byType(CustomTextFormField), 'test'); + await tester.pump(); + + expect(changedValue, 'test'); + }); }); } diff --git a/test/core/widgets/data_error_widget_test.dart b/test/core/widgets/data_error_widget_test.dart index cccfa4bd..d5e6fde6 100644 --- a/test/core/widgets/data_error_widget_test.dart +++ b/test/core/widgets/data_error_widget_test.dart @@ -27,5 +27,33 @@ void main() { expect(find.byType(DataErrorWidget), findsOneWidget); }); + + testWidgets('displays error message and reload button', (tester) async { + await tester.pumpWidget(wrap(DataErrorWidget(onReload: () async {}))); + await tester.pump(); + + expect(find.byType(DataErrorWidget), findsOneWidget); + expect(find.textContaining('Что-то пошло не так'), findsWidgets); + }); + + testWidgets('calls onReload when button is tapped', (tester) async { + bool reloadCalled = false; + await tester.pumpWidget( + wrap( + DataErrorWidget( + onReload: () async { + reloadCalled = true; + }, + ), + ), + ); + await tester.pump(); + + final button = find.textContaining('Попробовать'); + await tester.tap(button); + await tester.pump(); + + expect(reloadCalled, true); + }); }); } diff --git a/test/core/widgets/feed_icon_button_test.dart b/test/core/widgets/feed_icon_button_test.dart index 2a0dcc59..a978d554 100644 --- a/test/core/widgets/feed_icon_button_test.dart +++ b/test/core/widgets/feed_icon_button_test.dart @@ -31,5 +31,42 @@ void main() { expect(find.byIcon(Icons.star), findsOneWidget); expect(find.text('Star'), findsOneWidget); }); + + testWidgets('renders feed icon button with custom colors', (tester) async { + await tester.pumpWidget( + wrap( + FeedIconButton( + icon: Icons.favorite, + text: 'Like', + onTap: () {}, + iconColor: Colors.red, + backgroundColor: Colors.pink, + textColor: Colors.red, + ), + ), + ); + await tester.pump(); + expect(find.byType(FeedIconButton), findsOneWidget); + }); + + testWidgets('calls onTap when button is tapped', (tester) async { + bool tapped = false; + await tester.pumpWidget( + wrap( + FeedIconButton( + icon: Icons.favorite, + onTap: () { + tapped = true; + }, + ), + ), + ); + await tester.pump(); + + await tester.tap(find.byType(FeedIconButton)); + await tester.pump(); + + expect(tapped, true); + }); }); } diff --git a/test/core/widgets/info_row_test.dart b/test/core/widgets/info_row_test.dart index 064052c7..318c4280 100644 --- a/test/core/widgets/info_row_test.dart +++ b/test/core/widgets/info_row_test.dart @@ -2,6 +2,7 @@ import 'package:eatvio_app/core/enums/product_type.dart'; import 'package:eatvio_app/core/theme/theme_helper.dart'; import 'package:eatvio_app/core/widgets/info_row.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/features/feed/models/recipes_model.dart'; import 'package:eatvio_app/generated/l10n.dart'; import 'package:flutter/material.dart'; @@ -97,5 +98,39 @@ void main() { expect(find.byType(InfoRow), findsOneWidget); }); + + testWidgets('renders info row for plan', (tester) async { + const plan = PlanModel( + id: 1, + slug: 'test-plan', + name: 'Test Plan', + image: '/test.jpg', + duration: 14, + state: 'active', + type: PlanType(slim: true, fit: false, wellness: false, behavior: false), + details: 'Test plan description', + packetsCount: 1, + author: Author( + authorSlug: 'test-author', + authorName: 'Test Author', + authorProfileIcon: '/test.jpg', + authorIsMe: false, + isEntitySubscribedByMe: false, + authorPlaceInRating: null, + ), + totalLikes: 10, + totalViews: 100, + totalComments: 5, + wasLikedByMe: false, + isFavorite: false, + minPriceOfOptions: 1000, + maxPriceOfOptions: 2000, + ); + + await tester.pumpWidget(wrap(const InfoRow(type: ProductType.plan, plan: plan))); + await tester.pump(); + + expect(find.byType(InfoRow), findsOneWidget); + }); }); } diff --git a/test/core/widgets/item_widget_test.dart b/test/core/widgets/item_widget_test.dart index f22225dd..64f00960 100644 --- a/test/core/widgets/item_widget_test.dart +++ b/test/core/widgets/item_widget_test.dart @@ -40,5 +40,21 @@ void main() { expect(find.byType(ItemWidget), findsOneWidget); expect(find.text('Test Item'), findsOneWidget); }); + + testWidgets('renders item widget with custom icon size', (tester) async { + await tester.pumpWidget(wrap(const ItemWidget(text: 'Test Item', iconSize: 30))); + await tester.pumpAndSettle(); + + expect(find.byType(ItemWidget), findsOneWidget); + expect(find.text('Test Item'), findsOneWidget); + }); + + testWidgets('renders item widget reversed with custom icon size', (tester) async { + await tester.pumpWidget(wrap(const ItemWidget(text: 'Test Item', reversed: true, iconSize: 25))); + await tester.pumpAndSettle(); + + expect(find.byType(ItemWidget), findsOneWidget); + expect(find.text('Test Item'), findsOneWidget); + }); }); } diff --git a/test/core/widgets/loader_test.dart b/test/core/widgets/loader_test.dart index 6de1bbcf..16132361 100644 --- a/test/core/widgets/loader_test.dart +++ b/test/core/widgets/loader_test.dart @@ -17,6 +17,17 @@ void main() { await tester.pump(); expect(find.byType(Loader), findsOneWidget); + expect(find.byType(Center), findsOneWidget); + expect(find.byType(SizedBox), findsWidgets); + }); + + testWidgets('renders loader with correct dimensions', (tester) async { + await tester.pumpWidget(wrap(const Loader())); + await tester.pump(); + + final sizedBox = tester.widget(find.byType(SizedBox).first); + expect(sizedBox.height, 42); + expect(sizedBox.width, 42); }); }); } diff --git a/test/core/widgets/main_background_bouble_test.dart b/test/core/widgets/main_background_bouble_test.dart index 8d4c5653..25bac88d 100644 --- a/test/core/widgets/main_background_bouble_test.dart +++ b/test/core/widgets/main_background_bouble_test.dart @@ -33,5 +33,19 @@ void main() { expect(find.byType(MainBackgroundBouble), findsOneWidget); }); + + testWidgets('renders main background bouble with custom width', (tester) async { + await tester.pumpWidget(wrap(const MainBackgroundBouble(width: 200.0, child: Text('Test')))); + await tester.pump(); + + expect(find.byType(MainBackgroundBouble), findsOneWidget); + }); + + testWidgets('renders main background bouble with custom padding', (tester) async { + await tester.pumpWidget(wrap(const MainBackgroundBouble(padding: EdgeInsets.all(16), child: Text('Test')))); + await tester.pump(); + + expect(find.byType(MainBackgroundBouble), findsOneWidget); + }); }); } diff --git a/test/core/widgets/markdown_html_widget_test.dart b/test/core/widgets/markdown_html_widget_test.dart new file mode 100644 index 00000000..c7654a3f --- /dev/null +++ b/test/core/widgets/markdown_html_widget_test.dart @@ -0,0 +1,43 @@ +import 'package:eatvio_app/core/theme/theme_helper.dart'; +import 'package:eatvio_app/core/widgets/markdown_html_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('MarkdownHtmlWidget smoke', () { + Widget wrap(Widget child) { + return MaterialApp( + theme: ThemeHelper().themeData(), + home: Scaffold(body: child), + ); + } + + testWidgets('renders markdown html widget', (tester) async { + await tester.pumpWidget(wrap(const MarkdownHtmlWidget(markdownText: '# Test'))); + await tester.pump(); + + expect(find.byType(MarkdownHtmlWidget), findsOneWidget); + }); + + testWidgets('renders markdown html widget with custom image width', (tester) async { + await tester.pumpWidget(wrap(const MarkdownHtmlWidget(markdownText: '# Test', imageWidth: 200.0))); + await tester.pump(); + + expect(find.byType(MarkdownHtmlWidget), findsOneWidget); + }); + + testWidgets('renders markdown html widget with custom text color', (tester) async { + await tester.pumpWidget(wrap(const MarkdownHtmlWidget(markdownText: '# Test', textColor: Colors.red))); + await tester.pump(); + + expect(find.byType(MarkdownHtmlWidget), findsOneWidget); + }); + + testWidgets('renders markdown html widget with custom font size', (tester) async { + await tester.pumpWidget(wrap(const MarkdownHtmlWidget(markdownText: '# Test', fontSize: 18.0))); + await tester.pump(); + + expect(find.byType(MarkdownHtmlWidget), findsOneWidget); + }); + }); +} diff --git a/test/core/widgets/option_widget_test.dart b/test/core/widgets/option_widget_test.dart index 6a70eb22..a4a9a8f1 100644 --- a/test/core/widgets/option_widget_test.dart +++ b/test/core/widgets/option_widget_test.dart @@ -1,93 +1,105 @@ -import 'package:eatvio_app/core/theme/theme_helper.dart'; -import 'package:eatvio_app/core/widgets/option_widget.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_localizations/flutter_localizations.dart'; -import 'package:flutter_test/flutter_test.dart'; - -void main() { - group('OptionWidget', () { - Widget createTestWidget(Widget child) { - return MaterialApp( - theme: ThemeHelper().themeData(), - localizationsDelegates: const [ - S.delegate, - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - GlobalCupertinoLocalizations.delegate, - ], - supportedLocales: S.delegate.supportedLocales, - home: Scaffold(body: child), - ); - } - - testWidgets('returns SizedBox.shrink when option is null', (tester) async { - await tester.pumpWidget(createTestWidget(const OptionWidget(option: null, duration: 7))); - await tester.pumpAndSettle(); - - expect(find.byType(OptionWidget), findsOneWidget); - expect(find.byType(SizedBox), findsWidgets); - }); - - testWidgets('displays option widget with option', (tester) async { - const option = PacketOptions(items: ['Item 1', 'Item 2'], price: 1000.0, short: 'Short', description: 'Description', hasPersonalDiet: false); - - await tester.pumpWidget(createTestWidget(const OptionWidget(option: option, duration: 7))); - await tester.pumpAndSettle(); - - expect(find.byType(OptionWidget), findsOneWidget); - expect(find.text('Short'), findsOneWidget); - expect(find.text('Description'), findsOneWidget); - }); - - testWidgets('displays option widget with personal diet', (tester) async { - const option = PacketOptions(items: ['Item 1'], price: 1000.0, short: 'Short', description: 'Description', hasPersonalDiet: true); - - await tester.pumpWidget(createTestWidget(const OptionWidget(option: option, duration: 7))); - await tester.pumpAndSettle(); - - expect(find.byType(OptionWidget), findsOneWidget); - }); - - testWidgets('hides duration when disableDuration is true', (tester) async { - const option = PacketOptions(items: ['Item 1'], price: 1000.0, short: 'Short', description: 'Description', hasPersonalDiet: false); - - await tester.pumpWidget(createTestWidget(const OptionWidget(option: option, duration: 7, disableDuration: true))); - await tester.pumpAndSettle(); - - expect(find.byType(OptionWidget), findsOneWidget); - }); - }); -} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +import 'package:eatvio_app/core/theme/theme_helper.dart'; +import 'package:eatvio_app/core/widgets/option_widget.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_localizations/flutter_localizations.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('OptionWidget', () { + Widget createTestWidget(Widget child) { + return MaterialApp( + theme: ThemeHelper().themeData(), + localizationsDelegates: const [ + S.delegate, + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + supportedLocales: S.delegate.supportedLocales, + home: Scaffold(body: child), + ); + } + + testWidgets('returns SizedBox.shrink when option is null', (tester) async { + await tester.pumpWidget(createTestWidget(const OptionWidget(option: null, duration: 7))); + await tester.pumpAndSettle(); + + expect(find.byType(OptionWidget), findsOneWidget); + expect(find.byType(SizedBox), findsWidgets); + }); + + testWidgets('displays option widget with option', (tester) async { + const option = PacketOptions(items: ['Item 1', 'Item 2'], price: 1000.0, short: 'Short', description: 'Description', hasPersonalDiet: false); + + await tester.pumpWidget(createTestWidget(const OptionWidget(option: option, duration: 7))); + await tester.pumpAndSettle(); + + expect(find.byType(OptionWidget), findsOneWidget); + expect(find.text('Short'), findsOneWidget); + expect(find.text('Description'), findsOneWidget); + }); + + testWidgets('displays option widget with personal diet', (tester) async { + const option = PacketOptions(items: ['Item 1'], price: 1000.0, short: 'Short', description: 'Description', hasPersonalDiet: true); + + await tester.pumpWidget(createTestWidget(const OptionWidget(option: option, duration: 7))); + await tester.pumpAndSettle(); + + expect(find.byType(OptionWidget), findsOneWidget); + }); + + testWidgets('hides duration when disableDuration is true', (tester) async { + const option = PacketOptions(items: ['Item 1'], price: 1000.0, short: 'Short', description: 'Description', hasPersonalDiet: false); + + await tester.pumpWidget(createTestWidget(const OptionWidget(option: option, duration: 7, disableDuration: true))); + await tester.pumpAndSettle(); + + expect(find.byType(OptionWidget), findsOneWidget); + }); + + testWidgets('displays option items', (tester) async { + const option = PacketOptions(items: ['Item 1', 'Item 2', 'Item 3'], price: 1000.0, short: 'Short', description: 'Description', hasPersonalDiet: false); + + await tester.pumpWidget(createTestWidget(const OptionWidget(option: option, duration: 7))); + await tester.pumpAndSettle(); + + expect(find.byType(OptionWidget), findsOneWidget); + expect(find.text('Item 1'), findsOneWidget); + expect(find.text('Item 2'), findsOneWidget); + expect(find.text('Item 3'), findsOneWidget); + }); + }); +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/core/widgets/success_published_diet_bottom_sheet_test.dart b/test/core/widgets/success_published_diet_bottom_sheet_test.dart index ebc4aa45..5e184ce3 100644 --- a/test/core/widgets/success_published_diet_bottom_sheet_test.dart +++ b/test/core/widgets/success_published_diet_bottom_sheet_test.dart @@ -59,5 +59,21 @@ void main() { expect(find.byType(SuccessPublishedDietOrPlanBottomSheet), findsOneWidget); }); + + testWidgets('displays title and published text', (tester) async { + await tester.pumpWidget(wrap(const SuccessPublishedDietOrPlanBottomSheet(title: 'Test Diet', isPlan: false))); + await tester.pump(); + + expect(find.text('Test Diet'), findsOneWidget); + expect(find.textContaining('опубликованна'), findsWidgets); + }); + + testWidgets('displays plan published text when isPlan is true', (tester) async { + await tester.pumpWidget(wrap(const SuccessPublishedDietOrPlanBottomSheet(title: 'Test Plan', isPlan: true))); + await tester.pump(); + + expect(find.text('Test Plan'), findsOneWidget); + expect(find.textContaining('опубликован'), findsWidgets); + }); }); } diff --git a/test/core/widgets/success_published_recipe_bottom_sheet_test.dart b/test/core/widgets/success_published_recipe_bottom_sheet_test.dart index bb8d3e46..ad14e7e3 100644 --- a/test/core/widgets/success_published_recipe_bottom_sheet_test.dart +++ b/test/core/widgets/success_published_recipe_bottom_sheet_test.dart @@ -52,5 +52,12 @@ void main() { expect(find.byType(SuccessPublishedRecipeBottomSheet), findsOneWidget); }); + + testWidgets('displays recipe title', (tester) async { + await tester.pumpWidget(wrap(const SuccessPublishedRecipeBottomSheet(title: 'My Recipe'))); + await tester.pump(); + + expect(find.textContaining('My Recipe'), findsWidgets); + }); }); } diff --git a/test/features/auth/models/oauth_verification_confirm_request_test.dart b/test/features/auth/models/oauth_verification_confirm_request_test.dart new file mode 100644 index 00000000..be2a7747 --- /dev/null +++ b/test/features/auth/models/oauth_verification_confirm_request_test.dart @@ -0,0 +1,150 @@ +import 'package:eatvio_app/features/auth/models/oauth_verification_confirm_request.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('OAuthVerificationConfirmRequest', () { + test('creates instance with required fields', () { + const request = OAuthVerificationConfirmRequest( + accessToken: 'access_token_123', + refreshToken: 'refresh_token_123', + clientId: 'client_id_123', + provider: 'vk', + id: 'user_id_123', + code: 'code_123', + verificationType: 'email', + firstName: 'John', + lastName: 'Doe', + dateOfBirth: '1990-01-01', + gender: 'male', + weight: 70, + height: 180, + ); + + expect(request.accessToken, 'access_token_123'); + expect(request.refreshToken, 'refresh_token_123'); + expect(request.clientId, 'client_id_123'); + expect(request.provider, 'vk'); + expect(request.id, 'user_id_123'); + expect(request.code, 'code_123'); + expect(request.verificationType, 'email'); + expect(request.firstName, 'John'); + expect(request.lastName, 'Doe'); + expect(request.dateOfBirth, '1990-01-01'); + expect(request.gender, 'male'); + expect(request.weight, 70); + expect(request.height, 180); + }); + + test('creates instance with optional email', () { + const request = OAuthVerificationConfirmRequest( + accessToken: 'access_token_123', + refreshToken: 'refresh_token_123', + clientId: 'client_id_123', + provider: 'vk', + id: 'user_id_123', + email: 'test@example.com', + code: 'code_123', + verificationType: 'email', + firstName: 'John', + lastName: 'Doe', + dateOfBirth: '1990-01-01', + gender: 'male', + weight: 70, + height: 180, + ); + + expect(request.email, 'test@example.com'); + }); + + test('creates instance with optional phone', () { + const request = OAuthVerificationConfirmRequest( + accessToken: 'access_token_123', + refreshToken: 'refresh_token_123', + clientId: 'client_id_123', + provider: 'vk', + id: 'user_id_123', + phone: '+79991234567', + code: 'code_123', + verificationType: 'phone', + firstName: 'John', + lastName: 'Doe', + dateOfBirth: '1990-01-01', + gender: 'male', + weight: 70, + height: 180, + ); + + expect(request.phone, '+79991234567'); + }); + + test('fromJson creates instance from JSON', () { + final json = { + 'access_token': 'access_token_123', + 'refresh_token': 'refresh_token_123', + 'client_id': 'client_id_123', + 'provider': 'vk', + 'id': 'user_id_123', + 'code': 'code_123', + 'verification_type': 'email', + 'first_name': 'John', + 'last_name': 'Doe', + 'date_of_birth': '1990-01-01', + 'gender': 'male', + 'weight': 70, + 'height': 180, + }; + + final request = OAuthVerificationConfirmRequest.fromJson(json); + + expect(request.accessToken, 'access_token_123'); + expect(request.refreshToken, 'refresh_token_123'); + expect(request.clientId, 'client_id_123'); + expect(request.provider, 'vk'); + expect(request.id, 'user_id_123'); + expect(request.code, 'code_123'); + expect(request.verificationType, 'email'); + expect(request.firstName, 'John'); + expect(request.lastName, 'Doe'); + expect(request.dateOfBirth, '1990-01-01'); + expect(request.gender, 'male'); + expect(request.weight, 70); + expect(request.height, 180); + }); + + test('toJson converts instance to JSON', () { + const request = OAuthVerificationConfirmRequest( + accessToken: 'access_token_123', + refreshToken: 'refresh_token_123', + clientId: 'client_id_123', + provider: 'vk', + id: 'user_id_123', + email: 'test@example.com', + code: 'code_123', + verificationType: 'email', + firstName: 'John', + lastName: 'Doe', + dateOfBirth: '1990-01-01', + gender: 'male', + weight: 70, + height: 180, + ); + + final json = request.toJson(); + + expect(json['access_token'], 'access_token_123'); + expect(json['refresh_token'], 'refresh_token_123'); + expect(json['client_id'], 'client_id_123'); + expect(json['provider'], 'vk'); + expect(json['id'], 'user_id_123'); + expect(json['email'], 'test@example.com'); + expect(json['code'], 'code_123'); + expect(json['verification_type'], 'email'); + expect(json['first_name'], 'John'); + expect(json['last_name'], 'Doe'); + expect(json['date_of_birth'], '1990-01-01'); + expect(json['gender'], 'male'); + expect(json['weight'], 70); + expect(json['height'], 180); + }); + }); +} diff --git a/test/features/auth/models/oauth_verification_confirm_response_test.dart b/test/features/auth/models/oauth_verification_confirm_response_test.dart new file mode 100644 index 00000000..495782ea --- /dev/null +++ b/test/features/auth/models/oauth_verification_confirm_response_test.dart @@ -0,0 +1,97 @@ +import 'package:eatvio_app/features/auth/models/oauth_verification_confirm_response.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('OAuthVerificationConfirmResponse', () { + test('creates instance with required fields', () { + const response = OAuthVerificationConfirmResponse( + success: true, + message: 'Success', + token: 'token_123', + user: {'id': 1, 'name': 'John'}, + isNewUser: false, + ); + + expect(response.success, true); + expect(response.message, 'Success'); + expect(response.token, 'token_123'); + expect(response.user, {'id': 1, 'name': 'John'}); + expect(response.isNewUser, false); + }); + + test('creates instance with isNewUser true', () { + const response = OAuthVerificationConfirmResponse( + success: true, + message: 'New user created', + token: 'token_123', + user: {'id': 1, 'name': 'John'}, + isNewUser: true, + ); + + expect(response.isNewUser, true); + }); + + test('fromJson creates instance from JSON', () { + final json = { + 'success': true, + 'message': 'Success', + 'token': 'token_123', + 'user': {'id': 1, 'name': 'John'}, + 'is_new_user': false, + }; + + final response = OAuthVerificationConfirmResponse.fromJson(json); + + expect(response.success, true); + expect(response.message, 'Success'); + expect(response.token, 'token_123'); + expect(response.user, {'id': 1, 'name': 'John'}); + expect(response.isNewUser, false); + }); + + test('toJson converts instance to JSON', () { + const response = OAuthVerificationConfirmResponse( + success: true, + message: 'Success', + token: 'token_123', + user: {'id': 1, 'name': 'John'}, + isNewUser: false, + ); + + final json = response.toJson(); + + expect(json['success'], true); + expect(json['message'], 'Success'); + expect(json['token'], 'token_123'); + expect(json['user'], {'id': 1, 'name': 'John'}); + expect(json['is_new_user'], false); + }); + + test('equality works correctly', () { + const response1 = OAuthVerificationConfirmResponse( + success: true, + message: 'Success', + token: 'token_123', + user: {'id': 1}, + isNewUser: false, + ); + const response2 = OAuthVerificationConfirmResponse( + success: true, + message: 'Success', + token: 'token_123', + user: {'id': 1}, + isNewUser: false, + ); + const response3 = OAuthVerificationConfirmResponse( + success: false, + message: 'Error', + token: 'token_123', + user: {'id': 1}, + isNewUser: false, + ); + + expect(response1, equals(response2)); + expect(response1, isNot(equals(response3))); + }); + }); +} diff --git a/test/features/auth/models/oauth_verification_request_response_test.dart b/test/features/auth/models/oauth_verification_request_response_test.dart new file mode 100644 index 00000000..afc6832a --- /dev/null +++ b/test/features/auth/models/oauth_verification_request_response_test.dart @@ -0,0 +1,58 @@ +import 'package:eatvio_app/features/auth/models/oauth_verification_request_response.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('OAuthVerificationRequestResponse', () { + test('creates instance with required fields', () { + const response = OAuthVerificationRequestResponse( + email: 'test@example.com', + status: 'pending', + ); + + expect(response.email, 'test@example.com'); + expect(response.status, 'pending'); + }); + + test('fromJson creates instance from JSON', () { + final json = { + 'email': 'test@example.com', + 'status': 'pending', + }; + + final response = OAuthVerificationRequestResponse.fromJson(json); + + expect(response.email, 'test@example.com'); + expect(response.status, 'pending'); + }); + + test('toJson converts instance to JSON', () { + const response = OAuthVerificationRequestResponse( + email: 'test@example.com', + status: 'pending', + ); + + final json = response.toJson(); + + expect(json['email'], 'test@example.com'); + expect(json['status'], 'pending'); + }); + + test('equality works correctly', () { + const response1 = OAuthVerificationRequestResponse( + email: 'test@example.com', + status: 'pending', + ); + const response2 = OAuthVerificationRequestResponse( + email: 'test@example.com', + status: 'pending', + ); + const response3 = OAuthVerificationRequestResponse( + email: 'other@example.com', + status: 'pending', + ); + + expect(response1, equals(response2)); + expect(response1, isNot(equals(response3))); + }); + }); +} diff --git a/test/features/auth/presentation/pages/scan_qr_code_screen_smoke_test.dart b/test/features/auth/presentation/pages/scan_qr_code_screen_smoke_test.dart new file mode 100644 index 00000000..116d2d85 --- /dev/null +++ b/test/features/auth/presentation/pages/scan_qr_code_screen_smoke_test.dart @@ -0,0 +1,43 @@ +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/auth/presentation/pages/scan_qr_code_screen.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('ScanQRCodeScreen smoke', () { + setUpAll(() async { + dotenv.loadFromString(envString: 'DEV_BASE_URL=http://localhost\nBASE_URL=http://localhost'); + }); + + setUp(() async { + await sl.reset(); + sl.registerLazySingleton(() => AppConfig(buildType: AppBuildType.tests)..setBaseUrl('http://localhost')); + }); + + Widget wrap(Widget child) { + return MaterialApp( + theme: ThemeHelper().themeData(), + home: child, + ); + } + + testWidgets('renders scan QR code screen', (tester) async { + await tester.pumpWidget(wrap(const ScanQRCodeScreen())); + await tester.pumpAndSettle(); + + expect(find.byType(ScanQRCodeScreen), findsOneWidget); + expect(find.text('Сканирование QR-кода'), findsOneWidget); + }); + + testWidgets('displays QR scanner UI', (tester) async { + await tester.pumpWidget(wrap(const ScanQRCodeScreen())); + await tester.pumpAndSettle(); + + expect(find.byType(ScanQRCodeScreen), findsOneWidget); + }); + }); +} diff --git a/test/features/blog_post/presentation/pages/blog_post_screen_smoke_test.dart b/test/features/blog_post/presentation/pages/blog_post_screen_smoke_test.dart index fba2161e..0758ab9b 100644 --- a/test/features/blog_post/presentation/pages/blog_post_screen_smoke_test.dart +++ b/test/features/blog_post/presentation/pages/blog_post_screen_smoke_test.dart @@ -17,6 +17,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:go_router/go_router.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; +import 'package:network_image_mock/network_image_mock.dart'; import 'blog_post_screen_smoke_test.mocks.dart'; @@ -105,5 +106,58 @@ void main() { expect(find.byType(BlogPostScreen), findsOneWidget); }); + testWidgets('renders error state', (tester) async { + await mockNetworkImagesFor(() async { + final blogPostCubit = MockBlogPostCubit(); + final feedCubit = MockFeedCubit(); + final commentsCubit = MockCommentsCubit(); + final state = BlogPostState(isLoading: false, blogPostModel: null, error: 'Test error'); + + when(blogPostCubit.state).thenReturn(state); + when(blogPostCubit.stream).thenAnswer((_) => Stream.value(state)); + when(blogPostCubit.close()).thenAnswer((_) async => Future.value()); + when(feedCubit.viewPost(any)).thenAnswer((_) async => Future.value()); + when(feedCubit.stream).thenAnswer((_) => const Stream.empty()); + when(feedCubit.close()).thenAnswer((_) async => Future.value()); + when(commentsCubit.state).thenReturn(const CommentsState()); + when(commentsCubit.stream).thenAnswer((_) => const Stream.empty()); + when(commentsCubit.close()).thenAnswer((_) async => Future.value()); + when(commentsCubit.getComments(any, any, any)).thenAnswer((_) async => Future.value()); + + registerMocks(blogPostCubit, commentsCubit); + + await tester.pumpWidget(wrap(blogPostCubit, feedCubit, commentsCubit, const BlogPostScreen(id: 1))); + await tester.pump(); + + expect(find.byType(BlogPostScreen), findsOneWidget); + }); + }); + + testWidgets('renders loaded state', (tester) async { + await mockNetworkImagesFor(() async { + final blogPostCubit = MockBlogPostCubit(); + final feedCubit = MockFeedCubit(); + final commentsCubit = MockCommentsCubit(); + final state = BlogPostState(isLoading: false, blogPostModel: null, error: null); + + when(blogPostCubit.state).thenReturn(state); + when(blogPostCubit.stream).thenAnswer((_) => Stream.value(state)); + when(blogPostCubit.close()).thenAnswer((_) async => Future.value()); + when(feedCubit.viewPost(any)).thenAnswer((_) async => Future.value()); + when(feedCubit.stream).thenAnswer((_) => const Stream.empty()); + when(feedCubit.close()).thenAnswer((_) async => Future.value()); + when(commentsCubit.state).thenReturn(const CommentsState()); + when(commentsCubit.stream).thenAnswer((_) => const Stream.empty()); + when(commentsCubit.close()).thenAnswer((_) async => Future.value()); + when(commentsCubit.getComments(any, any, any)).thenAnswer((_) async => Future.value()); + + registerMocks(blogPostCubit, commentsCubit); + + await tester.pumpWidget(wrap(blogPostCubit, feedCubit, commentsCubit, const BlogPostScreen(id: 1))); + await tester.pump(); + + expect(find.byType(BlogPostScreen), findsOneWidget); + }); + }); }); } diff --git a/test/features/consultations/presentation/pages/consultations_screen_smoke_test.dart b/test/features/consultations/presentation/pages/consultations_screen_smoke_test.dart new file mode 100644 index 00000000..22e83fe7 --- /dev/null +++ b/test/features/consultations/presentation/pages/consultations_screen_smoke_test.dart @@ -0,0 +1,89 @@ +import 'package:eatvio_app/core/app_config.dart'; +import 'package:eatvio_app/core/app_initializer.dart'; +import 'package:eatvio_app/core/enums/base_state.dart'; +import 'package:eatvio_app/core/locator_service.dart'; +import 'package:eatvio_app/core/theme/theme_helper.dart'; +import 'package:eatvio_app/features/consultations/presentation/cubit/consultations_cubit.dart'; +import 'package:eatvio_app/features/consultations/presentation/cubit/consultations_state.dart'; +import 'package:eatvio_app/features/consultations/presentation/pages/consultations_screen.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:go_router/go_router.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; + +import 'consultations_screen_smoke_test.mocks.dart'; + +@GenerateNiceMocks([MockSpec()]) +void main() { + group('ConsultationsScreen smoke', () { + setUpAll(() async { + dotenv.loadFromString(envString: 'DEV_BASE_URL=http://localhost\nBASE_URL=http://localhost'); + }); + + setUp(() async { + await sl.reset(); + sl.registerLazySingleton(() => AppConfig(buildType: AppBuildType.tests)..setBaseUrl('http://localhost')); + }); + + Widget wrap(Widget child, ConsultationsCubit cubit) { + return MaterialApp.router( + theme: ThemeHelper().themeData(), + routerConfig: GoRouter( + routes: [ + GoRoute( + path: '/', + builder: (context, state) => Scaffold( + body: BlocProvider.value(value: cubit, child: child), + ), + ), + ], + ), + ); + } + + testWidgets('renders consultations screen', (tester) async { + final cubit = MockConsultationsCubit(); + final state = ConsultationsState(state: BaseState.initial, consultations: []); + + when(cubit.state).thenReturn(state); + when(cubit.stream).thenAnswer((_) => Stream.value(state)); + when(cubit.close()).thenAnswer((_) async => Future.value()); + + await tester.pumpWidget(wrap(const ConsultationsContent(), cubit)); + await tester.pump(); + + expect(find.byType(ConsultationsContent), findsOneWidget); + }); + + testWidgets('displays consultations title', (tester) async { + final cubit = MockConsultationsCubit(); + final state = ConsultationsState(state: BaseState.success, consultations: []); + + when(cubit.state).thenReturn(state); + when(cubit.stream).thenAnswer((_) => Stream.value(state)); + when(cubit.close()).thenAnswer((_) async => Future.value()); + + await tester.pumpWidget(wrap(const ConsultationsContent(), cubit)); + await tester.pump(); + + expect(find.text('Консультации'), findsOneWidget); + }); + + testWidgets('displays archive button when loaded', (tester) async { + final cubit = MockConsultationsCubit(); + final state = ConsultationsState(state: BaseState.success, consultations: []); + + when(cubit.state).thenReturn(state); + when(cubit.stream).thenAnswer((_) => Stream.value(state)); + when(cubit.close()).thenAnswer((_) async => Future.value()); + + await tester.pumpWidget(wrap(const ConsultationsContent(), cubit)); + await tester.pump(); + + expect(find.text('Архив консультаций'), findsOneWidget); + }); + }); +} diff --git a/test/features/dev_tools/presentation/dev_tools_screen_smoke_test.dart b/test/features/dev_tools/presentation/dev_tools_screen_smoke_test.dart new file mode 100644 index 00000000..7995d87e --- /dev/null +++ b/test/features/dev_tools/presentation/dev_tools_screen_smoke_test.dart @@ -0,0 +1,43 @@ +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/dev_tools/presentation/dev_tools_screen.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('DevToolsScreen smoke', () { + setUpAll(() async { + dotenv.loadFromString(envString: 'DEV_BASE_URL=http://localhost\nBASE_URL=http://localhost'); + }); + + setUp(() async { + await sl.reset(); + sl.registerLazySingleton(() => AppConfig(buildType: AppBuildType.tests)..setBaseUrl('http://localhost')); + }); + + Widget wrap(Widget child) { + return MaterialApp( + theme: ThemeHelper().themeData(), + home: child, + ); + } + + testWidgets('renders dev tools screen', (tester) async { + await tester.pumpWidget(wrap(const DevToolsScreen())); + await tester.pump(); + + expect(find.byType(DevToolsScreen), findsOneWidget); + expect(find.text('Для разработчиков'), findsOneWidget); + }); + + testWidgets('displays dev tools screen content', (tester) async { + await tester.pumpWidget(wrap(const DevToolsScreen())); + await tester.pump(); + + expect(find.byType(DevToolsScreenContent), findsOneWidget); + }); + }); +} diff --git a/test/features/diary/presentation/pages/barcode_scanner_screen_smoke_test.dart b/test/features/diary/presentation/pages/barcode_scanner_screen_smoke_test.dart new file mode 100644 index 00000000..e3aacfeb --- /dev/null +++ b/test/features/diary/presentation/pages/barcode_scanner_screen_smoke_test.dart @@ -0,0 +1,45 @@ +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/diary/presentation/pages/barcode_scanner_screen.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('BarcodeScannerScreen smoke', () { + setUpAll(() async { + dotenv.loadFromString(envString: 'DEV_BASE_URL=http://localhost\nBASE_URL=http://localhost'); + }); + + setUp(() async { + await sl.reset(); + sl.registerLazySingleton(() => AppConfig(buildType: AppBuildType.tests)..setBaseUrl('http://localhost')); + }); + + Widget wrap(Widget child) { + return MaterialApp( + theme: ThemeHelper().themeData(), + home: child, + ); + } + + testWidgets('renders barcode scanner screen', (tester) async { + await tester.pumpWidget(wrap(const BarcodeScannerScreen())); + await tester.pumpAndSettle(); + + expect(find.byType(BarcodeScannerScreen), findsOneWidget); + expect(find.text('Сканирование штрихкода'), findsOneWidget); + expect(find.text('Наведите камеру на штрихкод'), findsOneWidget); + }); + + testWidgets('displays barcode scanner UI elements', (tester) async { + await tester.pumpWidget(wrap(const BarcodeScannerScreen())); + await tester.pumpAndSettle(); + + expect(find.text('Найденный штрихкод'), findsOneWidget); + expect(find.text('Наведите камеру на штрихкод'), findsOneWidget); + }); + }); +} diff --git a/test/features/menu/presentation/pages/blocked_users_screen_smoke_test.dart b/test/features/menu/presentation/pages/blocked_users_screen_smoke_test.dart new file mode 100644 index 00000000..cfb19dc5 --- /dev/null +++ b/test/features/menu/presentation/pages/blocked_users_screen_smoke_test.dart @@ -0,0 +1,82 @@ +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/menu/presentation/blocked_user_cubit/blocked_users_cubit.dart'; +import 'package:eatvio_app/features/menu/presentation/blocked_user_cubit/blocked_users_state.dart'; +import 'package:eatvio_app/features/menu/presentation/pages/blocked_users_screen.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:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; + +import 'blocked_users_screen_smoke_test.mocks.dart'; + +@GenerateNiceMocks([MockSpec()]) +void main() { + group('BlockedUsersScreen smoke', () { + setUpAll(() async { + dotenv.loadFromString(envString: 'DEV_BASE_URL=http://localhost\nBASE_URL=http://localhost'); + }); + + setUp(() async { + await sl.reset(); + sl.registerLazySingleton(() => 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: child, + ); + } + + testWidgets('renders blocked users screen with loading state', (tester) async { + final cubit = MockBlockedUsersCubit(); + const state = BlockedUsersStateLoading(); + + when(cubit.state).thenReturn(state); + when(cubit.stream).thenAnswer((_) => Stream.value(state)); + when(cubit.close()).thenAnswer((_) async => Future.value()); + when(cubit.getBlockedUsers()).thenAnswer((_) async => Future.value()); + + // Регистрируем cubit в sl, так как BlockedUsersScreen использует sl() + sl.registerFactory(() => cubit); + + await tester.pumpWidget(wrap(const BlockedUsersScreen())); + await tester.pump(); + + expect(find.byType(BlockedUsersScreen), findsOneWidget); + expect(find.byType(CircularProgressIndicator), findsOneWidget); + }); + + testWidgets('renders blocked users screen with loaded state', (tester) async { + final cubit = MockBlockedUsersCubit(); + const state = BlockedUsersStateLoaded(profilesList: []); + + when(cubit.state).thenReturn(state); + when(cubit.stream).thenAnswer((_) => Stream.value(state)); + when(cubit.close()).thenAnswer((_) async => Future.value()); + when(cubit.getBlockedUsers()).thenAnswer((_) async => Future.value()); + + // Регистрируем cubit в sl, так как BlockedUsersScreen использует sl() + sl.registerFactory(() => cubit); + + await tester.pumpWidget(wrap(const BlockedUsersScreen())); + await tester.pump(); + + expect(find.byType(BlockedUsersScreen), findsOneWidget); + expect(find.text('Чёрный список'), findsOneWidget); + }); + }); +} diff --git a/test/features/personal_diet_suggestion/presentation/personal_diet_suggestion_screen_smoke_test.dart b/test/features/personal_diet_suggestion/presentation/personal_diet_suggestion_screen_smoke_test.dart new file mode 100644 index 00000000..b74938ae --- /dev/null +++ b/test/features/personal_diet_suggestion/presentation/personal_diet_suggestion_screen_smoke_test.dart @@ -0,0 +1,88 @@ +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/personal_diet_suggestion/cubit/personal_diet_suggestion_cubit.dart'; +import 'package:eatvio_app/features/personal_diet_suggestion/cubit/personal_diet_suggestion_state.dart'; +import 'package:eatvio_app/features/personal_diet_suggestion/presentation/personal_diet_suggestion_screen.dart'; +import 'package:eatvio_app/generated/l10n.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; + +import 'personal_diet_suggestion_screen_smoke_test.mocks.dart'; + +@GenerateNiceMocks([MockSpec()]) +void main() { + group('PersonalDietSuggestionScreen smoke', () { + setUpAll(() async { + dotenv.loadFromString(envString: 'DEV_BASE_URL=http://localhost\nBASE_URL=http://localhost'); + }); + + setUp(() async { + await sl.reset(); + sl.registerLazySingleton(() => AppConfig(buildType: AppBuildType.tests)..setBaseUrl('http://localhost')); + }); + + Widget wrap(Widget child, PersonalDietSuggestionCubit cubit) { + return MaterialApp( + theme: ThemeHelper().themeData(), + localizationsDelegates: const [ + S.delegate, + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + supportedLocales: const [Locale('ru')], + home: Scaffold( + body: BlocProvider.value( + value: cubit, + child: child, + ), + ), + ); + } + + testWidgets('renders personal diet suggestion screen', (tester) async { + final cubit = MockPersonalDietSuggestionCubit(); + final state = PersonalDietSuggestionState( + isLoading: true, + dietName: 'Test Diet', + consultationsWithDiet: [], + consultationsWithoutDiet: [], + ); + + when(cubit.state).thenReturn(state); + when(cubit.stream).thenAnswer((_) => Stream.value(state)); + when(cubit.close()).thenAnswer((_) async => Future.value()); + + await tester.pumpWidget(wrap(const PersonalDietSuggestionScreenContent(), cubit)); + await tester.pump(); + + expect(find.byType(PersonalDietSuggestionScreenContent), findsOneWidget); + }); + + testWidgets('displays diet name in app bar', (tester) async { + final cubit = MockPersonalDietSuggestionCubit(); + final state = PersonalDietSuggestionState( + isLoading: false, + dietName: 'Test Diet Name', + consultationsWithDiet: [], + consultationsWithoutDiet: [], + ); + + when(cubit.state).thenReturn(state); + when(cubit.stream).thenAnswer((_) => Stream.value(state)); + when(cubit.close()).thenAnswer((_) async => Future.value()); + + await tester.pumpWidget(wrap(const PersonalDietSuggestionScreenContent(), cubit)); + await tester.pump(); + + expect(find.textContaining('Test Diet Name'), findsOneWidget); + }); + }); +} diff --git a/test/features/recipe/presentation/pages/my_recipes_screen_smoke_test.dart b/test/features/recipe/presentation/pages/my_recipes_screen_smoke_test.dart new file mode 100644 index 00000000..40176b9d --- /dev/null +++ b/test/features/recipe/presentation/pages/my_recipes_screen_smoke_test.dart @@ -0,0 +1,70 @@ +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/recipe/presentation/cubit/my_recipes_cubit/my_recieps_cubit.dart'; +import 'package:eatvio_app/features/recipe/presentation/cubit/my_recipes_cubit/my_recipes_state.dart'; +import 'package:eatvio_app/features/recipe/presentation/pages/my_recipes_screen.dart'; +import 'package:flutter/material.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 'my_recipes_screen_smoke_test.mocks.dart'; + +@GenerateNiceMocks([MockSpec()]) +void main() { + group('MyRecipesScreen smoke', () { + setUpAll(() async { + dotenv.loadFromString(envString: 'DEV_BASE_URL=http://localhost\nBASE_URL=http://localhost'); + }); + + setUp(() async { + await sl.reset(); + sl.registerLazySingleton(() => AppConfig(buildType: AppBuildType.tests)..setBaseUrl('http://localhost')); + }); + + Widget wrap(Widget child) { + return MaterialApp(theme: ThemeHelper().themeData(), home: child); + } + + testWidgets('renders my recipes screen', (tester) async { + final cubit = MockMyRecipesCubit(); + const state = MyRecipesStateLoading(); + + when(cubit.state).thenReturn(state); + when(cubit.stream).thenAnswer((_) => Stream.value(state)); + when(cubit.close()).thenAnswer((_) async => Future.value()); + when(cubit.getMyRecipes()).thenAnswer((_) async => Future.value()); + + // Регистрируем cubit в sl, так как MyRecipesScreen использует sl() + sl.registerFactory(() => cubit); + + await tester.pumpWidget(wrap(const MyRecipesScreen())); + await tester.pump(); + + expect(find.byType(MyRecipesScreen), findsOneWidget); + expect(find.text('Мои рецепты'), findsOneWidget); + }); + + testWidgets('renders my recipes screen with loaded state', (tester) async { + final cubit = MockMyRecipesCubit(); + const state = MyRecipesStateLoaded(recipes: [], selectedState: 'all'); + + when(cubit.state).thenReturn(state); + when(cubit.stream).thenAnswer((_) => Stream.value(state)); + when(cubit.close()).thenAnswer((_) async => Future.value()); + when(cubit.getMyRecipes()).thenAnswer((_) async => Future.value()); + + // Регистрируем cubit в sl, так как MyRecipesScreen использует sl() + sl.registerFactory(() => cubit); + + await tester.pumpWidget(wrap(const MyRecipesScreen())); + await tester.pump(); + + expect(find.byType(MyRecipesScreen), findsOneWidget); + expect(find.text('Мои рецепты'), findsOneWidget); + }); + }); +} diff --git a/test/features/registration_v2/presentation/pages/registration_v2_page_smoke_test.dart b/test/features/registration_v2/presentation/pages/registration_v2_page_smoke_test.dart new file mode 100644 index 00000000..d3a0905b --- /dev/null +++ b/test/features/registration_v2/presentation/pages/registration_v2_page_smoke_test.dart @@ -0,0 +1,79 @@ +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/registration_v2/cubit/registration_cubit.dart'; +import 'package:eatvio_app/features/registration_v2/cubit/registration_state.dart'; +import 'package:eatvio_app/features/registration_v2/presentation/pages/registration_v2_page.dart'; +import 'package:eatvio_app/generated/l10n.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; + +import 'registration_v2_page_smoke_test.mocks.dart'; + +@GenerateNiceMocks([MockSpec()]) +void main() { + group('RegistrationV2Page smoke', () { + setUpAll(() async { + dotenv.loadFromString(envString: 'DEV_BASE_URL=http://localhost\nBASE_URL=http://localhost'); + }); + + setUp(() async { + await sl.reset(); + sl.registerLazySingleton(() => AppConfig(buildType: AppBuildType.tests)..setBaseUrl('http://localhost')); + }); + + Widget wrap(Widget child, RegistrationCubitV2 cubit) { + sl.registerLazySingleton(() => cubit); + return MaterialApp( + theme: ThemeHelper().themeData(), + localizationsDelegates: const [ + S.delegate, + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + supportedLocales: const [Locale('ru')], + home: Scaffold( + body: BlocProvider.value( + value: cubit, + child: child, + ), + ), + ); + } + + testWidgets('renders registration v2 page', (tester) async { + final cubit = MockRegistrationCubitV2(); + const state = RegistrationStateV2(step: 0); + + when(cubit.state).thenReturn(state); + when(cubit.stream).thenAnswer((_) => Stream.value(state)); + when(cubit.close()).thenAnswer((_) async => Future.value()); + + await tester.pumpWidget(wrap(const RegistrationV2Page(), cubit)); + await tester.pump(); + + expect(find.byType(RegistrationV2Page), findsOneWidget); + }); + + testWidgets('displays step progress', (tester) async { + final cubit = MockRegistrationCubitV2(); + const state = RegistrationStateV2(step: 0); + + when(cubit.state).thenReturn(state); + when(cubit.stream).thenAnswer((_) => Stream.value(state)); + when(cubit.close()).thenAnswer((_) async => Future.value()); + + await tester.pumpWidget(wrap(const RegistrationV2Page(), cubit)); + await tester.pump(); + + expect(find.byType(RegistrationV2PageContent), findsOneWidget); + }); + }); +} diff --git a/test/features/registration_v2/presentation/widgets/phisical_activity_test.dart b/test/features/registration_v2/presentation/widgets/phisical_activity_test.dart new file mode 100644 index 00000000..cedace03 --- /dev/null +++ b/test/features/registration_v2/presentation/widgets/phisical_activity_test.dart @@ -0,0 +1,66 @@ +import 'package:eatvio_app/core/theme/theme_helper.dart'; +import 'package:eatvio_app/features/registration_v2/cubit/registration_cubit.dart'; +import 'package:eatvio_app/features/registration_v2/cubit/registration_state.dart'; +import 'package:eatvio_app/features/registration_v2/presentation/widgets/phisical_activity.dart'; +import 'package:eatvio_app/generated/l10n.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; + +import 'step_widget_test.mocks.dart'; + +void main() { + group('PhisicalActivity', () { + late MockRegistrationCubitV2 mockCubit; + + setUp(() { + mockCubit = MockRegistrationCubitV2(); + }); + + Widget createTestWidget() { + return MaterialApp( + theme: ThemeHelper().themeData(), + localizationsDelegates: const [ + S.delegate, + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + supportedLocales: const [Locale('ru'), Locale('en')], + locale: const Locale('ru'), + home: BlocProvider.value( + value: mockCubit, + child: const Scaffold( + body: PhisicalActivity(), + ), + ), + ); + } + + testWidgets('displays phisical activity widget', (tester) async { + when(mockCubit.state).thenReturn( + const RegistrationStateV2(activityCoefficient: 1.2), + ); + when(mockCubit.stream).thenAnswer((_) => Stream.value(const RegistrationStateV2(activityCoefficient: 1.2))); + + await tester.pumpWidget(createTestWidget()); + await tester.pump(); + + expect(find.byType(PhisicalActivity), findsOneWidget); + }); + + testWidgets('displays phisical activity with different coefficient', (tester) async { + when(mockCubit.state).thenReturn( + const RegistrationStateV2(activityCoefficient: 1.55), + ); + when(mockCubit.stream).thenAnswer((_) => Stream.value(const RegistrationStateV2(activityCoefficient: 1.55))); + + await tester.pumpWidget(createTestWidget()); + await tester.pump(); + + expect(find.byType(PhisicalActivity), findsOneWidget); + }); + }); +} diff --git a/test/features/registration_v2/presentation/widgets/step_progress_test.dart b/test/features/registration_v2/presentation/widgets/step_progress_test.dart new file mode 100644 index 00000000..8ec95898 --- /dev/null +++ b/test/features/registration_v2/presentation/widgets/step_progress_test.dart @@ -0,0 +1,84 @@ +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/registration_v2/cubit/registration_cubit.dart'; +import 'package:eatvio_app/features/registration_v2/cubit/registration_state.dart'; +import 'package:eatvio_app/features/registration_v2/presentation/widgets/step_progress.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.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 'step_progress_test.mocks.dart'; + +@GenerateNiceMocks([MockSpec()]) +void main() { + group('StepProgress smoke', () { + setUpAll(() async { + dotenv.loadFromString(envString: 'DEV_BASE_URL=http://localhost\nBASE_URL=http://localhost'); + }); + + setUp(() async { + await sl.reset(); + sl.registerLazySingleton(() => AppConfig(buildType: AppBuildType.tests)..setBaseUrl('http://localhost')); + }); + + Widget wrap(Widget child, RegistrationCubitV2 cubit) { + return MaterialApp( + theme: ThemeHelper().themeData(), + home: Scaffold( + body: BlocProvider.value( + value: cubit, + child: child, + ), + ), + ); + } + + testWidgets('renders step progress widget', (tester) async { + final cubit = MockRegistrationCubitV2(); + const state = RegistrationStateV2(step: 0); + + when(cubit.state).thenReturn(state); + when(cubit.stream).thenAnswer((_) => Stream.value(state)); + when(cubit.close()).thenAnswer((_) async => Future.value()); + + await tester.pumpWidget(wrap(const StepProgress(), cubit)); + await tester.pump(); + + expect(find.byType(StepProgress), findsOneWidget); + expect(find.byType(LinearProgressIndicator), findsOneWidget); + }); + + testWidgets('displays progress indicator for step 0', (tester) async { + final cubit = MockRegistrationCubitV2(); + const state = RegistrationStateV2(step: 0); + + when(cubit.state).thenReturn(state); + when(cubit.stream).thenAnswer((_) => Stream.value(state)); + when(cubit.close()).thenAnswer((_) async => Future.value()); + + await tester.pumpWidget(wrap(const StepProgress(), cubit)); + await tester.pump(); + + expect(find.byType(LinearProgressIndicator), findsOneWidget); + }); + + testWidgets('displays progress indicator for step 1', (tester) async { + final cubit = MockRegistrationCubitV2(); + const state = RegistrationStateV2(step: 1); + + when(cubit.state).thenReturn(state); + when(cubit.stream).thenAnswer((_) => Stream.value(state)); + when(cubit.close()).thenAnswer((_) async => Future.value()); + + await tester.pumpWidget(wrap(const StepProgress(), cubit)); + await tester.pump(); + + expect(find.byType(LinearProgressIndicator), findsOneWidget); + }); + }); +} diff --git a/test/features/registration_v2/repositories/registration_repository_v2_test.dart b/test/features/registration_v2/repositories/registration_repository_v2_test.dart index e1c764cb..cd612dbb 100644 --- a/test/features/registration_v2/repositories/registration_repository_v2_test.dart +++ b/test/features/registration_v2/repositories/registration_repository_v2_test.dart @@ -1,11 +1,14 @@ +import 'package:dio/dio.dart'; import 'package:eatvio_app/features/registration_v2/models/registration_model.dart'; import 'package:eatvio_app/features/registration_v2/repositories/registration_repository_v2.dart'; import 'package:eatvio_app/features/registration_v2/services/registration_service_v2.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mocktail/mocktail.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; -class MockRegistrationServiceV2 extends Mock implements RegistrationServiceV2 {} +import 'registration_repository_v2_test.mocks.dart'; +@GenerateMocks([RegistrationServiceV2]) void main() { group('RegistrationRepositoryV2', () { late RegistrationRepositoryV2 repository; @@ -16,73 +19,79 @@ void main() { repository = RegistrationRepositoryV2(registrationService: mockService); }); - test('register - успешная регистрация возвращает токен', () async { - const token = 'test-token-123'; + test('register - успешная регистрация', () async { + const token = 'test_token_123'; final registrationModel = RegistrationModel( - firstName: 'Test', - lastName: 'User', + firstName: 'John', + lastName: 'Doe', email: 'test@example.com', - phone: '+1234567890', + phone: '+79991234567', gender: 'male', password: 'password123', confirmPassword: 'password123', weight: 70, - height: 175, - activityCoefficient: 1.5, + height: 180, + activityCoefficient: 1.55, birthday: '1990-01-01', ); - when(() => mockService.register(model: any(named: 'model'))) + when(mockService.register(model: anyNamed('model'))) .thenAnswer((_) async => {'token': token}); final result = await repository.register(registrationModel: registrationModel); expect(result, token); - verify(() => mockService.register(model: any(named: 'model'))).called(1); + verify(mockService.register(model: anyNamed('model'))).called(1); }); - test('register - возвращает null если токена нет', () async { + test('register - обработка ошибки', () async { final registrationModel = RegistrationModel( - firstName: 'Test', - lastName: 'User', + firstName: 'John', + lastName: 'Doe', email: 'test@example.com', - phone: '+1234567890', + phone: '+79991234567', gender: 'male', password: 'password123', confirmPassword: 'password123', weight: 70, - height: 175, - activityCoefficient: 1.5, + height: 180, + activityCoefficient: 1.55, birthday: '1990-01-01', ); - when(() => mockService.register(model: any(named: 'model'))) + when(mockService.register(model: anyNamed('model'))) + .thenThrow(DioException( + requestOptions: RequestOptions(path: '/api/register'), + response: Response( + requestOptions: RequestOptions(path: '/api/register'), + statusCode: 400, + ), + )); + + expect(() => repository.register(registrationModel: registrationModel), throwsA(isA())); + }); + + test('register - возвращает null если токен отсутствует', () async { + final registrationModel = RegistrationModel( + firstName: 'John', + lastName: 'Doe', + email: 'test@example.com', + phone: '+79991234567', + gender: 'male', + password: 'password123', + confirmPassword: 'password123', + weight: 70, + height: 180, + activityCoefficient: 1.55, + birthday: '1990-01-01', + ); + + when(mockService.register(model: anyNamed('model'))) .thenAnswer((_) async => {}); final result = await repository.register(registrationModel: registrationModel); - expect(result, null); - }); - - test('register - пробрасывает ошибку', () async { - final registrationModel = RegistrationModel( - firstName: 'Test', - lastName: 'User', - email: 'test@example.com', - phone: '+1234567890', - gender: 'male', - password: 'password123', - confirmPassword: 'password123', - weight: 70, - height: 175, - activityCoefficient: 1.5, - birthday: '1990-01-01', - ); - - when(() => mockService.register(model: any(named: 'model'))) - .thenThrow(Exception('Error')); - - expect(() => repository.register(registrationModel: registrationModel), throwsException); + expect(result, isNull); }); }); } diff --git a/test/features/update_screen/presentation/update_screen_smoke_test.dart b/test/features/update_screen/presentation/update_screen_smoke_test.dart new file mode 100644 index 00000000..4ba0bbb0 --- /dev/null +++ b/test/features/update_screen/presentation/update_screen_smoke_test.dart @@ -0,0 +1,53 @@ +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/update_screen/presentation/update_screen.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('UpdateScreen smoke', () { + setUpAll(() async { + dotenv.loadFromString(envString: 'DEV_BASE_URL=http://localhost\nBASE_URL=http://localhost\nAPP_STORE_URL=https://apps.apple.com\nRUSTORE_URL=https://apps.rustore.ru'); + }); + + setUp(() async { + await sl.reset(); + sl.registerLazySingleton(() => AppConfig(buildType: AppBuildType.tests)..setBaseUrl('http://localhost')); + }); + + Widget wrap(Widget child) { + return MaterialApp( + theme: ThemeHelper().themeData(), + home: child, + ); + } + + testWidgets('renders update screen', (tester) async { + await tester.pumpWidget(wrap(const UpdateScreen())); + await tester.pump(); + + expect(find.byType(UpdateScreen), findsOneWidget); + expect(find.text('Пора обновиться! ⚡'), findsOneWidget); + expect(find.text('Обновить'), findsOneWidget); + }); + + testWidgets('displays update message', (tester) async { + await tester.pumpWidget(wrap(const UpdateScreen())); + await tester.pump(); + + // Проверяем наличие текста об обновлении (может быть в многострочном тексте) + expect(find.textContaining('обновление'), findsWidgets); + }); + + testWidgets('displays update button', (tester) async { + await tester.pumpWidget(wrap(const UpdateScreen())); + await tester.pump(); + + expect(find.text('Обновить'), findsOneWidget); + expect(find.byType(InkWell), findsOneWidget); + }); + }); +}