forked from eatvio/eatvio_app
238 lines
11 KiB
Dart
238 lines
11 KiB
Dart
import 'package:eatvio_app/app_keys.dart';
|
||
import 'package:eatvio_app/features/create_recipe/entities/recipe_create_entity.dart';
|
||
import 'package:eatvio_app/features/create_recipe/presentation/pages/recipe_create_screen.dart';
|
||
import 'package:flutter/material.dart';
|
||
import 'package:flutter_test/flutter_test.dart';
|
||
|
||
/// Helper функции для интеграционных тестов создания рецепта
|
||
|
||
/// Навигация к экрану создания рецепта
|
||
Future<void> navigateToCreateRecipeScreen(WidgetTester tester) async {
|
||
// Переключаемся на вкладку "Рецепты" если она есть
|
||
final recipesTab = find.text('Рецепты');
|
||
if (recipesTab.evaluate().isNotEmpty) {
|
||
await tester.tap(recipesTab);
|
||
await tester.pumpAndSettle();
|
||
}
|
||
|
||
// Находим кнопку добавления рецепта (круглая кнопка с плюсом)
|
||
final addRecipeButton = find.byType(InkWell).first;
|
||
await tester.tap(addRecipeButton);
|
||
await tester.pumpAndSettle(const Duration(seconds: 2));
|
||
}
|
||
|
||
/// Заполняет основную информацию о рецепте
|
||
Future<void> fillRecipeBasicInfo(
|
||
WidgetTester tester, {
|
||
String title = 'Интеграционный Тестовый Рецепт',
|
||
TypeOfRecipe type = TypeOfRecipe.salads,
|
||
int portions = 4,
|
||
double calories = 250.0,
|
||
int totalHours = 1,
|
||
int totalMinutes = 30,
|
||
int activeHours = 0,
|
||
int activeMinutes = 45,
|
||
String description = 'Этот рецепт создан для интеграционного тестирования. Он содержит все необходимые ингредиенты и пошаговую инструкцию приготовления.',
|
||
}) async {
|
||
// Заполняем название рецепта
|
||
final titleField = find.byKey(AppKeys.recipeCreateScreenTitleField);
|
||
await tester.enterText(titleField, title);
|
||
await tester.pumpAndSettle();
|
||
|
||
// Выбираем тип рецепта
|
||
await selectRecipeType(tester, type);
|
||
|
||
// Заполняем количество порций
|
||
final portionsField = find.byKey(AppKeys.recipeCreateScreenPortionsField);
|
||
await tester.enterText(portionsField, portions.toString());
|
||
await tester.pumpAndSettle();
|
||
|
||
// Заполняем калорийность
|
||
final caloriesField = find.byKey(AppKeys.recipeCreateScreenCaloriesField);
|
||
await tester.enterText(caloriesField, calories.toString());
|
||
await tester.pumpAndSettle();
|
||
|
||
// Заполняем общее время приготовления
|
||
final totalHoursField = find.byKey(AppKeys.recipeCreateScreenTotalHoursField);
|
||
await tester.enterText(totalHoursField, totalHours.toString());
|
||
await tester.pumpAndSettle();
|
||
|
||
final totalMinutesField = find.byKey(AppKeys.recipeCreateScreenTotalMinutesField);
|
||
await tester.enterText(totalMinutesField, totalMinutes.toString());
|
||
await tester.pumpAndSettle();
|
||
|
||
// Заполняем активное время приготовления
|
||
final activeHoursField = find.byKey(AppKeys.recipeCreateScreenActiveHoursField);
|
||
await tester.enterText(activeHoursField, activeHours.toString());
|
||
await tester.pumpAndSettle();
|
||
|
||
final activeMinutesField = find.byKey(AppKeys.recipeCreateScreenActiveMinutesField);
|
||
await tester.enterText(activeMinutesField, activeMinutes.toString());
|
||
await tester.pumpAndSettle();
|
||
|
||
// Заполняем описание
|
||
final descriptionField = find.byKey(AppKeys.recipeCreateScreenDescriptionField);
|
||
await tester.enterText(descriptionField, description);
|
||
await tester.pumpAndSettle();
|
||
}
|
||
|
||
/// Выбирает тип рецепта из выпадающего списка
|
||
Future<void> selectRecipeType(WidgetTester tester, TypeOfRecipe type) async {
|
||
final typeDropdown = find.byKey(AppKeys.recipeCreateScreenTypeDropdown);
|
||
await tester.tap(typeDropdown);
|
||
await tester.pumpAndSettle();
|
||
|
||
// Выбираем нужный тип из выпадающего списка
|
||
final typeOption = find.text(type.toString());
|
||
await tester.tap(typeOption);
|
||
await tester.pumpAndSettle();
|
||
}
|
||
|
||
/// Добавляет теги к рецепту
|
||
Future<void> addTags(WidgetTester tester, List<String> tags) async {
|
||
final tagField = find.byKey(AppKeys.recipeCreateScreenTagField);
|
||
final addTagButton = find.byKey(AppKeys.recipeCreateScreenAddTagButton);
|
||
|
||
for (final tag in tags) {
|
||
await tester.enterText(tagField, tag);
|
||
await tester.pumpAndSettle();
|
||
await tester.tap(addTagButton);
|
||
await tester.pumpAndSettle();
|
||
}
|
||
}
|
||
|
||
/// Добавляет ингредиенты к рецепту
|
||
Future<void> addIngredients(WidgetTester tester, List<Map<String, String>> ingredients) async {
|
||
final addIngredientButton = find.byKey(AppKeys.recipeCreateScreenAddIngredientButton);
|
||
|
||
for (final ingredient in ingredients) {
|
||
await tester.ensureVisible(addIngredientButton);
|
||
await tester.tap(addIngredientButton);
|
||
await tester.pumpAndSettle();
|
||
|
||
await addIngredientInModal(tester, ingredient['name']!, ingredient['quantity']!);
|
||
await tester.pumpAndSettle();
|
||
}
|
||
}
|
||
|
||
/// Добавляет ингредиент в модальном окне
|
||
Future<void> addIngredientInModal(WidgetTester tester, String name, String quantity) async {
|
||
// Ищем поля в модальном окне
|
||
final nameFields = find.byType(TextFormField);
|
||
if (nameFields.evaluate().isNotEmpty) {
|
||
await tester.enterText(nameFields.first, name);
|
||
await tester.pumpAndSettle();
|
||
}
|
||
|
||
// Ищем поле количества (второе поле)
|
||
if (nameFields.evaluate().length > 1) {
|
||
await tester.enterText(nameFields.at(1), quantity);
|
||
await tester.pumpAndSettle();
|
||
}
|
||
|
||
// Ищем кнопку добавления в модальном окне
|
||
final addButton = find.text('Добавить');
|
||
if (addButton.evaluate().isNotEmpty) {
|
||
await tester.tap(addButton);
|
||
await tester.pumpAndSettle();
|
||
}
|
||
}
|
||
|
||
/// Добавляет шаги приготовления
|
||
Future<void> addCookingSteps(WidgetTester tester, List<Map<String, String>> steps) async {
|
||
final addStepButton = find.byKey(AppKeys.recipeCreateScreenAddStepButton);
|
||
|
||
for (int i = 0; i < steps.length; i++) {
|
||
final step = steps[i];
|
||
|
||
// Заполняем описание шага
|
||
final stepDescription = find.byKey(Key('${AppKeys.recipeCreateScreenStepDescriptionField}_$i'));
|
||
await tester.enterText(stepDescription, step['description']!);
|
||
await tester.pumpAndSettle();
|
||
|
||
// Симулируем добавление изображения к шагу если указано
|
||
if (step.containsKey('hasImage') && step['hasImage'] == 'true') {
|
||
await mockStepImageSelection(tester, i);
|
||
}
|
||
|
||
// Добавляем следующий шаг (кроме последнего)
|
||
if (i < steps.length - 1) {
|
||
await tester.ensureVisible(addStepButton);
|
||
await tester.tap(addStepButton);
|
||
await tester.pumpAndSettle();
|
||
}
|
||
}
|
||
}
|
||
|
||
/// Публикует рецепт
|
||
Future<void> publishRecipe(WidgetTester tester) async {
|
||
final publishButton = find.byKey(AppKeys.recipeCreateScreenPublishButton);
|
||
await tester.ensureVisible(publishButton);
|
||
await tester.tap(publishButton);
|
||
await tester.pumpAndSettle(const Duration(seconds: 5)); // Дать время на API-запрос
|
||
}
|
||
|
||
/// Симулирует выбор изображения для основного рецепта
|
||
Future<void> mockImageSelection(WidgetTester tester) async {
|
||
final imageWidget = find.byKey(AppKeys.recipeCreateScreenImageWidget);
|
||
if (imageWidget.evaluate().isNotEmpty) {
|
||
await tester.tap(imageWidget);
|
||
await tester.pumpAndSettle();
|
||
|
||
// В реальном интеграционном тесте здесь будет мок image_picker
|
||
// который возвращает тестовое изображение
|
||
}
|
||
}
|
||
|
||
/// Симулирует выбор изображения для шага приготовления
|
||
Future<void> mockStepImageSelection(WidgetTester tester, int stepIndex) async {
|
||
final stepImageWidget = find.byKey(Key('${AppKeys.recipeCreateScreenStepImageWidget}_$stepIndex'));
|
||
if (stepImageWidget.evaluate().isNotEmpty) {
|
||
await tester.tap(stepImageWidget);
|
||
await tester.pumpAndSettle();
|
||
|
||
// В реальном интеграционном тесте здесь будет мок image_picker
|
||
// который возвращает тестовое изображение
|
||
}
|
||
}
|
||
|
||
/// Проверяет, что рецепт был успешно опубликован
|
||
void verifyRecipePublished(WidgetTester tester) {
|
||
// После успешной публикации должен произойти переход обратно на экран ленты
|
||
expect(find.byKey(AppKeys.recipeCreateScreenPublishButton), findsNothing);
|
||
// Можно добавить дополнительные проверки в зависимости от того, куда происходит переход
|
||
}
|
||
|
||
/// Проверяет, что валидация полей работает корректно
|
||
void verifyValidationErrors(WidgetTester tester) {
|
||
// Проверяем, что остались на экране создания рецепта (валидация не прошла)
|
||
expect(find.byType(RecipeCreateScreen), findsOneWidget);
|
||
expect(find.byKey(AppKeys.recipeCreateScreenPublishButton), findsOneWidget);
|
||
}
|
||
|
||
/// Создает тестовые данные для рецепта
|
||
Map<String, dynamic> createTestRecipeData() {
|
||
return {
|
||
'title': 'Интеграционный Тестовый Рецепт',
|
||
'type': TypeOfRecipe.salads,
|
||
'portions': 4,
|
||
'calories': 250.0,
|
||
'totalHours': 1,
|
||
'totalMinutes': 30,
|
||
'activeHours': 0,
|
||
'activeMinutes': 45,
|
||
'description': 'Этот рецепт создан для интеграционного тестирования. Он содержит все необходимые ингредиенты и пошаговую инструкцию приготовления.',
|
||
'tags': ['здоровое питание', 'быстро', 'легко'],
|
||
'ingredients': [
|
||
{'name': 'Помидоры', 'quantity': '200'},
|
||
{'name': 'Огурцы', 'quantity': '150'},
|
||
{'name': 'Лук', 'quantity': '50'},
|
||
],
|
||
'steps': [
|
||
{'description': 'Помойте и нарежьте помидоры и огурцы кубиками среднего размера.', 'hasImage': 'true'},
|
||
{'description': 'Нарежьте лук полукольцами и добавьте к овощам.', 'hasImage': 'false'},
|
||
{'description': 'Смешайте все ингредиенты в большой миске, добавьте соль и перец по вкусу.', 'hasImage': 'true'},
|
||
],
|
||
};
|
||
}
|