Files
eatvio_app/test/features/create_recipe/widget_test.dart
2025-10-20 12:04:53 +03:00

369 lines
14 KiB
Dart

import 'package:eatvio_app/app_keys.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/features/create_recipe/presentation/bloc/create_recipe_cubit/create_recipe_cubit.dart';
import 'package:eatvio_app/features/create_recipe/presentation/pages/recipe_create_screen.dart';
import 'package:eatvio_app/features/create_recipe/presentation/widgets/info_about_recipe_widget.dart';
import 'package:eatvio_app/features/create_recipe/presentation/widgets/ingredients_widget.dart';
import 'package:eatvio_app/features/create_recipe/presentation/widgets/steps_widget.dart';
import 'package:eatvio_app/features/create_recipe/reporitories/recipe_repository.dart';
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
import 'package:eatvio_app/features/feed/models/recipes_model.dart';
import 'package:eatvio_app/features/glossary/repositories/glossary_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 'widget_test.mocks.dart';
@GenerateMocks([RecipeRepository, GlossaryRepository])
void main() {
group('RecipeCreateScreen Widget Tests', () {
late MockRecipeRepository mockRecipeRepository;
late MockGlossaryRepository mockGlossaryRepository;
late CreateRecipeCubit createRecipeCubit;
setUp(() {
mockRecipeRepository = MockRecipeRepository();
mockGlossaryRepository = MockGlossaryRepository();
createRecipeCubit = CreateRecipeCubit(recipeRepository: mockRecipeRepository, glossaryRepository: mockGlossaryRepository);
});
tearDown(() {
createRecipeCubit.close();
});
Widget createTestWidget() {
return MaterialApp(
theme: ThemeHelper().themeData(),
home: BlocProvider<CreateRecipeCubit>(create: (context) => createRecipeCubit, child: const RecipeCreateScreen()),
);
}
testWidgets('should display all required form fields', (WidgetTester tester) async {
// Arrange & Act
await tester.pumpWidget(createTestWidget());
await tester.pumpAndSettle();
// Assert
expect(find.byKey(AppKeys.recipeCreateScreenTitleField), findsOneWidget);
expect(find.byKey(AppKeys.recipeCreateScreenTypeDropdown), findsOneWidget);
expect(find.byKey(AppKeys.recipeCreateScreenPortionsField), findsOneWidget);
expect(find.byKey(AppKeys.recipeCreateScreenCaloriesField), findsOneWidget);
expect(find.byKey(AppKeys.recipeCreateScreenTotalHoursField), findsOneWidget);
expect(find.byKey(AppKeys.recipeCreateScreenTotalMinutesField), findsOneWidget);
expect(find.byKey(AppKeys.recipeCreateScreenActiveHoursField), findsOneWidget);
expect(find.byKey(AppKeys.recipeCreateScreenActiveMinutesField), findsOneWidget);
expect(find.byKey(AppKeys.recipeCreateScreenDescriptionField), findsOneWidget);
expect(find.byKey(AppKeys.recipeCreateScreenPublishButton), findsOneWidget);
});
testWidgets('should display recipe creation widgets', (WidgetTester tester) async {
// Arrange & Act
await tester.pumpWidget(createTestWidget());
await tester.pumpAndSettle();
// Assert
expect(find.byType(InfoAboutRecipeWidget), findsOneWidget);
expect(find.byType(IngredientsWidget), findsOneWidget);
expect(find.byType(StepsWidget), findsOneWidget);
});
testWidgets('should update title when user types in title field', (WidgetTester tester) async {
// Arrange
await tester.pumpWidget(createTestWidget());
await tester.pumpAndSettle();
const testTitle = 'Тестовый рецепт';
// Act
await tester.enterText(find.byKey(AppKeys.recipeCreateScreenTitleField), testTitle);
await tester.pump();
// Assert
expect(find.text(testTitle), findsOneWidget);
});
testWidgets('should update portions when user types in portions field', (WidgetTester tester) async {
// Arrange
await tester.pumpWidget(createTestWidget());
await tester.pumpAndSettle();
const testPortions = '4';
// Act
await tester.enterText(find.byKey(AppKeys.recipeCreateScreenPortionsField), testPortions);
await tester.pump();
// Assert
expect(find.text(testPortions), findsOneWidget);
});
testWidgets('should update calories when user types in calories field', (WidgetTester tester) async {
// Arrange
await tester.pumpWidget(createTestWidget());
await tester.pumpAndSettle();
const testCalories = '250';
// Act
await tester.enterText(find.byKey(AppKeys.recipeCreateScreenCaloriesField), testCalories);
await tester.pump();
// Assert
expect(find.text(testCalories), findsOneWidget);
});
testWidgets('should update description when user types in description field', (WidgetTester tester) async {
// Arrange
await tester.pumpWidget(createTestWidget());
await tester.pumpAndSettle();
const testDescription = 'Это тестовое описание рецепта';
// Act
await tester.enterText(find.byKey(AppKeys.recipeCreateScreenDescriptionField), testDescription);
await tester.pump();
// Assert
expect(find.text(testDescription), findsOneWidget);
});
testWidgets('should update cooking time fields', (WidgetTester tester) async {
// Arrange
await tester.pumpWidget(createTestWidget());
await tester.pumpAndSettle();
const testHours = '1';
const testMinutes = '30';
// Act
await tester.enterText(find.byKey(AppKeys.recipeCreateScreenTotalHoursField), testHours);
await tester.enterText(find.byKey(AppKeys.recipeCreateScreenTotalMinutesField), testMinutes);
await tester.pump();
// Assert
expect(find.text(testHours), findsOneWidget);
expect(find.text(testMinutes), findsOneWidget);
});
testWidgets('should update active cooking time fields', (WidgetTester tester) async {
// Arrange
await tester.pumpWidget(createTestWidget());
await tester.pumpAndSettle();
const testActiveHours = '0';
const testActiveMinutes = '45';
// Act
await tester.enterText(find.byKey(AppKeys.recipeCreateScreenActiveHoursField), testActiveHours);
await tester.enterText(find.byKey(AppKeys.recipeCreateScreenActiveMinutesField), testActiveMinutes);
await tester.pump();
// Assert
expect(find.text(testActiveHours), findsOneWidget);
expect(find.text(testActiveMinutes), findsOneWidget);
});
testWidgets('should show validation error for empty title', (WidgetTester tester) async {
// Arrange
await tester.pumpWidget(createTestWidget());
await tester.pumpAndSettle();
// Act - try to publish without title
await tester.tap(find.byKey(AppKeys.recipeCreateScreenPublishButton));
await tester.pump();
// Assert - should show validation error
expect(find.text('Название рецепта обязательно'), findsOneWidget);
});
testWidgets('should show validation error for empty description', (WidgetTester tester) async {
// Arrange
await tester.pumpWidget(createTestWidget());
await tester.pumpAndSettle();
// Fill title but leave description empty
await tester.enterText(find.byKey(AppKeys.recipeCreateScreenTitleField), 'Тестовый рецепт');
await tester.pump();
// Act - try to publish without description
await tester.tap(find.byKey(AppKeys.recipeCreateScreenPublishButton));
await tester.pump();
// Assert - should show validation error
expect(find.text('Описание рецепта обязательно'), findsOneWidget);
});
testWidgets('should show validation error for invalid portions', (WidgetTester tester) async {
// Arrange
await tester.pumpWidget(createTestWidget());
await tester.pumpAndSettle();
// Fill title and description
await tester.enterText(find.byKey(AppKeys.recipeCreateScreenTitleField), 'Тестовый рецепт');
await tester.enterText(find.byKey(AppKeys.recipeCreateScreenDescriptionField), 'Тестовое описание');
// Enter invalid portions (0 or negative)
await tester.enterText(find.byKey(AppKeys.recipeCreateScreenPortionsField), '0');
await tester.pump();
// Act - try to publish with invalid portions
await tester.tap(find.byKey(AppKeys.recipeCreateScreenPublishButton));
await tester.pump();
// Assert - should show validation error
expect(find.text('Количество порций должно быть больше 0'), findsOneWidget);
});
testWidgets('should show validation error for invalid calories', (WidgetTester tester) async {
// Arrange
await tester.pumpWidget(createTestWidget());
await tester.pumpAndSettle();
// Fill required fields
await tester.enterText(find.byKey(AppKeys.recipeCreateScreenTitleField), 'Тестовый рецепт');
await tester.enterText(find.byKey(AppKeys.recipeCreateScreenDescriptionField), 'Тестовое описание');
await tester.enterText(find.byKey(AppKeys.recipeCreateScreenPortionsField), '2');
// Enter invalid calories (negative)
await tester.enterText(find.byKey(AppKeys.recipeCreateScreenCaloriesField), '-100');
await tester.pump();
// Act - try to publish with invalid calories
await tester.tap(find.byKey(AppKeys.recipeCreateScreenPublishButton));
await tester.pump();
// Assert - should show validation error
expect(find.text('Калорийность не может быть отрицательной'), findsOneWidget);
});
testWidgets('should add ingredient when add ingredient button is tapped', (WidgetTester tester) async {
// Arrange
await tester.pumpWidget(createTestWidget());
await tester.pumpAndSettle();
// Act
await tester.tap(find.byKey(AppKeys.recipeCreateScreenAddIngredientButton));
await tester.pumpAndSettle();
// Assert - should show ingredient modal or form
expect(find.byKey(AppKeys.recipeCreateScreenIngredientNameField), findsOneWidget);
expect(find.byKey(AppKeys.recipeCreateScreenIngredientQuantityField), findsOneWidget);
expect(find.byKey(AppKeys.recipeCreateScreenIngredientAddButton), findsOneWidget);
});
testWidgets('should add step when add step button is tapped', (WidgetTester tester) async {
// Arrange
await tester.pumpWidget(createTestWidget());
await tester.pumpAndSettle();
// Act
await tester.tap(find.byKey(AppKeys.recipeCreateScreenAddStepButton));
await tester.pumpAndSettle();
// Assert - should show additional step field
expect(find.byKey(AppKeys.recipeCreateScreenStepDescriptionField), findsAtLeastNWidgets(2));
});
testWidgets('should add tag when add tag button is tapped', (WidgetTester tester) async {
// Arrange
await tester.pumpWidget(createTestWidget());
await tester.pumpAndSettle();
const testTag = 'тестовый тег';
// Act
await tester.enterText(find.byKey(AppKeys.recipeCreateScreenTagField), testTag);
await tester.tap(find.byKey(AppKeys.recipeCreateScreenAddTagButton));
await tester.pumpAndSettle();
// Assert - should show the added tag
expect(find.text(testTag), findsOneWidget);
});
testWidgets('should display loading state when cubit is loading', (WidgetTester tester) async {
// Arrange
final loadingCubit = CreateRecipeCubit(recipeRepository: mockRecipeRepository, glossaryRepository: mockGlossaryRepository);
Widget loadingWidget() {
return MaterialApp(
theme: ThemeHelper().themeData(),
home: BlocProvider<CreateRecipeCubit>(create: (context) => loadingCubit, child: const RecipeCreateScreen()),
);
}
// Act
await tester.pumpWidget(loadingWidget());
await tester.pump();
// Assert - should show loading indicator
expect(find.byType(CircularProgressIndicator), findsOneWidget);
loadingCubit.close();
});
testWidgets('should display app bar with correct title for new recipe', (WidgetTester tester) async {
// Arrange & Act
await tester.pumpWidget(createTestWidget());
await tester.pumpAndSettle();
// Assert
expect(find.text('Добавление рецепта'), findsOneWidget);
});
testWidgets('should display app bar with correct title for editing recipe', (WidgetTester tester) async {
// Arrange
final testRecipe = Recipe(
id: 1,
image: '',
state: 'published',
cookingTime: 30,
activeCookingTime: 15,
ingredients: [],
description: 'Тестовое описание',
steps: [],
videoUrl: null,
createdAt: DateTime.now(),
updatedAt: DateTime.now(),
title: 'Тестовый рецепт',
type: 'salads',
slug: 'test-recipe',
commentsCount: 0,
wasLikedByMe: false,
isFavorite: false,
calories: 200.0,
servings: 2,
likesCount: 0,
viewsCount: 0,
tags: [],
author: Author(
authorSlug: 'test-author',
authorName: 'Test Author',
authorProfileIcon: '',
authorIsMe: false,
isEntitySubscribedByMe: false,
authorPlaceInRating: null,
),
);
Widget editWidget() {
return MaterialApp(
theme: ThemeHelper().themeData(),
home: BlocProvider<CreateRecipeCubit>(
create: (context) => createRecipeCubit,
child: RecipeCreateScreen(recipe: testRecipe),
),
);
}
// Act
await tester.pumpWidget(editWidget());
await tester.pumpAndSettle();
// Assert
expect(find.text('Редактирование рецепта'), findsOneWidget);
});
});
}