forked from eatvio/eatvio_app
added widget and unit tests for plans, diets, recipes
This commit is contained in:
244
test/features/diets/presentation/widgets/basic_widget_test.dart
Normal file
244
test/features/diets/presentation/widgets/basic_widget_test.dart
Normal file
@@ -0,0 +1,244 @@
|
||||
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/diets/repositories/diet_repository.dart';
|
||||
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
|
||||
import 'package:eatvio_app/features/feed/presentation/bloc/diets_cubit/diets_cubit.dart';
|
||||
import 'package:eatvio_app/features/feed/presentation/bloc/diets_cubit/diets_state.dart';
|
||||
import 'package:eatvio_app/features/feed/presentation/widgets/diets.dart';
|
||||
import 'package:eatvio_app/features/feed/presentation/widgets/diets_widget.dart';
|
||||
import 'package:eatvio_app/features/feed/repositories/feed_repository.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_portal/flutter_portal.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/annotations.dart';
|
||||
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
||||
import 'package:network_image_mock/network_image_mock.dart';
|
||||
import 'package:visibility_detector/visibility_detector.dart';
|
||||
|
||||
import 'basic_widget_test.mocks.dart';
|
||||
|
||||
@GenerateNiceMocks([MockSpec<FeedRepository>(), MockSpec<DietRepository>()])
|
||||
void main() {
|
||||
group('Diets Basic Widget Tests', () {
|
||||
late MockFeedRepository mockFeedRepository;
|
||||
late MockDietRepository mockDietRepository;
|
||||
late DietsCubit dietsCubit;
|
||||
|
||||
const testDiet = Diet(
|
||||
id: 1,
|
||||
slug: 'test-diet',
|
||||
name: 'Test Diet',
|
||||
image: 'test-image.jpg',
|
||||
duration: 30,
|
||||
state: DietState.active,
|
||||
type: DietType(light: false, veg: false, carb: false, meat: false),
|
||||
details: 'Test diet description',
|
||||
dailySchedule: {},
|
||||
author: Author(authorSlug: '', authorName: '', authorProfileIcon: 'https://example.com/icon.jpg', authorIsMe: false),
|
||||
totalLikes: 1,
|
||||
totalViews: 1,
|
||||
totalComments: 1,
|
||||
wasLikedByMe: false,
|
||||
isFavorite: false,
|
||||
dailyNutrition: [],
|
||||
);
|
||||
|
||||
setUp(() async {
|
||||
await sl.reset();
|
||||
dotenv.loadFromString(envString: 'DEV_BASE_URL=http://localhost\nBASE_URL=http://localhost');
|
||||
mockFeedRepository = MockFeedRepository();
|
||||
mockDietRepository = MockDietRepository();
|
||||
dietsCubit = DietsCubit(feedRepository: mockFeedRepository, dietsRepository: mockDietRepository);
|
||||
sl.registerLazySingleton<AppConfig>(() => AppConfig(buildType: AppBuildType.tests)..setBaseUrl('http://localhost'));
|
||||
VisibilityDetectorController.instance.updateInterval = Duration.zero;
|
||||
});
|
||||
|
||||
tearDown(() async {
|
||||
await Future.delayed(const Duration(milliseconds: 100));
|
||||
if (!dietsCubit.isClosed) {
|
||||
dietsCubit.close();
|
||||
}
|
||||
});
|
||||
|
||||
Widget createTestWidget(Widget child, {bool wrapInScaffold = true}) {
|
||||
final widget = BlocProvider<DietsCubit>(
|
||||
create: (context) => dietsCubit,
|
||||
child: Portal(
|
||||
child: wrapInScaffold
|
||||
? Scaffold(body: SingleChildScrollView(child: child))
|
||||
: SizedBox(width: 800, height: 1200, child: SingleChildScrollView(child: child)),
|
||||
),
|
||||
);
|
||||
return MaterialApp(
|
||||
theme: ThemeHelper().themeData(),
|
||||
localizationsDelegates: const [
|
||||
S.delegate,
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
],
|
||||
supportedLocales: const [Locale('ru')],
|
||||
home: widget,
|
||||
);
|
||||
}
|
||||
|
||||
Widget createDietsTestWidget(Widget child) {
|
||||
return MaterialApp(
|
||||
theme: ThemeHelper().themeData(),
|
||||
localizationsDelegates: const [
|
||||
S.delegate,
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
],
|
||||
supportedLocales: const [Locale('ru')],
|
||||
home: Portal(
|
||||
child: Scaffold(
|
||||
body: BlocProvider<DietsCubit>(
|
||||
create: (context) => dietsCubit,
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraints) => SizedBox(
|
||||
width: constraints.maxWidth > 0 ? constraints.maxWidth : 800,
|
||||
height: constraints.maxHeight > 0 ? constraints.maxHeight : 600,
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
testWidgets('should create DietsCubit successfully', (WidgetTester tester) async {
|
||||
// Arrange & Act
|
||||
final cubit = DietsCubit(feedRepository: mockFeedRepository, dietsRepository: mockDietRepository);
|
||||
|
||||
// Assert
|
||||
expect(cubit, isA<DietsCubit>());
|
||||
expect(cubit.state.dietsList, isEmpty);
|
||||
|
||||
cubit.close();
|
||||
});
|
||||
|
||||
testWidgets('should display DietsWidget with diet name', (WidgetTester tester) async {
|
||||
await mockNetworkImagesFor(() async {
|
||||
// Arrange
|
||||
dietsCubit.emit(DietsState.initial().copyWith(dietsList: [testDiet], isLoading: false));
|
||||
const widget = DietsWidget(diet: testDiet, index: 0);
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(widget));
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 100));
|
||||
|
||||
// Assert
|
||||
expect(find.text('Test Diet'), findsOneWidget);
|
||||
expect(find.byType(DietsWidget), findsOneWidget);
|
||||
});
|
||||
await tester.pump(const Duration(milliseconds: 600));
|
||||
});
|
||||
|
||||
testWidgets('should display DietsWidget with description when details are present', (WidgetTester tester) async {
|
||||
await mockNetworkImagesFor(() async {
|
||||
// Arrange
|
||||
dietsCubit.emit(DietsState.initial().copyWith(dietsList: [testDiet], isLoading: false));
|
||||
const widget = DietsWidget(diet: testDiet, index: 0);
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(widget));
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 100));
|
||||
|
||||
// Assert
|
||||
expect(find.text('Test Diet'), findsOneWidget);
|
||||
expect(find.byType(DietsWidget), findsOneWidget);
|
||||
});
|
||||
await tester.pump(const Duration(milliseconds: 600));
|
||||
});
|
||||
|
||||
testWidgets('should display DietsWidget structure correctly', (WidgetTester tester) async {
|
||||
await mockNetworkImagesFor(() async {
|
||||
// Arrange
|
||||
dietsCubit.emit(DietsState.initial().copyWith(dietsList: [testDiet], isLoading: false));
|
||||
const widget = DietsWidget(diet: testDiet, index: 0);
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(widget));
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 100));
|
||||
|
||||
// Assert
|
||||
expect(find.byType(DietsWidget), findsOneWidget);
|
||||
expect(find.text('Test Diet'), findsOneWidget);
|
||||
});
|
||||
await tester.pump(const Duration(milliseconds: 600));
|
||||
});
|
||||
|
||||
testWidgets('should display Diets widget with loading state', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
dietsCubit.emit(DietsState.initial());
|
||||
final widget = Diets(handleVisibility: ({required visibleFraction, required id, required type}) {});
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createDietsTestWidget(widget));
|
||||
await tester.pump();
|
||||
|
||||
// Assert
|
||||
expect(find.byType(Diets), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('should display Diets widget with loaded state', (WidgetTester tester) async {
|
||||
await mockNetworkImagesFor(() async {
|
||||
// Arrange
|
||||
dietsCubit.emit(DietsState.initial().copyWith(dietsList: [testDiet], isLoading: false));
|
||||
final widget = Diets(handleVisibility: ({required visibleFraction, required id, required type}) {});
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createDietsTestWidget(widget));
|
||||
await tester.pump();
|
||||
|
||||
// Assert
|
||||
expect(find.byType(Diets), findsOneWidget);
|
||||
expect(find.byType(RefreshIndicator), findsOneWidget);
|
||||
expect(find.byType(ListView), findsOneWidget);
|
||||
});
|
||||
await tester.pump(const Duration(milliseconds: 600));
|
||||
});
|
||||
|
||||
testWidgets('should display Diets widget with error state', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
dietsCubit.emit(DietsState.initial().copyWith(isLoading: false, errorMessage: 'Error message'));
|
||||
final widget = Diets(handleVisibility: ({required visibleFraction, required id, required type}) {});
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createDietsTestWidget(widget));
|
||||
await tester.pump();
|
||||
|
||||
// Assert
|
||||
expect(find.byType(Diets), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('should display Diets widget with multiple diets', (WidgetTester tester) async {
|
||||
await mockNetworkImagesFor(() async {
|
||||
// Arrange
|
||||
final testDiet2 = testDiet.copyWith(id: 2, name: 'Test Diet 2');
|
||||
dietsCubit.emit(DietsState.initial().copyWith(dietsList: [testDiet, testDiet2], isLoading: false));
|
||||
final widget = Diets(handleVisibility: ({required visibleFraction, required id, required type}) {});
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createDietsTestWidget(widget));
|
||||
await tester.pump();
|
||||
|
||||
// Assert
|
||||
expect(find.byType(Diets), findsOneWidget);
|
||||
expect(find.byType(ListView), findsOneWidget);
|
||||
});
|
||||
await tester.pump(const Duration(milliseconds: 600));
|
||||
});
|
||||
});
|
||||
}
|
||||
242
test/features/diets/presentation/widgets/widgets_test.dart
Normal file
242
test/features/diets/presentation/widgets/widgets_test.dart
Normal file
@@ -0,0 +1,242 @@
|
||||
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/diets/presentation/widgets/author_card.dart';
|
||||
import 'package:eatvio_app/features/diets/presentation/widgets/expandable_text.dart';
|
||||
import 'package:eatvio_app/features/diets/presentation/widgets/info_bouble.dart';
|
||||
import 'package:eatvio_app/features/diets/presentation/widgets/type_badges.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_localizations/flutter_localizations.dart';
|
||||
import 'package:flutter_portal/flutter_portal.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
||||
import 'package:network_image_mock/network_image_mock.dart';
|
||||
|
||||
void main() {
|
||||
group('Diets Widgets Tests', () {
|
||||
setUp(() async {
|
||||
await sl.reset();
|
||||
dotenv.loadFromString(envString: 'DEV_BASE_URL=http://localhost\nBASE_URL=http://localhost');
|
||||
sl.registerLazySingleton<AppConfig>(() => AppConfig(buildType: AppBuildType.tests)..setBaseUrl('http://localhost'));
|
||||
});
|
||||
|
||||
Widget createTestWidget(Widget child) {
|
||||
return MaterialApp(
|
||||
theme: ThemeHelper().themeData(),
|
||||
localizationsDelegates: const [
|
||||
S.delegate,
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
],
|
||||
supportedLocales: const [Locale('ru')],
|
||||
home: Portal(
|
||||
child: Scaffold(body: SingleChildScrollView(child: child)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
group('InfoBouble', () {
|
||||
testWidgets('should display InfoBouble with children', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
const widget = InfoBouble(children: [Text('Test Text')]);
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(widget));
|
||||
await tester.pump();
|
||||
|
||||
// Assert
|
||||
expect(find.byType(InfoBouble), findsOneWidget);
|
||||
expect(find.text('Test Text'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('should display InfoBouble with multiple children', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
const widget = InfoBouble(children: [Text('First'), Text('Second')]);
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(widget));
|
||||
await tester.pump();
|
||||
|
||||
// Assert
|
||||
expect(find.byType(InfoBouble), findsOneWidget);
|
||||
expect(find.text('First'), findsOneWidget);
|
||||
expect(find.text('Second'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('should display InfoBouble with custom radius', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
const widget = InfoBouble(radius: 20, children: [Text('Test')]);
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(widget));
|
||||
await tester.pump();
|
||||
|
||||
// Assert
|
||||
expect(find.byType(InfoBouble), findsOneWidget);
|
||||
expect(find.text('Test'), findsOneWidget);
|
||||
});
|
||||
});
|
||||
|
||||
group('ExpandableText', () {
|
||||
testWidgets('should display ExpandableText with short text', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
const widget = ExpandableText(text: 'Short text', maxLines: 5);
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(widget));
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 100));
|
||||
|
||||
// Assert
|
||||
expect(find.byType(ExpandableText), findsOneWidget);
|
||||
expect(find.text('Short text'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('should display ExpandableText with long text', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
const longText =
|
||||
'This is a very long text that should be truncated and show an expand button when it exceeds the maximum number of lines specified in the widget. This text should be long enough to trigger the expand functionality.';
|
||||
const widget = ExpandableText(text: longText, maxLines: 3);
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(widget));
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 200));
|
||||
|
||||
// Assert
|
||||
expect(find.byType(ExpandableText), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('should toggle expansion when expand button is tapped', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
const longText =
|
||||
'This is a very long text that should be truncated and show an expand button when it exceeds the maximum number of lines specified in the widget. This text should be long enough to trigger the expand functionality.';
|
||||
const widget = ExpandableText(text: longText, maxLines: 3);
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(widget));
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 200));
|
||||
|
||||
// Try to find and tap expand button if it exists
|
||||
final expandButtons = find.text('Развернуть');
|
||||
if (expandButtons.evaluate().isNotEmpty) {
|
||||
await tester.tap(expandButtons);
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 200));
|
||||
}
|
||||
|
||||
// Assert
|
||||
expect(find.byType(ExpandableText), findsOneWidget);
|
||||
});
|
||||
});
|
||||
|
||||
group('AuthorCard', () {
|
||||
testWidgets('should display AuthorCard with author information', (WidgetTester tester) async {
|
||||
await mockNetworkImagesFor(() async {
|
||||
// Arrange
|
||||
const author = Author(authorSlug: 'test-author', authorName: 'Test Author', authorProfileIcon: 'https://example.com/icon.jpg', authorIsMe: false);
|
||||
const widget = AuthorCard(author: author, title: 'Автор');
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(widget));
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 100));
|
||||
|
||||
// Assert
|
||||
expect(find.byType(AuthorCard), findsOneWidget);
|
||||
expect(find.text('Test Author'), findsOneWidget);
|
||||
expect(find.text('Автор'), findsOneWidget);
|
||||
});
|
||||
await tester.pump(const Duration(milliseconds: 600));
|
||||
});
|
||||
|
||||
testWidgets('should display AuthorCard with rating when available', (WidgetTester tester) async {
|
||||
await mockNetworkImagesFor(() async {
|
||||
// Arrange
|
||||
const author = Author(
|
||||
authorSlug: 'test-author',
|
||||
authorName: 'Test Author',
|
||||
authorProfileIcon: 'https://example.com/icon.jpg',
|
||||
authorIsMe: false,
|
||||
authorPlaceInRating: 5,
|
||||
);
|
||||
const widget = AuthorCard(author: author, title: 'Автор');
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(widget));
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 100));
|
||||
|
||||
// Assert
|
||||
expect(find.byType(AuthorCard), findsOneWidget);
|
||||
expect(find.text('Test Author'), findsOneWidget);
|
||||
});
|
||||
await tester.pump(const Duration(milliseconds: 600));
|
||||
});
|
||||
});
|
||||
|
||||
group('TypeBadges', () {
|
||||
testWidgets('should display TypeBadges with diet type', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
const dietType = DietType(light: true, veg: false, carb: false, meat: false);
|
||||
const widget = TypeBadges(type: dietType, disableDescriptionOfTypes: false);
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(widget));
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 100));
|
||||
|
||||
// Assert
|
||||
expect(find.byType(TypeBadges), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('should display TypeBadges with plan type', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
const planType = PlanType(fit: true, slim: false, behavior: false, wellness: false);
|
||||
const widget = TypeBadges(planType: planType, disableDescriptionOfTypes: false);
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(widget));
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 100));
|
||||
|
||||
// Assert
|
||||
expect(find.byType(TypeBadges), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('should display TypeBadges with multiple types', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
const dietType = DietType(light: true, veg: true, carb: false, meat: false);
|
||||
const widget = TypeBadges(type: dietType, disableDescriptionOfTypes: false);
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(widget));
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 100));
|
||||
|
||||
// Assert
|
||||
expect(find.byType(TypeBadges), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('should not show overlay when disableDescriptionOfTypes is true', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
const dietType = DietType(light: true, veg: false, carb: false, meat: false);
|
||||
const widget = TypeBadges(type: dietType, disableDescriptionOfTypes: true);
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(widget));
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 100));
|
||||
|
||||
// Assert
|
||||
expect(find.byType(TypeBadges), findsOneWidget);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user