Files
eatvio_app/test/features/create_plan/basic_widget_test.dart
2025-11-21 23:24:49 +03:00

629 lines
25 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'dart:io';
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_image_compress/flutter_image_compress.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);
});
testWidgets('should send correct request when creating plan', (WidgetTester tester) async {
// Arrange
final tempFile = File('${Directory.systemTemp.path}/test_image.jpg');
await tempFile.writeAsBytes([0, 1, 2, 3]);
final xFile = XFile(tempFile.path);
createPlanCubit.setName(planFixture['name'] as String);
createPlanCubit.setDetails(planFixture['description'] as String);
createPlanCubit.setDuration(0); // 7 days
createPlanCubit.setPlanState(planFixture['status'] as String);
createPlanCubit.setImage(xFile);
createPlanCubit.setPlanType([true, false, false, false]); // At least one type must be selected
// Set at least one option (required for validation)
final options = planFixture['options'] as List<dynamic>;
if (options.isNotEmpty) {
final firstOption = options[0] as Map<String, dynamic>;
createPlanCubit.setOptionTitle(firstOption['title'] as String);
createPlanCubit.setPrice((firstOption['price'] as num).toDouble());
createPlanCubit.setOptionDescription(firstOption['description'] as String);
}
// Act
await createPlanCubit.publishPlan();
await tester.pumpAndSettle();
// Assert
expect(createPlanRepository.createPlanRequests.length, 1);
final request = createPlanRepository.createPlanRequests.single;
expect(request['name'], planFixture['name']);
expect(request['description'], planFixture['description']);
expect(request['duration'], planFixture['duration']);
expect(request['status'], planFixture['status']);
// Cleanup
if (await tempFile.exists()) {
await tempFile.delete();
}
});
});
}