forked from eatvio/eatvio_app
587 lines
23 KiB
Dart
587 lines
23 KiB
Dart
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||
import 'package:eatvio_app/features/create_plan/presentation/cubit/create_plan_cubit.dart';
|
||
import 'package:flutter/material.dart';
|
||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||
import 'package:flutter_test/flutter_test.dart';
|
||
|
||
import '../../fixtures/doubles/create_plan_doubles.dart';
|
||
import '../../fixtures/fixture_reader.dart';
|
||
|
||
void main() {
|
||
group('Meal Plan Creation Basic Widget Tests', () {
|
||
late FixtureCreatePlanRepository createPlanRepository;
|
||
late CreatePlanCubit createPlanCubit;
|
||
late Map<String, dynamic> planFixture;
|
||
|
||
setUp(() {
|
||
planFixture = FixtureReader.readAsMap('create_plan/create_plan_request.json');
|
||
createPlanRepository = FixtureCreatePlanRepository();
|
||
createPlanCubit = CreatePlanCubit(createPlanRepository: createPlanRepository);
|
||
});
|
||
|
||
tearDown(() {
|
||
createPlanCubit.close();
|
||
});
|
||
|
||
Widget createTestWidget(Widget child) {
|
||
return MaterialApp(
|
||
theme: ThemeHelper().themeData(),
|
||
home: BlocProvider<CreatePlanCubit>(
|
||
create: (context) => createPlanCubit,
|
||
child: Scaffold(body: SingleChildScrollView(child: child)),
|
||
),
|
||
);
|
||
}
|
||
|
||
testWidgets('should create CreatePlanCubit successfully', (WidgetTester tester) async {
|
||
// Arrange & Act
|
||
final cubit = CreatePlanCubit(createPlanRepository: createPlanRepository);
|
||
|
||
// Assert
|
||
expect(cubit, isA<CreatePlanCubit>());
|
||
expect(cubit.state.isLoading, isFalse);
|
||
expect(cubit.state.planCreated, isFalse);
|
||
|
||
cubit.close();
|
||
});
|
||
|
||
testWidgets('should display basic meal plan form fields', (WidgetTester tester) async {
|
||
// Arrange
|
||
Widget formWidget = Column(
|
||
children: [
|
||
TextFormField(
|
||
key: const Key('planNameField'),
|
||
decoration: const InputDecoration(labelText: 'Название плана питания', hintText: 'Например: План для похудения'),
|
||
),
|
||
const SizedBox(height: 16),
|
||
TextFormField(
|
||
key: const Key('planDescriptionField'),
|
||
decoration: const InputDecoration(labelText: 'Описание плана питания'),
|
||
maxLines: 3,
|
||
),
|
||
const SizedBox(height: 16),
|
||
DropdownButtonFormField<int>(
|
||
key: const Key('planDurationField'),
|
||
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),
|
||
ElevatedButton(key: const Key('publishPlanButton'), onPressed: () {}, child: const Text('Опубликовать план')),
|
||
],
|
||
);
|
||
|
||
// Act
|
||
await tester.pumpWidget(createTestWidget(formWidget));
|
||
await tester.pumpAndSettle();
|
||
|
||
// Assert
|
||
expect(find.byKey(const Key('planNameField')), findsOneWidget);
|
||
expect(find.byKey(const Key('planDescriptionField')), findsOneWidget);
|
||
expect(find.byKey(const Key('planDurationField')), findsOneWidget);
|
||
expect(find.byKey(const Key('publishPlanButton')), findsOneWidget);
|
||
});
|
||
|
||
testWidgets('should update plan name when user types in name field', (WidgetTester tester) async {
|
||
// Arrange
|
||
Widget formWidget = TextFormField(
|
||
key: const Key('planNameField'),
|
||
decoration: const InputDecoration(labelText: 'Название плана питания', hintText: 'Например: План для похудения'),
|
||
);
|
||
|
||
await tester.pumpWidget(createTestWidget(formWidget));
|
||
await tester.pumpAndSettle();
|
||
|
||
final testName = planFixture['name'] as String;
|
||
|
||
// Act
|
||
await tester.enterText(find.byKey(const Key('planNameField')), testName);
|
||
await tester.pump();
|
||
|
||
// Assert
|
||
expect(find.text(testName), findsOneWidget);
|
||
});
|
||
|
||
testWidgets('should update plan description when user types in description field', (WidgetTester tester) async {
|
||
// Arrange
|
||
Widget formWidget = TextFormField(
|
||
key: const Key('planDescriptionField'),
|
||
decoration: const InputDecoration(labelText: 'Описание плана питания'),
|
||
maxLines: 3,
|
||
);
|
||
|
||
await tester.pumpWidget(createTestWidget(formWidget));
|
||
await tester.pumpAndSettle();
|
||
|
||
final testDescription = planFixture['description'] as String;
|
||
|
||
// Act
|
||
await tester.enterText(find.byKey(const Key('planDescriptionField')), testDescription);
|
||
await tester.pump();
|
||
|
||
// Assert
|
||
expect(find.text(testDescription), findsOneWidget);
|
||
});
|
||
|
||
testWidgets('should display plan type selection', (WidgetTester tester) async {
|
||
// Arrange
|
||
Widget typeWidget = Column(
|
||
children: [
|
||
const Text('Тип плана питания'),
|
||
const SizedBox(height: 8),
|
||
Row(
|
||
children: [
|
||
Expanded(
|
||
child: CheckboxListTile(key: const Key('slimTypeCheckbox'), title: const Text('Похудение'), value: false, onChanged: (value) {}),
|
||
),
|
||
Expanded(
|
||
child: CheckboxListTile(key: const Key('fitnessTypeCheckbox'), title: const Text('Фитнес'), value: false, onChanged: (value) {}),
|
||
),
|
||
],
|
||
),
|
||
Row(
|
||
children: [
|
||
Expanded(
|
||
child: CheckboxListTile(key: const Key('wellnessTypeCheckbox'), title: const Text('Здоровье'), value: false, onChanged: (value) {}),
|
||
),
|
||
Expanded(
|
||
child: CheckboxListTile(key: const Key('eatingBehaviorTypeCheckbox'), title: const Text('Поведение'), value: false, onChanged: (value) {}),
|
||
),
|
||
],
|
||
),
|
||
],
|
||
);
|
||
|
||
// Act
|
||
await tester.pumpWidget(createTestWidget(typeWidget));
|
||
await tester.pumpAndSettle();
|
||
|
||
// Assert
|
||
expect(find.text('Тип плана питания'), findsOneWidget);
|
||
expect(find.byKey(const Key('slimTypeCheckbox')), findsOneWidget);
|
||
expect(find.byKey(const Key('fitnessTypeCheckbox')), findsOneWidget);
|
||
expect(find.byKey(const Key('wellnessTypeCheckbox')), findsOneWidget);
|
||
expect(find.byKey(const Key('eatingBehaviorTypeCheckbox')), findsOneWidget);
|
||
});
|
||
|
||
testWidgets('should handle plan type selection', (WidgetTester tester) async {
|
||
// Arrange
|
||
bool slimSelected = false;
|
||
Widget typeWidget = CheckboxListTile(
|
||
key: const Key('slimTypeCheckbox'),
|
||
title: const Text('Похудение'),
|
||
value: slimSelected,
|
||
onChanged: (value) {
|
||
slimSelected = value ?? false;
|
||
},
|
||
);
|
||
|
||
await tester.pumpWidget(createTestWidget(typeWidget));
|
||
await tester.pumpAndSettle();
|
||
|
||
// Act
|
||
await tester.tap(find.byKey(const Key('slimTypeCheckbox')));
|
||
await tester.pump();
|
||
|
||
// Assert
|
||
expect(slimSelected, isTrue);
|
||
});
|
||
|
||
testWidgets('should display plan duration dropdown', (WidgetTester tester) async {
|
||
// Arrange
|
||
Widget durationWidget = DropdownButtonFormField<int>(
|
||
key: const Key('planDurationField'),
|
||
decoration: const InputDecoration(labelText: 'Продолжительность плана'),
|
||
items: const [
|
||
DropdownMenuItem(value: 7, child: Text('7 дней')),
|
||
DropdownMenuItem(value: 14, child: Text('14 дней')),
|
||
DropdownMenuItem(value: 21, child: Text('21 день')),
|
||
DropdownMenuItem(value: 30, child: Text('30 дней')),
|
||
],
|
||
onChanged: (value) {},
|
||
);
|
||
|
||
// Act
|
||
await tester.pumpWidget(createTestWidget(durationWidget));
|
||
await tester.pumpAndSettle();
|
||
|
||
// Assert
|
||
expect(find.byKey(const Key('planDurationField')), findsOneWidget);
|
||
expect(find.text('Продолжительность плана'), findsOneWidget);
|
||
});
|
||
|
||
testWidgets('should display plan status dropdown', (WidgetTester tester) async {
|
||
// Arrange
|
||
Widget statusWidget = DropdownButtonFormField<String>(
|
||
key: const Key('planStatusField'),
|
||
decoration: const InputDecoration(labelText: 'Статус плана'),
|
||
items: const [
|
||
DropdownMenuItem(value: 'active', child: Text('Активный')),
|
||
DropdownMenuItem(value: 'draft', child: Text('Черновик')),
|
||
DropdownMenuItem(value: 'personal', child: Text('Личный')),
|
||
],
|
||
onChanged: (value) {},
|
||
);
|
||
|
||
// Act
|
||
await tester.pumpWidget(createTestWidget(statusWidget));
|
||
await tester.pumpAndSettle();
|
||
|
||
// Assert
|
||
expect(find.byKey(const Key('planStatusField')), findsOneWidget);
|
||
expect(find.text('Статус плана'), findsOneWidget);
|
||
});
|
||
|
||
testWidgets('should display plan options section', (WidgetTester tester) async {
|
||
// Arrange
|
||
Widget optionsWidget = Column(
|
||
children: [
|
||
const Text('Опции плана питания'),
|
||
const SizedBox(height: 16),
|
||
Container(
|
||
key: const Key('option1Container'),
|
||
padding: const EdgeInsets.all(16),
|
||
decoration: BoxDecoration(
|
||
border: Border.all(color: Colors.grey),
|
||
borderRadius: BorderRadius.circular(8),
|
||
),
|
||
child: Column(
|
||
children: [
|
||
TextFormField(
|
||
key: const Key('option1TitleField'),
|
||
decoration: const InputDecoration(labelText: 'Название опции', hintText: 'Например: Базовый план'),
|
||
),
|
||
const SizedBox(height: 8),
|
||
TextFormField(
|
||
key: const Key('option1PriceField'),
|
||
decoration: const InputDecoration(labelText: 'Цена', hintText: '1000'),
|
||
keyboardType: TextInputType.number,
|
||
),
|
||
const SizedBox(height: 8),
|
||
TextFormField(
|
||
key: const Key('option1DescriptionField'),
|
||
decoration: const InputDecoration(labelText: 'Описание опции'),
|
||
maxLines: 2,
|
||
),
|
||
],
|
||
),
|
||
),
|
||
],
|
||
);
|
||
|
||
// Act
|
||
await tester.pumpWidget(createTestWidget(optionsWidget));
|
||
await tester.pumpAndSettle();
|
||
|
||
// Assert
|
||
expect(find.text('Опции плана питания'), findsOneWidget);
|
||
expect(find.byKey(const Key('option1Container')), findsOneWidget);
|
||
expect(find.byKey(const Key('option1TitleField')), findsOneWidget);
|
||
expect(find.byKey(const Key('option1PriceField')), findsOneWidget);
|
||
expect(find.byKey(const Key('option1DescriptionField')), findsOneWidget);
|
||
});
|
||
|
||
testWidgets('should display form labels correctly', (WidgetTester tester) async {
|
||
// Arrange
|
||
Widget formWidget = const Column(
|
||
children: [
|
||
Text('Название плана питания'),
|
||
Text('Описание плана питания'),
|
||
Text('Продолжительность'),
|
||
Text('Тип плана питания'),
|
||
Text('Статус'),
|
||
Text('Опции плана'),
|
||
],
|
||
);
|
||
|
||
// Act
|
||
await tester.pumpWidget(createTestWidget(formWidget));
|
||
await tester.pumpAndSettle();
|
||
|
||
// Assert
|
||
expect(find.text('Название плана питания'), findsOneWidget);
|
||
expect(find.text('Описание плана питания'), findsOneWidget);
|
||
expect(find.text('Продолжительность'), findsOneWidget);
|
||
expect(find.text('Тип плана питания'), findsOneWidget);
|
||
expect(find.text('Статус'), findsOneWidget);
|
||
expect(find.text('Опции плана'), findsOneWidget);
|
||
});
|
||
|
||
testWidgets('should display placeholder text in name field', (WidgetTester tester) async {
|
||
// Arrange
|
||
Widget formWidget = TextFormField(
|
||
key: const Key('planNameField'),
|
||
decoration: const InputDecoration(labelText: 'Название плана питания', hintText: 'Например: План для похудения'),
|
||
);
|
||
|
||
// Act
|
||
await tester.pumpWidget(createTestWidget(formWidget));
|
||
await tester.pumpAndSettle();
|
||
|
||
// Assert
|
||
expect(find.text('Например: План для похудения'), findsOneWidget);
|
||
});
|
||
|
||
testWidgets('should display publish button', (WidgetTester tester) async {
|
||
// Arrange
|
||
Widget buttonWidget = ElevatedButton(key: const Key('publishPlanButton'), onPressed: () {}, child: const Text('Опубликовать план'));
|
||
|
||
// Act
|
||
await tester.pumpWidget(createTestWidget(buttonWidget));
|
||
await tester.pumpAndSettle();
|
||
|
||
// Assert
|
||
expect(find.byKey(const Key('publishPlanButton')), findsOneWidget);
|
||
expect(find.text('Опубликовать план'), findsOneWidget);
|
||
});
|
||
|
||
testWidgets('should handle publish button tap', (WidgetTester tester) async {
|
||
// Arrange
|
||
bool buttonPressed = false;
|
||
Widget buttonWidget = ElevatedButton(
|
||
key: const Key('publishPlanButton'),
|
||
onPressed: () {
|
||
buttonPressed = true;
|
||
},
|
||
child: const Text('Опубликовать план'),
|
||
);
|
||
|
||
await tester.pumpWidget(createTestWidget(buttonWidget));
|
||
await tester.pumpAndSettle();
|
||
|
||
// Act
|
||
await tester.tap(find.byKey(const Key('publishPlanButton')));
|
||
await tester.pump();
|
||
|
||
// Assert
|
||
expect(buttonPressed, isTrue);
|
||
});
|
||
|
||
testWidgets('should display loading state', (WidgetTester tester) async {
|
||
// Arrange
|
||
Widget loadingWidget = const Center(child: CircularProgressIndicator());
|
||
|
||
// Act
|
||
await tester.pumpWidget(createTestWidget(loadingWidget));
|
||
await tester.pump();
|
||
|
||
// Assert
|
||
expect(find.byType(CircularProgressIndicator), findsOneWidget);
|
||
});
|
||
|
||
testWidgets('should display error message', (WidgetTester tester) async {
|
||
// Arrange
|
||
Widget errorWidget = const Center(child: Text('Произошла ошибка при создании плана питания'));
|
||
|
||
// Act
|
||
await tester.pumpWidget(createTestWidget(errorWidget));
|
||
await tester.pumpAndSettle();
|
||
|
||
// Assert
|
||
expect(find.text('Произошла ошибка при создании плана питания'), findsOneWidget);
|
||
});
|
||
|
||
testWidgets('should display success message', (WidgetTester tester) async {
|
||
// Arrange
|
||
Widget successWidget = const Center(child: Text('План питания успешно создан!'));
|
||
|
||
// Act
|
||
await tester.pumpWidget(createTestWidget(successWidget));
|
||
await tester.pumpAndSettle();
|
||
|
||
// Assert
|
||
expect(find.text('План питания успешно создан!'), findsOneWidget);
|
||
});
|
||
|
||
testWidgets('should validate required fields', (WidgetTester tester) async {
|
||
// Arrange
|
||
Widget validationWidget = Column(
|
||
children: [
|
||
TextFormField(
|
||
key: const Key('planNameField'),
|
||
decoration: const InputDecoration(labelText: 'Название плана питания', errorText: 'Название плана питания обязательно'),
|
||
),
|
||
const SizedBox(height: 16),
|
||
TextFormField(
|
||
key: const Key('planDescriptionField'),
|
||
decoration: const InputDecoration(labelText: 'Описание плана питания', errorText: 'Описание плана питания обязательно'),
|
||
),
|
||
],
|
||
);
|
||
|
||
// Act
|
||
await tester.pumpWidget(createTestWidget(validationWidget));
|
||
await tester.pumpAndSettle();
|
||
|
||
// Assert
|
||
expect(find.text('Название плана питания обязательно'), findsOneWidget);
|
||
expect(find.text('Описание плана питания обязательно'), findsOneWidget);
|
||
});
|
||
|
||
testWidgets('should display complete meal plan creation form', (WidgetTester tester) async {
|
||
// Arrange
|
||
Widget completeForm = Column(
|
||
children: [
|
||
TextFormField(
|
||
key: const Key('planNameField'),
|
||
decoration: const InputDecoration(labelText: 'Название плана питания', hintText: 'Например: План для похудения'),
|
||
),
|
||
const SizedBox(height: 16),
|
||
TextFormField(
|
||
key: const Key('planDescriptionField'),
|
||
decoration: const InputDecoration(labelText: 'Описание плана питания'),
|
||
maxLines: 3,
|
||
),
|
||
const SizedBox(height: 16),
|
||
DropdownButtonFormField<int>(
|
||
key: const Key('planDurationField'),
|
||
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('planStatusField'),
|
||
decoration: const InputDecoration(labelText: 'Статус плана'),
|
||
items: const [
|
||
DropdownMenuItem(value: 'active', child: Text('Активный')),
|
||
DropdownMenuItem(value: 'draft', child: Text('Черновик')),
|
||
],
|
||
onChanged: (value) {},
|
||
),
|
||
const SizedBox(height: 32),
|
||
ElevatedButton(key: const Key('publishPlanButton'), onPressed: () {}, child: const Text('Опубликовать план')),
|
||
],
|
||
);
|
||
|
||
// Act
|
||
await tester.pumpWidget(createTestWidget(completeForm));
|
||
await tester.pumpAndSettle();
|
||
|
||
// Assert
|
||
expect(find.byKey(const Key('planNameField')), findsOneWidget);
|
||
expect(find.byKey(const Key('planDescriptionField')), findsOneWidget);
|
||
expect(find.byKey(const Key('planDurationField')), findsOneWidget);
|
||
expect(find.byKey(const Key('planStatusField')), findsOneWidget);
|
||
expect(find.byKey(const Key('publishPlanButton')), findsOneWidget);
|
||
});
|
||
|
||
testWidgets('should display plan type options', (WidgetTester tester) async {
|
||
// Arrange
|
||
Widget typeOptionsWidget = const Column(
|
||
children: [
|
||
Text('Выберите тип плана питания:'),
|
||
SizedBox(height: 8),
|
||
Text('• Похудение - план для снижения веса'),
|
||
Text('• Фитнес - план для активного образа жизни'),
|
||
Text('• Здоровье - план для общего оздоровления'),
|
||
Text('• Поведение - план для изменения пищевых привычек'),
|
||
],
|
||
);
|
||
|
||
// Act
|
||
await tester.pumpWidget(createTestWidget(typeOptionsWidget));
|
||
await tester.pumpAndSettle();
|
||
|
||
// Assert
|
||
expect(find.text('Выберите тип плана питания:'), findsOneWidget);
|
||
expect(find.text('• Похудение - план для снижения веса'), findsOneWidget);
|
||
expect(find.text('• Фитнес - план для активного образа жизни'), findsOneWidget);
|
||
expect(find.text('• Здоровье - план для общего оздоровления'), findsOneWidget);
|
||
expect(find.text('• Поведение - план для изменения пищевых привычек'), findsOneWidget);
|
||
});
|
||
|
||
testWidgets('should display plan duration options', (WidgetTester tester) async {
|
||
// Arrange
|
||
Widget durationOptionsWidget = const Column(
|
||
children: [
|
||
Text('Доступные продолжительности:'),
|
||
SizedBox(height: 8),
|
||
Text('• 7 дней - недельный план'),
|
||
Text('• 14 дней - двухнедельный план'),
|
||
Text('• 21 день - трехнедельный план'),
|
||
Text('• 30 дней - месячный план'),
|
||
],
|
||
);
|
||
|
||
// Act
|
||
await tester.pumpWidget(createTestWidget(durationOptionsWidget));
|
||
await tester.pumpAndSettle();
|
||
|
||
// Assert
|
||
expect(find.text('Доступные продолжительности:'), findsOneWidget);
|
||
expect(find.text('• 7 дней - недельный план'), findsOneWidget);
|
||
expect(find.text('• 14 дней - двухнедельный план'), findsOneWidget);
|
||
expect(find.text('• 21 день - трехнедельный план'), findsOneWidget);
|
||
expect(find.text('• 30 дней - месячный план'), findsOneWidget);
|
||
});
|
||
|
||
testWidgets('should display plan options with pricing', (WidgetTester tester) async {
|
||
// Arrange
|
||
Widget optionsWidget = Column(
|
||
children: [
|
||
const Text('Настройка опций плана'),
|
||
const SizedBox(height: 16),
|
||
Container(
|
||
key: const Key('basicOptionContainer'),
|
||
padding: const EdgeInsets.all(16),
|
||
decoration: BoxDecoration(
|
||
border: Border.all(color: Colors.blue),
|
||
borderRadius: BorderRadius.circular(8),
|
||
),
|
||
child: const Column(
|
||
children: [
|
||
Text('Базовая опция', style: TextStyle(fontWeight: FontWeight.bold)),
|
||
SizedBox(height: 8),
|
||
Text('Цена: 1000 руб/месяц'),
|
||
SizedBox(height: 8),
|
||
Text('Включает: базовое меню, консультации'),
|
||
],
|
||
),
|
||
),
|
||
const SizedBox(height: 16),
|
||
Container(
|
||
key: const Key('premiumOptionContainer'),
|
||
padding: const EdgeInsets.all(16),
|
||
decoration: BoxDecoration(
|
||
border: Border.all(color: Colors.amber),
|
||
borderRadius: BorderRadius.circular(8),
|
||
),
|
||
child: const Column(
|
||
children: [
|
||
Text('Премиум опция', style: TextStyle(fontWeight: FontWeight.bold)),
|
||
SizedBox(height: 8),
|
||
Text('Цена: 2500 руб/месяц'),
|
||
SizedBox(height: 8),
|
||
Text('Включает: расширенное меню, персональные консультации'),
|
||
],
|
||
),
|
||
),
|
||
],
|
||
);
|
||
|
||
// Act
|
||
await tester.pumpWidget(createTestWidget(optionsWidget));
|
||
await tester.pumpAndSettle();
|
||
|
||
// Assert
|
||
expect(find.text('Настройка опций плана'), findsOneWidget);
|
||
expect(find.text('Базовая опция'), findsOneWidget);
|
||
expect(find.text('Цена: 1000 руб/месяц'), findsOneWidget);
|
||
expect(find.text('Премиум опция'), findsOneWidget);
|
||
expect(find.text('Цена: 2500 руб/месяц'), findsOneWidget);
|
||
expect(find.byKey(const Key('basicOptionContainer')), findsOneWidget);
|
||
expect(find.byKey(const Key('premiumOptionContainer')), findsOneWidget);
|
||
});
|
||
|
||
});
|
||
}
|