forked from eatvio/eatvio_app
Merge pull request 'feat(test): diet, plan, registration' (#741) from mso7nueh/eatvio_app:main into main
Reviewed-on: eatvio/eatvio_app#741
This commit is contained in:
133
test/features/blog_post_create/README.md
Normal file
133
test/features/blog_post_create/README.md
Normal file
@@ -0,0 +1,133 @@
|
||||
# Blog Post Creation Tests
|
||||
|
||||
Этот набор тестов покрывает функциональность создания, обновления и удаления блог постов в приложении EatVio.
|
||||
|
||||
## Структура тестов
|
||||
|
||||
### 1. Basic Widget Tests (`basic_widget_test.dart`)
|
||||
|
||||
Базовые виджет тесты для проверки основных компонентов создания блог постов:
|
||||
|
||||
- **Создание BlogPostCreateCubit** - проверка инициализации cubit
|
||||
- **Отображение полей формы** - проверка наличия всех необходимых полей
|
||||
- **Обновление полей** - проверка ввода текста в поля заголовка, описания и видео
|
||||
- **Отображение лейблов** - проверка корректности отображения подписей полей
|
||||
- **Placeholder текст** - проверка подсказок в полях ввода
|
||||
- **Кнопка создания** - проверка отображения и функциональности кнопки
|
||||
- **Обработка нажатий** - проверка реакции на нажатия кнопок
|
||||
- **Состояния загрузки** - проверка отображения индикатора загрузки
|
||||
- **Сообщения об ошибках** - проверка отображения ошибок
|
||||
- **Сообщения об успехе** - проверка отображения успешных операций
|
||||
- **Валидация полей** - проверка валидации обязательных полей
|
||||
- **Полная форма** - проверка отображения всех полей формы
|
||||
- **Загрузка изображений** - проверка функциональности загрузки изображений
|
||||
|
||||
### 2. Advanced Widget Tests (`advanced_widget_test.dart`)
|
||||
|
||||
Продвинутые тесты для проверки сложных сценариев:
|
||||
|
||||
- **Создание с всеми полями** - тест создания блог поста со всеми заполненными полями
|
||||
- **Создание с изображением** - тест создания блог поста с загруженным изображением
|
||||
- **Обновление блог поста** - тест обновления существующего блог поста
|
||||
- **Удаление блог поста** - тест удаления блог поста
|
||||
- **Состояния загрузки** - проверка состояний загрузки во время операций
|
||||
- **Обработка ошибок** - проверка обработки ошибок при создании
|
||||
- **Успешное создание** - проверка успешного завершения операций
|
||||
- **Валидация формы** - проверка валидации полей перед отправкой
|
||||
- **Взаимодействие с загрузкой изображений** - проверка функциональности выбора изображений
|
||||
- **Валидация URL видео** - проверка валидации ссылок на видео
|
||||
- **Отправка с пустыми полями** - проверка обработки пустых полей
|
||||
- **Длинный контент** - проверка обработки длинного текста
|
||||
|
||||
### 3. Integration Widget Tests (`integration_widget_test.dart`)
|
||||
|
||||
Интеграционные тесты для проверки полного потока работы:
|
||||
|
||||
- **Полный поток создания** - тест полного процесса создания блог поста
|
||||
- **Поток обновления** - тест полного процесса обновления блог поста
|
||||
- **Поток удаления** - тест полного процесса удаления блог поста
|
||||
- **Обработка ошибок** - тест обработки ошибок в полном потоке
|
||||
- **Состояния загрузки** - тест состояний загрузки в полном потоке
|
||||
- **Успешное завершение** - тест успешного завершения операций
|
||||
- **Валидация формы** - тест валидации в полном потоке
|
||||
- **Загрузка изображений** - тест загрузки изображений в полном потоке
|
||||
- **Полный функционал** - тест всех функций создания блог постов
|
||||
|
||||
## Ключи приложения (AppKeys)
|
||||
|
||||
Тесты используют следующие ключи из `app_keys.dart`:
|
||||
|
||||
- `blogPostCreateScreenTitleTextField` - поле заголовка
|
||||
- `blogPostCreateScreenContentTextField` - поле описания
|
||||
- `blogPostCreateScreenVideoLinkTextField` - поле ссылки на видео
|
||||
- `blogPostCreateScreenCreateButton` - кнопка создания/обновления
|
||||
- `imageWidget` - виджет загрузки изображения
|
||||
|
||||
## Моки (Mocks)
|
||||
|
||||
Тесты используют моки для следующих компонентов:
|
||||
|
||||
- `MockBlogPostRepository` - репозиторий для работы с блог постами
|
||||
- `MockFeedCubit` - cubit для управления лентой постов
|
||||
|
||||
## Состояния BlogPostCreateState
|
||||
|
||||
Тесты проверяют следующие состояния:
|
||||
|
||||
- `BlogPostCreateState` - начальное состояние
|
||||
- `BlogPostCreateStateLoading` - состояние загрузки
|
||||
- `BlogPostCreateStateLoaded` - состояние успешного завершения
|
||||
- `BlogPostCreateStateError` - состояние ошибки
|
||||
|
||||
## Запуск тестов
|
||||
|
||||
Для запуска всех тестов блог постов:
|
||||
|
||||
```bash
|
||||
flutter test test/features/blog_post_create/
|
||||
```
|
||||
|
||||
Для запуска конкретного теста:
|
||||
|
||||
```bash
|
||||
flutter test test/features/blog_post_create/basic_widget_test.dart
|
||||
flutter test test/features/blog_post_create/advanced_widget_test.dart
|
||||
flutter test test/features/blog_post_create/integration_widget_test.dart
|
||||
```
|
||||
|
||||
## Покрытие функциональности
|
||||
|
||||
Тесты покрывают:
|
||||
|
||||
- ✅ Создание блог постов
|
||||
- ✅ Обновление блог постов
|
||||
- ✅ Удаление блог постов
|
||||
- ✅ Загрузка изображений
|
||||
- ✅ Валидация полей
|
||||
- ✅ Обработка ошибок
|
||||
- ✅ Состояния загрузки
|
||||
- ✅ Интеграция с лентой постов
|
||||
- ✅ Пользовательский интерфейс
|
||||
- ✅ Взаимодействие с формами
|
||||
|
||||
## Зависимости
|
||||
|
||||
Тесты используют следующие пакеты:
|
||||
|
||||
- `flutter_test` - базовые тесты Flutter
|
||||
- `mockito` - создание моков
|
||||
- `flutter_bloc` - тестирование BLoC
|
||||
- `image_picker` - работа с изображениями
|
||||
|
||||
## Структура файлов
|
||||
|
||||
```
|
||||
test/features/blog_post_create/
|
||||
├── basic_widget_test.dart
|
||||
├── basic_widget_test.mocks.dart
|
||||
├── advanced_widget_test.dart
|
||||
├── advanced_widget_test.mocks.dart
|
||||
├── integration_widget_test.dart
|
||||
├── integration_widget_test.mocks.dart
|
||||
└── README.md
|
||||
```
|
||||
517
test/features/blog_post_create/advanced_widget_test.dart
Normal file
517
test/features/blog_post_create/advanced_widget_test.dart
Normal file
@@ -0,0 +1,517 @@
|
||||
import 'package:eatvio_app/app_keys.dart';
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:eatvio_app/features/blog_post_create/bloc/blog_post_create_cubit/blog_post_create_cubit.dart';
|
||||
import 'package:eatvio_app/features/blog_post_create/bloc/blog_post_create_cubit/blog_post_create_state.dart';
|
||||
import 'package:eatvio_app/features/blog_post_create/repositories/blog_post_repository.dart';
|
||||
import 'package:eatvio_app/features/feed/models/feed_blog_post_model.dart';
|
||||
import 'package:eatvio_app/features/feed/models/diets_pagination_model.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 'advanced_widget_test.mocks.dart';
|
||||
|
||||
@GenerateMocks([BlogPostRepository])
|
||||
void main() {
|
||||
group('Blog Post Creation Advanced Widget Tests', () {
|
||||
late MockBlogPostRepository mockBlogPostRepository;
|
||||
late BlogPostCreateCubit blogPostCreateCubit;
|
||||
|
||||
setUp(() {
|
||||
mockBlogPostRepository = MockBlogPostRepository();
|
||||
blogPostCreateCubit = BlogPostCreateCubit(blogPostRepository: mockBlogPostRepository);
|
||||
});
|
||||
|
||||
tearDown(() {
|
||||
blogPostCreateCubit.close();
|
||||
});
|
||||
|
||||
Widget createTestWidget(Widget child) {
|
||||
return MaterialApp(
|
||||
theme: ThemeHelper().themeData(),
|
||||
home: BlocProvider<BlogPostCreateCubit>(
|
||||
create: (context) => blogPostCreateCubit,
|
||||
child: Scaffold(body: SingleChildScrollView(child: child)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
testWidgets('should handle blog post creation with all fields', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
when(mockBlogPostRepository.createBlogPost(any, any, any, any)).thenAnswer((_) async {});
|
||||
|
||||
Widget formWidget = Column(
|
||||
children: [
|
||||
TextFormField(
|
||||
key: AppKeys.blogPostCreateScreenTitleTextField,
|
||||
decoration: const InputDecoration(labelText: 'Заголовок'),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
TextFormField(
|
||||
key: AppKeys.blogPostCreateScreenContentTextField,
|
||||
decoration: const InputDecoration(labelText: 'Описание'),
|
||||
maxLines: 6,
|
||||
minLines: 4,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
TextFormField(
|
||||
key: AppKeys.blogPostCreateScreenVideoLinkTextField,
|
||||
decoration: const InputDecoration(labelText: 'Ссылка на видео'),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
ElevatedButton(
|
||||
key: AppKeys.blogPostCreateScreenCreateButton,
|
||||
onPressed: () async {
|
||||
await blogPostCreateCubit.createBlogPost(
|
||||
title: 'Test Title',
|
||||
description: 'Test Description',
|
||||
videoUrl: 'https://youtube.com/watch?v=test',
|
||||
image: null,
|
||||
);
|
||||
},
|
||||
child: const Text('Опубликовать'),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(formWidget));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.enterText(find.byKey(AppKeys.blogPostCreateScreenTitleTextField), 'Test Title');
|
||||
await tester.enterText(find.byKey(AppKeys.blogPostCreateScreenContentTextField), 'Test Description');
|
||||
await tester.enterText(find.byKey(AppKeys.blogPostCreateScreenVideoLinkTextField), 'https://youtube.com/watch?v=test');
|
||||
await tester.pump();
|
||||
|
||||
await tester.tap(find.byKey(AppKeys.blogPostCreateScreenCreateButton));
|
||||
await tester.pump();
|
||||
|
||||
// Assert
|
||||
verify(mockBlogPostRepository.createBlogPost('Test Title', 'https://youtube.com/watch?v=test', 'Test Description', null)).called(1);
|
||||
});
|
||||
|
||||
testWidgets('should handle blog post creation with image', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
when(mockBlogPostRepository.createBlogPost(any, any, any, any)).thenAnswer((_) async {});
|
||||
|
||||
Widget formWidget = Column(
|
||||
children: [
|
||||
TextFormField(
|
||||
key: AppKeys.blogPostCreateScreenTitleTextField,
|
||||
decoration: const InputDecoration(labelText: 'Заголовок'),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
TextFormField(
|
||||
key: AppKeys.blogPostCreateScreenContentTextField,
|
||||
decoration: const InputDecoration(labelText: 'Описание'),
|
||||
maxLines: 6,
|
||||
minLines: 4,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
GestureDetector(
|
||||
key: AppKeys.imageWidget,
|
||||
onTap: () {},
|
||||
child: Container(
|
||||
height: 200,
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Colors.grey),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: const Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(Icons.add_photo_alternate, size: 48, color: Colors.grey),
|
||||
SizedBox(height: 8),
|
||||
Text('Добавить изображение'),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
ElevatedButton(
|
||||
key: AppKeys.blogPostCreateScreenCreateButton,
|
||||
onPressed: () async {
|
||||
await blogPostCreateCubit.createBlogPost(
|
||||
title: 'Test Title',
|
||||
description: 'Test Description',
|
||||
videoUrl: null,
|
||||
image: null, // В реальном тесте здесь был бы XFile
|
||||
);
|
||||
},
|
||||
child: const Text('Опубликовать'),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(formWidget));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.enterText(find.byKey(AppKeys.blogPostCreateScreenTitleTextField), 'Test Title');
|
||||
await tester.enterText(find.byKey(AppKeys.blogPostCreateScreenContentTextField), 'Test Description');
|
||||
await tester.pump();
|
||||
|
||||
await tester.tap(find.byKey(AppKeys.blogPostCreateScreenCreateButton));
|
||||
await tester.pump();
|
||||
|
||||
// Assert
|
||||
verify(mockBlogPostRepository.createBlogPost('Test Title', null, 'Test Description', null)).called(1);
|
||||
});
|
||||
|
||||
testWidgets('should handle blog post update', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
when(mockBlogPostRepository.updateBlogPost(any, any, any, any, any, any)).thenAnswer((_) async {});
|
||||
|
||||
final testBlogPost = FeedBlogPostModel(
|
||||
id: 1,
|
||||
state: 'published',
|
||||
image: 'original_image.jpg',
|
||||
description: 'Original Description',
|
||||
author: const Author(authorSlug: 'test-author', authorName: 'Test Author', authorProfileIcon: 'avatar.jpg', authorIsMe: true),
|
||||
videoUrl: 'https://youtube.com/watch?v=original',
|
||||
createdAt: DateTime.now(),
|
||||
updatedAt: DateTime.now(),
|
||||
title: 'Original Title',
|
||||
totalLikes: 0,
|
||||
totalViews: 0,
|
||||
totalComments: 0,
|
||||
wasLikedByMe: false,
|
||||
);
|
||||
|
||||
Widget formWidget = Column(
|
||||
children: [
|
||||
TextFormField(
|
||||
key: AppKeys.blogPostCreateScreenTitleTextField,
|
||||
decoration: const InputDecoration(labelText: 'Заголовок'),
|
||||
controller: TextEditingController(text: testBlogPost.title),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
TextFormField(
|
||||
key: AppKeys.blogPostCreateScreenContentTextField,
|
||||
decoration: const InputDecoration(labelText: 'Описание'),
|
||||
maxLines: 6,
|
||||
minLines: 4,
|
||||
controller: TextEditingController(text: testBlogPost.description),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
TextFormField(
|
||||
key: AppKeys.blogPostCreateScreenVideoLinkTextField,
|
||||
decoration: const InputDecoration(labelText: 'Ссылка на видео'),
|
||||
controller: TextEditingController(text: testBlogPost.videoUrl),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
ElevatedButton(
|
||||
key: AppKeys.blogPostCreateScreenCreateButton,
|
||||
onPressed: () async {
|
||||
await blogPostCreateCubit.updateBlogPost(
|
||||
id: testBlogPost.id,
|
||||
title: 'Updated Title',
|
||||
description: 'Updated Description',
|
||||
videoUrl: 'https://youtube.com/watch?v=updated',
|
||||
image: null,
|
||||
oldImageUrl: testBlogPost.image,
|
||||
);
|
||||
},
|
||||
child: const Text('Обновить'),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(formWidget));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.enterText(find.byKey(AppKeys.blogPostCreateScreenTitleTextField), 'Updated Title');
|
||||
await tester.enterText(find.byKey(AppKeys.blogPostCreateScreenContentTextField), 'Updated Description');
|
||||
await tester.enterText(find.byKey(AppKeys.blogPostCreateScreenVideoLinkTextField), 'https://youtube.com/watch?v=updated');
|
||||
await tester.pump();
|
||||
|
||||
await tester.tap(find.byKey(AppKeys.blogPostCreateScreenCreateButton));
|
||||
await tester.pump();
|
||||
|
||||
// Assert
|
||||
verify(
|
||||
mockBlogPostRepository.updateBlogPost(1, 'Updated Title', 'https://youtube.com/watch?v=updated', 'Updated Description', null, 'original_image.jpg'),
|
||||
).called(1);
|
||||
});
|
||||
|
||||
testWidgets('should handle blog post deletion', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
when(mockBlogPostRepository.deleteBlogPost(any)).thenAnswer((_) async {});
|
||||
|
||||
Widget deleteWidget = ElevatedButton(
|
||||
key: const Key('deleteBlogPostButton'),
|
||||
onPressed: () async {
|
||||
await blogPostCreateCubit.deleteBlogPost(1);
|
||||
},
|
||||
child: const Text('Удалить пост'),
|
||||
);
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(deleteWidget));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.byKey(const Key('deleteBlogPostButton')));
|
||||
await tester.pump();
|
||||
|
||||
// Assert
|
||||
verify(mockBlogPostRepository.deleteBlogPost(1)).called(1);
|
||||
});
|
||||
|
||||
testWidgets('should handle loading state during blog post creation', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
when(mockBlogPostRepository.createBlogPost(any, any, any, any)).thenAnswer((_) async {
|
||||
await Future.delayed(const Duration(milliseconds: 100));
|
||||
});
|
||||
|
||||
Widget loadingWidget = BlocBuilder<BlogPostCreateCubit, BlogPostCreateState>(
|
||||
builder: (context, state) {
|
||||
if (state is BlogPostCreateStateLoading) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
return const Text('Not Loading');
|
||||
},
|
||||
);
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(loadingWidget));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Trigger loading state
|
||||
blogPostCreateCubit.createBlogPost(title: 'Test Title', description: 'Test Description', videoUrl: null, image: null);
|
||||
await tester.pump();
|
||||
|
||||
// Assert
|
||||
expect(find.byType(CircularProgressIndicator), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('should handle error state during blog post creation', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
when(mockBlogPostRepository.createBlogPost(any, any, any, any)).thenThrow(Exception('Network error'));
|
||||
|
||||
Widget errorWidget = BlocBuilder<BlogPostCreateCubit, BlogPostCreateState>(
|
||||
builder: (context, state) {
|
||||
if (state is BlogPostCreateStateError) {
|
||||
return Center(child: Text('Error: ${state.errorMessage}'));
|
||||
}
|
||||
return const Text('No Error');
|
||||
},
|
||||
);
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(errorWidget));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Trigger error state
|
||||
blogPostCreateCubit.createBlogPost(title: 'Test Title', description: 'Test Description', videoUrl: null, image: null);
|
||||
await tester.pump();
|
||||
|
||||
// Assert
|
||||
expect(find.textContaining('Error:'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('should handle success state after blog post creation', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
when(mockBlogPostRepository.createBlogPost(any, any, any, any)).thenAnswer((_) async {});
|
||||
|
||||
Widget successWidget = BlocBuilder<BlogPostCreateCubit, BlogPostCreateState>(
|
||||
builder: (context, state) {
|
||||
if (state is BlogPostCreateStateLoaded) {
|
||||
return const Center(child: Text('Blog post created successfully!'));
|
||||
}
|
||||
return const Text('Not Loaded');
|
||||
},
|
||||
);
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(successWidget));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Trigger success state
|
||||
blogPostCreateCubit.createBlogPost(title: 'Test Title', description: 'Test Description', videoUrl: null, image: null);
|
||||
await tester.pump();
|
||||
|
||||
// Assert
|
||||
expect(find.text('Blog post created successfully!'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('should validate form fields before submission', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
Widget validationWidget = Column(
|
||||
children: [
|
||||
TextFormField(
|
||||
key: AppKeys.blogPostCreateScreenTitleTextField,
|
||||
decoration: const InputDecoration(labelText: 'Заголовок', errorText: 'Заголовок не может быть пустым'),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
TextFormField(
|
||||
key: AppKeys.blogPostCreateScreenContentTextField,
|
||||
decoration: const InputDecoration(labelText: 'Описание', errorText: 'Описание не может быть пустым'),
|
||||
maxLines: 6,
|
||||
minLines: 4,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
ElevatedButton(key: AppKeys.blogPostCreateScreenCreateButton, onPressed: () {}, child: const Text('Опубликовать')),
|
||||
],
|
||||
);
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(validationWidget));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Assert
|
||||
expect(find.text('Заголовок не может быть пустым'), findsOneWidget);
|
||||
expect(find.text('Описание не может быть пустым'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('should handle image picker interaction', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
bool imagePickerCalled = false;
|
||||
Widget imagePickerWidget = GestureDetector(
|
||||
key: AppKeys.imageWidget,
|
||||
onTap: () {
|
||||
imagePickerCalled = true;
|
||||
},
|
||||
child: Container(
|
||||
height: 200,
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Colors.grey),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: const Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(Icons.add_photo_alternate, size: 48, color: Colors.grey),
|
||||
SizedBox(height: 8),
|
||||
Text('Добавить изображение'),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(imagePickerWidget));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.byKey(AppKeys.imageWidget));
|
||||
await tester.pump();
|
||||
|
||||
// Assert
|
||||
expect(imagePickerCalled, isTrue);
|
||||
});
|
||||
|
||||
testWidgets('should handle video URL validation', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
Widget videoValidationWidget = Column(
|
||||
children: [
|
||||
TextFormField(
|
||||
key: AppKeys.blogPostCreateScreenVideoLinkTextField,
|
||||
decoration: const InputDecoration(labelText: 'Ссылка на видео', errorText: 'Введите корректную ссылку на видео'),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
ElevatedButton(key: AppKeys.blogPostCreateScreenCreateButton, onPressed: () {}, child: const Text('Опубликовать')),
|
||||
],
|
||||
);
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(videoValidationWidget));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Assert
|
||||
expect(find.text('Введите корректную ссылку на видео'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('should handle form submission with empty fields', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
Widget emptyFormWidget = Column(
|
||||
children: [
|
||||
TextFormField(
|
||||
key: AppKeys.blogPostCreateScreenTitleTextField,
|
||||
decoration: const InputDecoration(labelText: 'Заголовок'),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
TextFormField(
|
||||
key: AppKeys.blogPostCreateScreenContentTextField,
|
||||
decoration: const InputDecoration(labelText: 'Описание'),
|
||||
maxLines: 6,
|
||||
minLines: 4,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
ElevatedButton(
|
||||
key: AppKeys.blogPostCreateScreenCreateButton,
|
||||
onPressed: () async {
|
||||
await blogPostCreateCubit.createBlogPost(title: '', description: '', videoUrl: '', image: null);
|
||||
},
|
||||
child: const Text('Опубликовать'),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(emptyFormWidget));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.byKey(AppKeys.blogPostCreateScreenCreateButton));
|
||||
await tester.pump();
|
||||
|
||||
// Assert
|
||||
verify(mockBlogPostRepository.createBlogPost('', '', '', null)).called(1);
|
||||
});
|
||||
|
||||
testWidgets('should handle blog post creation with long content', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
const longContent =
|
||||
'Это очень длинное описание блог поста, которое содержит много текста и должно быть правильно обработано системой. '
|
||||
'Оно может содержать несколько абзацев и различные форматирования. '
|
||||
'Пользователи должны иметь возможность создавать подробные и информативные посты.';
|
||||
|
||||
when(mockBlogPostRepository.createBlogPost(any, any, any, any)).thenAnswer((_) async {});
|
||||
|
||||
Widget longContentWidget = Column(
|
||||
children: [
|
||||
TextFormField(
|
||||
key: AppKeys.blogPostCreateScreenTitleTextField,
|
||||
decoration: const InputDecoration(labelText: 'Заголовок'),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
TextFormField(
|
||||
key: AppKeys.blogPostCreateScreenContentTextField,
|
||||
decoration: const InputDecoration(labelText: 'Описание'),
|
||||
maxLines: 6,
|
||||
minLines: 4,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
ElevatedButton(
|
||||
key: AppKeys.blogPostCreateScreenCreateButton,
|
||||
onPressed: () async {
|
||||
await blogPostCreateCubit.createBlogPost(title: 'Test Title', description: longContent, videoUrl: null, image: null);
|
||||
},
|
||||
child: const Text('Опубликовать'),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(longContentWidget));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.enterText(find.byKey(AppKeys.blogPostCreateScreenTitleTextField), 'Test Title');
|
||||
await tester.enterText(find.byKey(AppKeys.blogPostCreateScreenContentTextField), longContent);
|
||||
await tester.pump();
|
||||
|
||||
await tester.tap(find.byKey(AppKeys.blogPostCreateScreenCreateButton));
|
||||
await tester.pump();
|
||||
|
||||
// Assert
|
||||
verify(mockBlogPostRepository.createBlogPost('Test Title', null, longContent, null)).called(1);
|
||||
});
|
||||
});
|
||||
}
|
||||
443
test/features/blog_post_create/basic_widget_test.dart
Normal file
443
test/features/blog_post_create/basic_widget_test.dart
Normal file
@@ -0,0 +1,443 @@
|
||||
import 'package:eatvio_app/app_keys.dart';
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:eatvio_app/features/blog_post_create/bloc/blog_post_create_cubit/blog_post_create_cubit.dart';
|
||||
import 'package:eatvio_app/features/blog_post_create/repositories/blog_post_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 'basic_widget_test.mocks.dart';
|
||||
|
||||
@GenerateMocks([BlogPostRepository])
|
||||
void main() {
|
||||
group('Blog Post Creation Basic Widget Tests', () {
|
||||
late MockBlogPostRepository mockBlogPostRepository;
|
||||
late BlogPostCreateCubit blogPostCreateCubit;
|
||||
|
||||
setUp(() {
|
||||
mockBlogPostRepository = MockBlogPostRepository();
|
||||
blogPostCreateCubit = BlogPostCreateCubit(blogPostRepository: mockBlogPostRepository);
|
||||
});
|
||||
|
||||
tearDown(() {
|
||||
blogPostCreateCubit.close();
|
||||
});
|
||||
|
||||
Widget createTestWidget(Widget child) {
|
||||
return MaterialApp(
|
||||
theme: ThemeHelper().themeData(),
|
||||
home: BlocProvider<BlogPostCreateCubit>(
|
||||
create: (context) => blogPostCreateCubit,
|
||||
child: Scaffold(body: SingleChildScrollView(child: child)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
testWidgets('should create BlogPostCreateCubit successfully', (WidgetTester tester) async {
|
||||
// Arrange & Act
|
||||
final cubit = BlogPostCreateCubit(blogPostRepository: mockBlogPostRepository);
|
||||
|
||||
// Assert
|
||||
expect(cubit, isA<BlogPostCreateCubit>());
|
||||
|
||||
cubit.close();
|
||||
});
|
||||
|
||||
testWidgets('should display basic blog post form fields', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
Widget formWidget = Column(
|
||||
children: [
|
||||
TextFormField(
|
||||
key: AppKeys.blogPostCreateScreenTitleTextField,
|
||||
decoration: const InputDecoration(labelText: 'Заголовок', hintText: 'Введите заголовок поста'),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
TextFormField(
|
||||
key: AppKeys.blogPostCreateScreenContentTextField,
|
||||
decoration: const InputDecoration(labelText: 'Описание'),
|
||||
maxLines: 6,
|
||||
minLines: 4,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
TextFormField(
|
||||
key: AppKeys.blogPostCreateScreenVideoLinkTextField,
|
||||
decoration: const InputDecoration(labelText: 'Ссылка на видео'),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
ElevatedButton(key: AppKeys.blogPostCreateScreenCreateButton, onPressed: () {}, child: const Text('Опубликовать')),
|
||||
],
|
||||
);
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(formWidget));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Assert
|
||||
expect(find.byKey(AppKeys.blogPostCreateScreenTitleTextField), findsOneWidget);
|
||||
expect(find.byKey(AppKeys.blogPostCreateScreenContentTextField), findsOneWidget);
|
||||
expect(find.byKey(AppKeys.blogPostCreateScreenVideoLinkTextField), findsOneWidget);
|
||||
expect(find.byKey(AppKeys.blogPostCreateScreenCreateButton), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('should update title when user types in title field', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
Widget formWidget = TextFormField(
|
||||
key: AppKeys.blogPostCreateScreenTitleTextField,
|
||||
decoration: const InputDecoration(labelText: 'Заголовок', hintText: 'Введите заголовок поста'),
|
||||
);
|
||||
|
||||
await tester.pumpWidget(createTestWidget(formWidget));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
const testTitle = 'Тестовый заголовок поста';
|
||||
|
||||
// Act
|
||||
await tester.enterText(find.byKey(AppKeys.blogPostCreateScreenTitleTextField), testTitle);
|
||||
await tester.pump();
|
||||
|
||||
// Assert
|
||||
expect(find.text(testTitle), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('should update description when user types in description field', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
Widget formWidget = TextFormField(
|
||||
key: AppKeys.blogPostCreateScreenContentTextField,
|
||||
decoration: const InputDecoration(labelText: 'Описание'),
|
||||
maxLines: 6,
|
||||
minLines: 4,
|
||||
);
|
||||
|
||||
await tester.pumpWidget(createTestWidget(formWidget));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
const testDescription = 'Это тестовое описание блог поста';
|
||||
|
||||
// Act
|
||||
await tester.enterText(find.byKey(AppKeys.blogPostCreateScreenContentTextField), testDescription);
|
||||
await tester.pump();
|
||||
|
||||
// Assert
|
||||
expect(find.text(testDescription), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('should update video URL when user types in video field', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
Widget formWidget = TextFormField(
|
||||
key: AppKeys.blogPostCreateScreenVideoLinkTextField,
|
||||
decoration: const InputDecoration(labelText: 'Ссылка на видео'),
|
||||
);
|
||||
|
||||
await tester.pumpWidget(createTestWidget(formWidget));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
const testVideoUrl = 'https://youtube.com/watch?v=example';
|
||||
|
||||
// Act
|
||||
await tester.enterText(find.byKey(AppKeys.blogPostCreateScreenVideoLinkTextField), testVideoUrl);
|
||||
await tester.pump();
|
||||
|
||||
// Assert
|
||||
expect(find.text(testVideoUrl), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('should display form labels correctly', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
Widget formWidget = const Column(children: [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);
|
||||
});
|
||||
|
||||
testWidgets('should display placeholder text in title field', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
Widget formWidget = TextFormField(
|
||||
key: AppKeys.blogPostCreateScreenTitleTextField,
|
||||
decoration: const InputDecoration(labelText: 'Заголовок', hintText: 'Введите заголовок поста'),
|
||||
);
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(formWidget));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Assert
|
||||
expect(find.text('Введите заголовок поста'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('should display create button', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
Widget formWidget = ElevatedButton(key: AppKeys.blogPostCreateScreenCreateButton, onPressed: () {}, child: const Text('Опубликовать'));
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(formWidget));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Assert
|
||||
expect(find.byKey(AppKeys.blogPostCreateScreenCreateButton), findsOneWidget);
|
||||
expect(find.text('Опубликовать'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('should handle button tap', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
bool buttonPressed = false;
|
||||
Widget formWidget = ElevatedButton(
|
||||
key: AppKeys.blogPostCreateScreenCreateButton,
|
||||
onPressed: () {
|
||||
buttonPressed = true;
|
||||
},
|
||||
child: const Text('Опубликовать'),
|
||||
);
|
||||
|
||||
await tester.pumpWidget(createTestWidget(formWidget));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Act
|
||||
await tester.tap(find.byKey(AppKeys.blogPostCreateScreenCreateButton));
|
||||
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: AppKeys.blogPostCreateScreenTitleTextField,
|
||||
decoration: const InputDecoration(labelText: 'Заголовок', errorText: 'Заголовок обязателен'),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
TextFormField(
|
||||
key: AppKeys.blogPostCreateScreenContentTextField,
|
||||
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 form with all required fields', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
Widget completeForm = Column(
|
||||
children: [
|
||||
TextFormField(
|
||||
key: AppKeys.blogPostCreateScreenTitleTextField,
|
||||
decoration: const InputDecoration(labelText: 'Заголовок', hintText: 'Введите заголовок поста'),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
TextFormField(
|
||||
key: AppKeys.blogPostCreateScreenContentTextField,
|
||||
decoration: const InputDecoration(labelText: 'Описание'),
|
||||
maxLines: 6,
|
||||
minLines: 4,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
TextFormField(
|
||||
key: AppKeys.blogPostCreateScreenVideoLinkTextField,
|
||||
decoration: const InputDecoration(labelText: 'Ссылка на видео'),
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
ElevatedButton(key: AppKeys.blogPostCreateScreenCreateButton, onPressed: () {}, child: const Text('Опубликовать')),
|
||||
],
|
||||
);
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(completeForm));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Assert
|
||||
expect(find.byKey(AppKeys.blogPostCreateScreenTitleTextField), findsOneWidget);
|
||||
expect(find.byKey(AppKeys.blogPostCreateScreenContentTextField), findsOneWidget);
|
||||
expect(find.byKey(AppKeys.blogPostCreateScreenVideoLinkTextField), findsOneWidget);
|
||||
expect(find.byKey(AppKeys.blogPostCreateScreenCreateButton), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('should display image upload widget', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
Widget imageWidget = GestureDetector(
|
||||
key: AppKeys.imageWidget,
|
||||
onTap: () {},
|
||||
child: Container(
|
||||
height: 200,
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Colors.grey),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: const Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(Icons.add_photo_alternate, size: 48, color: Colors.grey),
|
||||
SizedBox(height: 8),
|
||||
Text('Добавить изображение'),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(imageWidget));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Assert
|
||||
expect(find.byKey(AppKeys.imageWidget), findsOneWidget);
|
||||
expect(find.text('Добавить изображение'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('should handle image upload tap', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
bool imageUploadTapped = false;
|
||||
Widget imageWidget = GestureDetector(
|
||||
key: AppKeys.imageWidget,
|
||||
onTap: () {
|
||||
imageUploadTapped = true;
|
||||
},
|
||||
child: Container(
|
||||
height: 200,
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Colors.grey),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: const Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(Icons.add_photo_alternate, size: 48, color: Colors.grey),
|
||||
SizedBox(height: 8),
|
||||
Text('Добавить изображение'),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pumpWidget(createTestWidget(imageWidget));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Act
|
||||
await tester.tap(find.byKey(AppKeys.imageWidget));
|
||||
await tester.pump();
|
||||
|
||||
// Assert
|
||||
expect(imageUploadTapped, isTrue);
|
||||
});
|
||||
|
||||
testWidgets('should display blog post creation form with image and video options', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
Widget completeForm = Column(
|
||||
children: [
|
||||
const Text('Заголовок'),
|
||||
const SizedBox(height: 8),
|
||||
TextFormField(
|
||||
key: AppKeys.blogPostCreateScreenTitleTextField,
|
||||
decoration: const InputDecoration(hintText: 'Введите заголовок поста'),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
const Text('Описание'),
|
||||
const SizedBox(height: 8),
|
||||
TextFormField(key: AppKeys.blogPostCreateScreenContentTextField, decoration: const InputDecoration(), maxLines: 6, minLines: 4),
|
||||
const SizedBox(height: 16),
|
||||
const Text('Изображение поста'),
|
||||
const SizedBox(height: 8),
|
||||
GestureDetector(
|
||||
key: AppKeys.imageWidget,
|
||||
onTap: () {},
|
||||
child: Container(
|
||||
height: 200,
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Colors.grey),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: const Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(Icons.add_photo_alternate, size: 48, color: Colors.grey),
|
||||
SizedBox(height: 8),
|
||||
Text('Добавить изображение'),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
const Text('Ссылка на видео'),
|
||||
const SizedBox(height: 8),
|
||||
TextFormField(key: AppKeys.blogPostCreateScreenVideoLinkTextField, decoration: const InputDecoration()),
|
||||
const SizedBox(height: 32),
|
||||
ElevatedButton(key: AppKeys.blogPostCreateScreenCreateButton, onPressed: () {}, child: const Text('Опубликовать')),
|
||||
],
|
||||
);
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(completeForm));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Assert
|
||||
expect(find.text('Заголовок'), findsOneWidget);
|
||||
expect(find.text('Описание'), findsOneWidget);
|
||||
expect(find.text('Изображение поста'), findsOneWidget);
|
||||
expect(find.text('Ссылка на видео'), findsOneWidget);
|
||||
expect(find.byKey(AppKeys.blogPostCreateScreenTitleTextField), findsOneWidget);
|
||||
expect(find.byKey(AppKeys.blogPostCreateScreenContentTextField), findsOneWidget);
|
||||
expect(find.byKey(AppKeys.imageWidget), findsOneWidget);
|
||||
expect(find.byKey(AppKeys.blogPostCreateScreenVideoLinkTextField), findsOneWidget);
|
||||
expect(find.byKey(AppKeys.blogPostCreateScreenCreateButton), findsOneWidget);
|
||||
});
|
||||
});
|
||||
}
|
||||
539
test/features/blog_post_create/integration_widget_test.dart
Normal file
539
test/features/blog_post_create/integration_widget_test.dart
Normal file
@@ -0,0 +1,539 @@
|
||||
import 'package:eatvio_app/app_keys.dart';
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:eatvio_app/features/blog_post_create/bloc/blog_post_create_cubit/blog_post_create_cubit.dart';
|
||||
import 'package:eatvio_app/features/blog_post_create/bloc/blog_post_create_cubit/blog_post_create_state.dart';
|
||||
import 'package:eatvio_app/features/blog_post_create/repositories/blog_post_repository.dart';
|
||||
import 'package:eatvio_app/features/feed/models/feed_blog_post_model.dart';
|
||||
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
|
||||
import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_cubit.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([BlogPostRepository, FeedCubit])
|
||||
void main() {
|
||||
group('Blog Post Creation Integration Widget Tests', () {
|
||||
late MockBlogPostRepository mockBlogPostRepository;
|
||||
late MockFeedCubit mockFeedCubit;
|
||||
late BlogPostCreateCubit blogPostCreateCubit;
|
||||
|
||||
setUp(() {
|
||||
mockBlogPostRepository = MockBlogPostRepository();
|
||||
mockFeedCubit = MockFeedCubit();
|
||||
blogPostCreateCubit = BlogPostCreateCubit(blogPostRepository: mockBlogPostRepository);
|
||||
});
|
||||
|
||||
tearDown(() {
|
||||
blogPostCreateCubit.close();
|
||||
});
|
||||
|
||||
Widget createTestWidget(Widget child) {
|
||||
return MaterialApp(
|
||||
theme: ThemeHelper().themeData(),
|
||||
home: MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider<BlogPostCreateCubit>(create: (context) => blogPostCreateCubit),
|
||||
BlocProvider<FeedCubit>(create: (context) => mockFeedCubit),
|
||||
],
|
||||
child: Scaffold(body: SingleChildScrollView(child: child)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
testWidgets('should complete full blog post creation flow', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
when(mockBlogPostRepository.createBlogPost(any, any, any, any)).thenAnswer((_) async {});
|
||||
when(mockFeedCubit.loadFeedBlogPostList()).thenAnswer((_) async {});
|
||||
|
||||
Widget fullFlowWidget = BlocListener<BlogPostCreateCubit, BlogPostCreateState>(
|
||||
listener: (context, state) {
|
||||
if (state is BlogPostCreateStateLoaded) {
|
||||
context.read<FeedCubit>().loadFeedBlogPostList();
|
||||
}
|
||||
},
|
||||
child: Column(
|
||||
children: [
|
||||
TextFormField(
|
||||
key: AppKeys.blogPostCreateScreenTitleTextField,
|
||||
decoration: const InputDecoration(labelText: 'Заголовок'),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
TextFormField(
|
||||
key: AppKeys.blogPostCreateScreenContentTextField,
|
||||
decoration: const InputDecoration(labelText: 'Описание'),
|
||||
maxLines: 6,
|
||||
minLines: 4,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
TextFormField(
|
||||
key: AppKeys.blogPostCreateScreenVideoLinkTextField,
|
||||
decoration: const InputDecoration(labelText: 'Ссылка на видео'),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
GestureDetector(
|
||||
key: AppKeys.imageWidget,
|
||||
onTap: () {},
|
||||
child: Container(
|
||||
height: 200,
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Colors.grey),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: const Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(Icons.add_photo_alternate, size: 48, color: Colors.grey),
|
||||
SizedBox(height: 8),
|
||||
Text('Добавить изображение'),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
ElevatedButton(
|
||||
key: AppKeys.blogPostCreateScreenCreateButton,
|
||||
onPressed: () async {
|
||||
await blogPostCreateCubit.createBlogPost(
|
||||
title: 'Integration Test Title',
|
||||
description: 'Integration Test Description',
|
||||
videoUrl: 'https://youtube.com/watch?v=integration',
|
||||
image: null,
|
||||
);
|
||||
},
|
||||
child: const Text('Опубликовать'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(fullFlowWidget));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.enterText(find.byKey(AppKeys.blogPostCreateScreenTitleTextField), 'Integration Test Title');
|
||||
await tester.enterText(find.byKey(AppKeys.blogPostCreateScreenContentTextField), 'Integration Test Description');
|
||||
await tester.enterText(find.byKey(AppKeys.blogPostCreateScreenVideoLinkTextField), 'https://youtube.com/watch?v=integration');
|
||||
await tester.pump();
|
||||
|
||||
await tester.tap(find.byKey(AppKeys.blogPostCreateScreenCreateButton));
|
||||
await tester.pump();
|
||||
|
||||
// Assert
|
||||
verify(
|
||||
mockBlogPostRepository.createBlogPost('Integration Test Title', 'https://youtube.com/watch?v=integration', 'Integration Test Description', null),
|
||||
).called(1);
|
||||
verify(mockFeedCubit.loadFeedBlogPostList()).called(1);
|
||||
});
|
||||
|
||||
testWidgets('should handle blog post update flow', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
when(mockBlogPostRepository.updateBlogPost(any, any, any, any, any, any)).thenAnswer((_) async {});
|
||||
when(mockFeedCubit.loadFeedBlogPostList()).thenAnswer((_) async {});
|
||||
|
||||
final existingBlogPost = FeedBlogPostModel(
|
||||
id: 1,
|
||||
state: 'published',
|
||||
image: 'original_image.jpg',
|
||||
description: 'Original Description',
|
||||
author: const Author(authorSlug: 'test-author', authorName: 'Test Author', authorProfileIcon: 'avatar.jpg', authorIsMe: true),
|
||||
videoUrl: 'https://youtube.com/watch?v=original',
|
||||
createdAt: DateTime.now(),
|
||||
updatedAt: DateTime.now(),
|
||||
title: 'Original Title',
|
||||
totalLikes: 5,
|
||||
totalViews: 10,
|
||||
totalComments: 3,
|
||||
wasLikedByMe: false,
|
||||
);
|
||||
|
||||
Widget updateFlowWidget = BlocListener<BlogPostCreateCubit, BlogPostCreateState>(
|
||||
listener: (context, state) {
|
||||
if (state is BlogPostCreateStateLoaded) {
|
||||
context.read<FeedCubit>().loadFeedBlogPostList();
|
||||
}
|
||||
},
|
||||
child: Column(
|
||||
children: [
|
||||
TextFormField(
|
||||
key: AppKeys.blogPostCreateScreenTitleTextField,
|
||||
decoration: const InputDecoration(labelText: 'Заголовок'),
|
||||
controller: TextEditingController(text: existingBlogPost.title),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
TextFormField(
|
||||
key: AppKeys.blogPostCreateScreenContentTextField,
|
||||
decoration: const InputDecoration(labelText: 'Описание'),
|
||||
maxLines: 6,
|
||||
minLines: 4,
|
||||
controller: TextEditingController(text: existingBlogPost.description),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
TextFormField(
|
||||
key: AppKeys.blogPostCreateScreenVideoLinkTextField,
|
||||
decoration: const InputDecoration(labelText: 'Ссылка на видео'),
|
||||
controller: TextEditingController(text: existingBlogPost.videoUrl),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
ElevatedButton(
|
||||
key: AppKeys.blogPostCreateScreenCreateButton,
|
||||
onPressed: () async {
|
||||
await blogPostCreateCubit.updateBlogPost(
|
||||
id: existingBlogPost.id,
|
||||
title: 'Updated Title',
|
||||
description: 'Updated Description',
|
||||
videoUrl: 'https://youtube.com/watch?v=updated',
|
||||
image: null,
|
||||
oldImageUrl: existingBlogPost.image,
|
||||
);
|
||||
},
|
||||
child: const Text('Обновить'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(updateFlowWidget));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.enterText(find.byKey(AppKeys.blogPostCreateScreenTitleTextField), 'Updated Title');
|
||||
await tester.enterText(find.byKey(AppKeys.blogPostCreateScreenContentTextField), 'Updated Description');
|
||||
await tester.enterText(find.byKey(AppKeys.blogPostCreateScreenVideoLinkTextField), 'https://youtube.com/watch?v=updated');
|
||||
await tester.pump();
|
||||
|
||||
await tester.tap(find.byKey(AppKeys.blogPostCreateScreenCreateButton));
|
||||
await tester.pump();
|
||||
|
||||
// Assert
|
||||
verify(
|
||||
mockBlogPostRepository.updateBlogPost(1, 'Updated Title', 'https://youtube.com/watch?v=updated', 'Updated Description', null, 'original_image.jpg'),
|
||||
).called(1);
|
||||
verify(mockFeedCubit.loadFeedBlogPostList()).called(1);
|
||||
});
|
||||
|
||||
testWidgets('should handle blog post deletion flow', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
when(mockBlogPostRepository.deleteBlogPost(any)).thenAnswer((_) async {});
|
||||
when(mockFeedCubit.loadFeedBlogPostList()).thenAnswer((_) async {});
|
||||
|
||||
Widget deleteFlowWidget = Column(
|
||||
children: [
|
||||
const Text('Удалить блог пост?'),
|
||||
const SizedBox(height: 16),
|
||||
ElevatedButton(
|
||||
key: const Key('deleteBlogPostButton'),
|
||||
onPressed: () async {
|
||||
await blogPostCreateCubit.deleteBlogPost(1);
|
||||
mockFeedCubit.loadFeedBlogPostList();
|
||||
},
|
||||
child: const Text('Удалить'),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(deleteFlowWidget));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.byKey(const Key('deleteBlogPostButton')));
|
||||
await tester.pump();
|
||||
|
||||
// Assert
|
||||
verify(mockBlogPostRepository.deleteBlogPost(1)).called(1);
|
||||
verify(mockFeedCubit.loadFeedBlogPostList()).called(1);
|
||||
});
|
||||
|
||||
testWidgets('should handle error flow during blog post creation', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
when(mockBlogPostRepository.createBlogPost(any, any, any, any)).thenThrow(Exception('Network error'));
|
||||
|
||||
Widget errorFlowWidget = BlocBuilder<BlogPostCreateCubit, BlogPostCreateState>(
|
||||
builder: (context, state) {
|
||||
if (state is BlogPostCreateStateError) {
|
||||
return Column(
|
||||
children: [
|
||||
Text('Error: ${state.errorMessage}'),
|
||||
const SizedBox(height: 16),
|
||||
ElevatedButton(
|
||||
key: const Key('retryButton'),
|
||||
onPressed: () async {
|
||||
await blogPostCreateCubit.createBlogPost(title: 'Retry Title', description: 'Retry Description', videoUrl: null, image: null);
|
||||
},
|
||||
child: const Text('Повторить'),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
return const Text('No Error');
|
||||
},
|
||||
);
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(errorFlowWidget));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Trigger error
|
||||
await blogPostCreateCubit.createBlogPost(title: 'Test Title', description: 'Test Description', videoUrl: null, image: null);
|
||||
await tester.pump();
|
||||
|
||||
// Assert
|
||||
expect(find.textContaining('Error:'), findsOneWidget);
|
||||
expect(find.byKey(const Key('retryButton')), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('should handle loading state during blog post creation', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
when(mockBlogPostRepository.createBlogPost(any, any, any, any)).thenAnswer((_) async {
|
||||
await Future.delayed(const Duration(milliseconds: 100));
|
||||
});
|
||||
|
||||
Widget loadingFlowWidget = BlocBuilder<BlogPostCreateCubit, BlogPostCreateState>(
|
||||
builder: (context, state) {
|
||||
if (state is BlogPostCreateStateLoading) {
|
||||
return const Column(children: [CircularProgressIndicator(), SizedBox(height: 16), Text('Создание блог поста...')]);
|
||||
}
|
||||
return const Text('Not Loading');
|
||||
},
|
||||
);
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(loadingFlowWidget));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Trigger loading
|
||||
await blogPostCreateCubit.createBlogPost(title: 'Test Title', description: 'Test Description', videoUrl: null, image: null);
|
||||
await tester.pump();
|
||||
|
||||
// Assert
|
||||
expect(find.byType(CircularProgressIndicator), findsOneWidget);
|
||||
expect(find.text('Создание блог поста...'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('should handle success state after blog post creation', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
when(mockBlogPostRepository.createBlogPost(any, any, any, any)).thenAnswer((_) async {});
|
||||
when(mockFeedCubit.loadFeedBlogPostList()).thenAnswer((_) async {});
|
||||
|
||||
Widget successFlowWidget = BlocListener<BlogPostCreateCubit, BlogPostCreateState>(
|
||||
listener: (context, state) {
|
||||
if (state is BlogPostCreateStateLoaded) {
|
||||
context.read<FeedCubit>().loadFeedBlogPostList();
|
||||
}
|
||||
},
|
||||
child: BlocBuilder<BlogPostCreateCubit, BlogPostCreateState>(
|
||||
builder: (context, state) {
|
||||
if (state is BlogPostCreateStateLoaded) {
|
||||
return const Column(
|
||||
children: [
|
||||
Icon(Icons.check_circle, color: Colors.green, size: 48),
|
||||
SizedBox(height: 16),
|
||||
Text('Блог пост успешно создан!'),
|
||||
],
|
||||
);
|
||||
}
|
||||
return const Text('Not Loaded');
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(successFlowWidget));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Trigger success
|
||||
await blogPostCreateCubit.createBlogPost(title: 'Test Title', description: 'Test Description', videoUrl: null, image: null);
|
||||
await tester.pump();
|
||||
|
||||
// Assert
|
||||
expect(find.byIcon(Icons.check_circle), findsOneWidget);
|
||||
expect(find.text('Блог пост успешно создан!'), findsOneWidget);
|
||||
verify(mockFeedCubit.loadFeedBlogPostList()).called(1);
|
||||
});
|
||||
|
||||
testWidgets('should handle form validation flow', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
Widget validationFlowWidget = Column(
|
||||
children: [
|
||||
TextFormField(
|
||||
key: AppKeys.blogPostCreateScreenTitleTextField,
|
||||
decoration: const InputDecoration(labelText: 'Заголовок', errorText: 'Заголовок обязателен'),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
TextFormField(
|
||||
key: AppKeys.blogPostCreateScreenContentTextField,
|
||||
decoration: const InputDecoration(labelText: 'Описание', errorText: 'Описание обязательно'),
|
||||
maxLines: 6,
|
||||
minLines: 4,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
TextFormField(
|
||||
key: AppKeys.blogPostCreateScreenVideoLinkTextField,
|
||||
decoration: const InputDecoration(labelText: 'Ссылка на видео', errorText: 'Введите корректную ссылку'),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
ElevatedButton(key: AppKeys.blogPostCreateScreenCreateButton, onPressed: () {}, child: const Text('Опубликовать')),
|
||||
],
|
||||
);
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(validationFlowWidget));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Assert
|
||||
expect(find.text('Заголовок обязателен'), findsOneWidget);
|
||||
expect(find.text('Описание обязательно'), findsOneWidget);
|
||||
expect(find.text('Введите корректную ссылку'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('should handle image upload flow', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
bool imageUploadInitiated = false;
|
||||
Widget imageUploadWidget = Column(
|
||||
children: [
|
||||
const Text('Изображение поста'),
|
||||
const SizedBox(height: 8),
|
||||
GestureDetector(
|
||||
key: AppKeys.imageWidget,
|
||||
onTap: () {
|
||||
imageUploadInitiated = true;
|
||||
},
|
||||
child: Container(
|
||||
height: 200,
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Colors.grey),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: const Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(Icons.add_photo_alternate, size: 48, color: Colors.grey),
|
||||
SizedBox(height: 8),
|
||||
Text('Добавить изображение'),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
ElevatedButton(
|
||||
key: AppKeys.blogPostCreateScreenCreateButton,
|
||||
onPressed: () async {
|
||||
await blogPostCreateCubit.createBlogPost(
|
||||
title: 'Test Title',
|
||||
description: 'Test Description',
|
||||
videoUrl: null,
|
||||
image: null, // В реальном тесте здесь был бы XFile
|
||||
);
|
||||
},
|
||||
child: const Text('Опубликовать'),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(imageUploadWidget));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.byKey(AppKeys.imageWidget));
|
||||
await tester.pump();
|
||||
|
||||
// Assert
|
||||
expect(imageUploadInitiated, isTrue);
|
||||
});
|
||||
|
||||
testWidgets('should handle complete blog post creation with all features', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
when(mockBlogPostRepository.createBlogPost(any, any, any, any)).thenAnswer((_) async {});
|
||||
when(mockFeedCubit.loadFeedBlogPostList()).thenAnswer((_) async {});
|
||||
|
||||
Widget completeFlowWidget = BlocListener<BlogPostCreateCubit, BlogPostCreateState>(
|
||||
listener: (context, state) {
|
||||
if (state is BlogPostCreateStateLoaded) {
|
||||
context.read<FeedCubit>().loadFeedBlogPostList();
|
||||
}
|
||||
},
|
||||
child: Column(
|
||||
children: [
|
||||
const Text('Заголовок'),
|
||||
const SizedBox(height: 8),
|
||||
TextFormField(
|
||||
key: AppKeys.blogPostCreateScreenTitleTextField,
|
||||
decoration: const InputDecoration(hintText: 'Введите заголовок поста'),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
const Text('Описание'),
|
||||
const SizedBox(height: 8),
|
||||
TextFormField(key: AppKeys.blogPostCreateScreenContentTextField, decoration: const InputDecoration(), maxLines: 6, minLines: 4),
|
||||
const SizedBox(height: 16),
|
||||
const Text('Изображение поста'),
|
||||
const SizedBox(height: 8),
|
||||
GestureDetector(
|
||||
key: AppKeys.imageWidget,
|
||||
onTap: () {},
|
||||
child: Container(
|
||||
height: 200,
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Colors.grey),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: const Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(Icons.add_photo_alternate, size: 48, color: Colors.grey),
|
||||
SizedBox(height: 8),
|
||||
Text('Добавить изображение'),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
const Text('Ссылка на видео'),
|
||||
const SizedBox(height: 8),
|
||||
TextFormField(key: AppKeys.blogPostCreateScreenVideoLinkTextField, decoration: const InputDecoration()),
|
||||
const SizedBox(height: 32),
|
||||
ElevatedButton(
|
||||
key: AppKeys.blogPostCreateScreenCreateButton,
|
||||
onPressed: () async {
|
||||
await blogPostCreateCubit.createBlogPost(
|
||||
title: 'Complete Test Title',
|
||||
description: 'Complete Test Description',
|
||||
videoUrl: 'https://youtube.com/watch?v=complete',
|
||||
image: null,
|
||||
);
|
||||
},
|
||||
child: const Text('Опубликовать'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(completeFlowWidget));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.enterText(find.byKey(AppKeys.blogPostCreateScreenTitleTextField), 'Complete Test Title');
|
||||
await tester.enterText(find.byKey(AppKeys.blogPostCreateScreenContentTextField), 'Complete Test Description');
|
||||
await tester.enterText(find.byKey(AppKeys.blogPostCreateScreenVideoLinkTextField), 'https://youtube.com/watch?v=complete');
|
||||
await tester.pump();
|
||||
|
||||
await tester.tap(find.byKey(AppKeys.blogPostCreateScreenCreateButton));
|
||||
await tester.pump();
|
||||
|
||||
// Assert
|
||||
verify(mockBlogPostRepository.createBlogPost('Complete Test Title', 'https://youtube.com/watch?v=complete', 'Complete Test Description', null)).called(1);
|
||||
verify(mockFeedCubit.loadFeedBlogPostList()).called(1);
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user