Files
eatvio_app/test/features/create_diet/integration_widget_test.dart
2025-11-13 18:58:56 +03:00

333 lines
14 KiB
Dart

import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/core/locator_service.dart';
import 'package:eatvio_app/core/app_config.dart';
import 'package:eatvio_app/core/app_initializer.dart';
import 'package:eatvio_app/features/create_diet/presentation/widgets/diet_duration_drop_down_widget.dart';
import 'package:eatvio_app/features/create_diet/presentation/widgets/status_drop_down_widget.dart';
import 'package:eatvio_app/features/create_diet/presentation/widgets/diet_type_choice_widget.dart';
import 'package:eatvio_app/features/create_diet/presentation/widgets/diet_image_picker_widget.dart';
import 'package:eatvio_app/core/widgets/custom_outlined_button.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_cubit.dart';
import 'package:eatvio_app/features/create_diet/presentation/create_diet_screen.dart';
import 'package:eatvio_app/features/create_diet/repositories/create_diet_repository.dart';
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
import 'package:eatvio_app/features/glossary/repositories/glossary_repository.dart';
import 'package:eatvio_app/features/profile/repositories/profile_repository.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'integration_widget_test.mocks.dart';
@GenerateMocks([CreateDietRepository, ProfileRepository, GlossaryRepository])
void main() {
group('Diet Creation Integration Widget Tests', () {
late MockCreateDietRepository mockCreateDietRepository;
late MockProfileRepository mockProfileRepository;
late MockGlossaryRepository mockGlossaryRepository;
late CreateDietCubit createDietCubit;
setUp(() async {
await sl.reset();
// Ensure dotenv has required keys before AppConfig constructor reads them
dotenv.loadFromString(envString: 'DEV_BASE_URL=http://localhost\nBASE_URL=http://localhost');
// Register minimal AppConfig for tests to satisfy dependencies (e.g. image URL building)
sl.registerLazySingleton<AppConfig>(() => AppConfig(buildType: AppBuildType.tests)..setBaseUrl('http://localhost'));
mockCreateDietRepository = MockCreateDietRepository();
mockProfileRepository = MockProfileRepository();
mockGlossaryRepository = MockGlossaryRepository();
createDietCubit = CreateDietCubit(
createDietRepository: mockCreateDietRepository,
profileRepository: mockProfileRepository,
glossaryRepository: mockGlossaryRepository,
);
sl.registerLazySingleton<CreateDietCubit>(() => createDietCubit);
when(mockCreateDietRepository.getDietPortionsNutrition(any)).thenAnswer((_) async => <int, Map<int, Map<int, DailySchedule>>>{});
});
tearDown(() {
createDietCubit.close();
});
Widget createTestWidget() {
return MaterialApp(
theme: ThemeHelper().themeData(),
localizationsDelegates: const [
S.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: const [Locale('ru')],
home: BlocProvider<CreateDietCubit>(create: (context) => createDietCubit, child: const CreateDietScreen()),
);
}
testWidgets('should display CreateDietScreen', (WidgetTester tester) async {
// Arrange & Act
await tester.pumpWidget(createTestWidget());
await tester.pumpAndSettle();
// Assert
expect(find.byType(CreateDietScreen), findsOneWidget);
});
testWidgets('should display diet creation form elements', (WidgetTester tester) async {
// Arrange
final arranged = Scaffold(
appBar: AppBar(title: const Text('Создание диеты')),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextFormField(
key: const Key('dietNameField'),
decoration: const InputDecoration(labelText: 'Название диеты', hintText: 'Введите название диеты'),
),
const SizedBox(height: 16),
TextFormField(
key: const Key('dietDescriptionField'),
decoration: const InputDecoration(labelText: 'Описание диеты', hintText: 'Опишите особенности диеты'),
maxLines: 3,
),
const SizedBox(height: 16),
DropdownButtonFormField<int>(
key: const Key('dietDurationField'),
decoration: const InputDecoration(labelText: 'Продолжительность'),
items: const [
DropdownMenuItem(value: 7, child: Text('7 дней')),
DropdownMenuItem(value: 14, child: Text('14 дней')),
DropdownMenuItem(value: 21, child: Text('21 день')),
],
onChanged: (value) {},
),
const SizedBox(height: 16),
DropdownButtonFormField<String>(
key: const Key('dietStatusField'),
decoration: const InputDecoration(labelText: 'Статус диеты'),
items: const [
DropdownMenuItem(value: 'active', child: Text('Активная')),
DropdownMenuItem(value: 'draft', child: Text('Черновик')),
],
onChanged: (value) {},
),
const SizedBox(height: 32),
SizedBox(
width: double.infinity,
child: ElevatedButton(key: const Key('createDietButton'), onPressed: () {}, child: const Text('Создать диету')),
),
],
),
),
),
);
// Act
await tester.pumpWidget(MaterialApp(theme: ThemeHelper().themeData(), home: arranged));
await tester.pumpAndSettle();
// Assert
expect(find.text('Создание диеты'), findsOneWidget);
});
testWidgets('should handle diet creation workflow', (WidgetTester tester) async {
// Arrange
final arranged = Column(
children: [
TextFormField(
key: const Key('dietNameField'),
decoration: const InputDecoration(labelText: 'Название диеты'),
),
const SizedBox(height: 16),
TextFormField(
key: const Key('dietDescriptionField'),
decoration: const InputDecoration(labelText: 'Описание диеты'),
),
const SizedBox(height: 16),
ElevatedButton(key: const Key('createDietButton'), onPressed: () {}, child: const Text('Создать диету')),
],
);
await tester.pumpWidget(
MaterialApp(
theme: ThemeHelper().themeData(),
home: Scaffold(body: SingleChildScrollView(child: arranged)),
),
);
await tester.pumpAndSettle();
// Act - Fill in diet information
await tester.enterText(find.byKey(const Key('dietNameField')), 'Тестовая диета');
await tester.enterText(find.byKey(const Key('dietDescriptionField')), 'Описание тестовой диеты');
await tester.pump();
// Assert
expect(find.text('Тестовая диета'), findsOneWidget);
expect(find.text('Описание тестовой диеты'), findsOneWidget);
});
testWidgets('should validate diet creation form', (WidgetTester tester) async {
// Arrange
final arranged = Column(
children: [
TextFormField(
key: const Key('dietNameField'),
decoration: const InputDecoration(labelText: 'Название диеты', errorText: 'Название диеты обязательно'),
),
const SizedBox(height: 16),
TextFormField(
key: const Key('dietDescriptionField'),
decoration: const InputDecoration(labelText: 'Описание диеты', errorText: 'Описание диеты обязательно'),
),
const SizedBox(height: 16),
ElevatedButton(key: const Key('createDietButton'), onPressed: () {}, child: const Text('Создать диету')),
],
);
// Act
await tester.pumpWidget(
MaterialApp(
theme: ThemeHelper().themeData(),
home: Scaffold(body: arranged),
),
);
await tester.pumpAndSettle();
// Assert
expect(find.text('Название диеты обязательно'), findsOneWidget);
expect(find.text('Описание диеты обязательно'), findsOneWidget);
});
testWidgets('should display diet creation success state', (WidgetTester tester) async {
// Arrange
const arranged = Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.check_circle, size: 64, color: Colors.green),
SizedBox(height: 16),
Text('Диета успешно создана!', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
SizedBox(height: 8),
Text('Ваша диета была сохранена и готова к использованию.'),
],
),
);
// Act
await tester.pumpWidget(MaterialApp(theme: ThemeHelper().themeData(), home: arranged));
await tester.pumpAndSettle();
// Assert
expect(find.text('Диета успешно создана!'), findsOneWidget);
expect(find.text('Ваша диета была сохранена и готова к использованию.'), findsOneWidget);
});
testWidgets('should display diet creation error state', (WidgetTester tester) async {
// Arrange
const arranged = Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.error, size: 64, color: Colors.red),
SizedBox(height: 16),
Text('Ошибка создания диеты', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
SizedBox(height: 8),
Text('Произошла ошибка при создании диеты. Попробуйте еще раз.'),
],
),
);
// Act
await tester.pumpWidget(MaterialApp(theme: ThemeHelper().themeData(), home: arranged));
await tester.pumpAndSettle();
// Assert
expect(find.text('Ошибка создания диеты'), findsOneWidget);
expect(find.text('Произошла ошибка при создании диеты. Попробуйте еще раз.'), findsOneWidget);
});
testWidgets('should display diet creation loading state', (WidgetTester tester) async {
// Arrange
const arranged = Center(
child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [CircularProgressIndicator(), SizedBox(height: 16), Text('Создание диеты...')]),
);
// Act
await tester.pumpWidget(MaterialApp(theme: ThemeHelper().themeData(), home: arranged));
await tester.pump();
// Assert
expect(find.byType(CircularProgressIndicator), findsOneWidget);
expect(find.text('Создание диеты...'), findsOneWidget);
});
testWidgets('should handle diet editing workflow', (WidgetTester tester) async {
// Arrange
const testDiet = Diet(
id: 1,
slug: 'test-diet',
name: 'Тестовая диета',
image: 'base64image',
duration: 7,
state: DietState.active,
type: DietType(meat: true, veg: false, carb: true, light: false),
details: 'Описание тестовой диеты',
dailySchedule: {},
author: Author(authorSlug: 'test-author', authorName: 'Test Author', authorProfileIcon: '', authorIsMe: true, isEntitySubscribedByMe: false),
totalLikes: 0,
totalViews: 0,
totalComments: 0,
wasLikedByMe: false,
isFavorite: false,
dailyNutrition: [],
);
Widget editWidget = MaterialApp(
theme: ThemeHelper().themeData(),
localizationsDelegates: const [
S.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: const [Locale('ru')],
home: BlocProvider<CreateDietCubit>(
create: (context) => createDietCubit,
child: const CreateDietScreen(diet: testDiet),
),
);
// Act
await tester.pumpWidget(editWidget);
await tester.pumpAndSettle();
// Assert
expect(find.byType(CreateDietScreen), findsOneWidget);
});
testWidgets('should display diet creation form with all required fields', (WidgetTester tester) async {
// Act
await tester.pumpWidget(createTestWidget());
await tester.pumpAndSettle();
// Assert
// Title
expect(find.text('Создание диеты'), findsOneWidget);
// Core widgets present on real screen
expect(find.byType(DietDurationDropDownWidget), findsOneWidget);
expect(find.byType(StatusDropDownWidget), findsOneWidget);
expect(find.byType(DietTypeChoiceWidget), findsOneWidget);
expect(find.byType(DietImagePickerWidget), findsOneWidget);
// Action buttons
expect(find.byType(CustomOutlinedButton), findsNWidgets(3));
});
});
}