Files
eatvio_app/test/features/feed/presentation/bloc/feed_cubit_test.dart

405 lines
17 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 'package:eatvio_app/core/models/pagination_model.dart';
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
import 'package:eatvio_app/features/feed/models/feed_blog_post_model.dart';
import 'package:eatvio_app/features/feed/models/feed_blog_post_settings_model.dart';
import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_cubit.dart';
import 'package:eatvio_app/features/feed/presentation/bloc/feed_cubit/feed_state.dart';
import 'package:eatvio_app/features/feed/repositories/feed_repository.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'feed_cubit_test.mocks.dart';
@GenerateNiceMocks([MockSpec<FeedRepository>()])
void main() {
group('FeedCubit', () {
FeedCubit? feedCubit;
late MockFeedRepository mockFeedRepository;
final testBlogPost = FeedBlogPostModel(
id: 1,
title: 'Test Post',
description: 'Test description',
image: 'https://example.com/image.jpg',
state: 'published',
author: const Author(authorSlug: 'author-1', authorName: 'Author Name', authorProfileIcon: 'https://example.com/icon.jpg', authorIsMe: false),
videoUrl: 'https://example.com/video.mp4',
createdAt: DateTime(2024, 1, 15),
updatedAt: DateTime(2024, 1, 15),
totalLikes: 10,
totalViews: 100,
totalComments: 5,
wasLikedByMe: false,
);
final testBlogPost2 = FeedBlogPostModel(
id: 2,
title: 'Test Post 2',
description: 'Test description 2',
image: 'https://example.com/image2.jpg',
state: 'published',
author: const Author(authorSlug: 'author-2', authorName: 'Author Name 2', authorProfileIcon: 'https://example.com/icon2.jpg', authorIsMe: false),
videoUrl: 'https://example.com/video2.mp4',
createdAt: DateTime(2024, 1, 16),
updatedAt: DateTime(2024, 1, 16),
totalLikes: 20,
totalViews: 200,
totalComments: 10,
wasLikedByMe: true,
);
setUp(() {
mockFeedRepository = MockFeedRepository();
});
tearDown(() async {
if (feedCubit != null && !feedCubit!.isClosed) {
await Future.delayed(const Duration(milliseconds: 100));
feedCubit!.close();
feedCubit = null;
}
});
test('начальное состояние - loading', () {
feedCubit = FeedCubit(feedRepository: mockFeedRepository);
expect(feedCubit!.state, const FeedState.loading());
});
test('loadFeedBlogPostList - успешно загружает список постов', () async {
feedCubit = FeedCubit(feedRepository: mockFeedRepository);
final expectedSettings = FeedBlogPostSettingsModel(
feedBlogPostList: [testBlogPost],
meta: const Meta(currentPage: 1, lastPage: 3, total: 3, perPage: 10, from: null, links: [Link(url: null, label: null, active: null)], to: null),
);
when(mockFeedRepository.getFeedBlogPosts(query: null)).thenAnswer((_) async => expectedSettings);
await feedCubit!.loadFeedBlogPostList();
await Future.delayed(const Duration(milliseconds: 100));
expect(feedCubit!.state, isA<FeedStateLoaded>());
final loadedState = feedCubit!.state as FeedStateLoaded;
expect(loadedState.feedBlogPostList.length, 1);
expect(loadedState.feedBlogPostList.first.id, 1);
expect(loadedState.isLoading, false);
expect(feedCubit!.lastPage, 3);
verify(mockFeedRepository.getFeedBlogPosts(query: null)).called(1);
});
test('loadFeedBlogPostList - загружает список постов с query', () async {
feedCubit = FeedCubit(feedRepository: mockFeedRepository);
const query = 'test query';
final expectedSettings = FeedBlogPostSettingsModel(
feedBlogPostList: [testBlogPost, testBlogPost2],
meta: const Meta(currentPage: 1, lastPage: 1, total: 2, perPage: 10, from: null, links: [Link(url: null, label: null, active: null)], to: null),
);
when(mockFeedRepository.getFeedBlogPosts(query: query)).thenAnswer((_) async => expectedSettings);
await feedCubit!.loadFeedBlogPostList(query: query);
await Future.delayed(const Duration(milliseconds: 100));
expect(feedCubit!.state, isA<FeedStateLoaded>());
final loadedState = feedCubit!.state as FeedStateLoaded;
expect(loadedState.feedBlogPostList.length, 2);
verify(mockFeedRepository.getFeedBlogPosts(query: query)).called(1);
});
test('loadFeedBlogPostList - обрабатывает ошибку', () async {
feedCubit = FeedCubit(feedRepository: mockFeedRepository);
final exception = Exception('Network error');
when(mockFeedRepository.getFeedBlogPosts(query: null)).thenThrow(exception);
await feedCubit!.loadFeedBlogPostList();
await Future.delayed(const Duration(milliseconds: 100));
expect(feedCubit!.state, isA<FeedStateError>());
final errorState = feedCubit!.state as FeedStateError;
expect(errorState.errorMessage, contains('Exception: Network error'));
});
test('loadNextPage - загружает следующую страницу', () async {
feedCubit = FeedCubit(feedRepository: mockFeedRepository);
final firstPageSettings = FeedBlogPostSettingsModel(
feedBlogPostList: [testBlogPost],
meta: const Meta(currentPage: 1, lastPage: 3, total: 3, perPage: 10, from: null, links: [Link(url: null, label: null, active: null)], to: null),
);
final secondPageSettings = FeedBlogPostSettingsModel(
feedBlogPostList: [testBlogPost2],
meta: const Meta(currentPage: 2, lastPage: 3, total: 3, perPage: 10, from: null, links: [Link(url: null, label: null, active: null)], to: null),
);
when(mockFeedRepository.getFeedBlogPosts(query: null)).thenAnswer((_) async => firstPageSettings);
await feedCubit!.loadFeedBlogPostList();
await Future.delayed(const Duration(milliseconds: 100));
when(mockFeedRepository.getFeedBlogPosts(page: 2, query: null)).thenAnswer((_) async => secondPageSettings);
await feedCubit!.loadNextPage();
await Future.delayed(const Duration(milliseconds: 100));
expect(feedCubit!.state, isA<FeedStateLoaded>());
final loadedState = feedCubit!.state as FeedStateLoaded;
expect(loadedState.feedBlogPostList.length, 2);
expect(feedCubit!.currentPage, 2);
expect(loadedState.isLoading, false);
verify(mockFeedRepository.getFeedBlogPosts(page: 2, query: null)).called(1);
});
test('loadNextPage - не загружает если уже на последней странице', () async {
feedCubit = FeedCubit(feedRepository: mockFeedRepository);
final settings = FeedBlogPostSettingsModel(
feedBlogPostList: [testBlogPost],
meta: const Meta(currentPage: 1, lastPage: 1, total: 1, perPage: 10, from: null, links: [Link(url: null, label: null, active: null)], to: null),
);
when(mockFeedRepository.getFeedBlogPosts(query: null)).thenAnswer((_) async => settings);
await feedCubit!.loadFeedBlogPostList();
await Future.delayed(const Duration(milliseconds: 100));
await feedCubit!.loadNextPage();
await Future.delayed(const Duration(milliseconds: 100));
verifyNever(mockFeedRepository.getFeedBlogPosts(page: 2, query: anyNamed('query')));
});
test('loadNextPage - не загружает если уже загружается', () async {
feedCubit = FeedCubit(feedRepository: mockFeedRepository);
final settings = FeedBlogPostSettingsModel(
feedBlogPostList: [testBlogPost],
meta: const Meta(currentPage: 1, lastPage: 3, total: 3, perPage: 10, from: null, links: [Link(url: null, label: null, active: null)], to: null),
);
when(mockFeedRepository.getFeedBlogPosts(query: null)).thenAnswer((_) async => settings);
await feedCubit!.loadFeedBlogPostList();
await Future.delayed(const Duration(milliseconds: 100));
feedCubit!.isLoading = true;
await feedCubit!.loadNextPage();
await Future.delayed(const Duration(milliseconds: 100));
verifyNever(mockFeedRepository.getFeedBlogPosts(page: 2, query: anyNamed('query')));
});
test('loadNextPage - обрабатывает ошибку при загрузке следующей страницы', () async {
feedCubit = FeedCubit(feedRepository: mockFeedRepository);
final firstPageSettings = FeedBlogPostSettingsModel(
feedBlogPostList: [testBlogPost],
meta: const Meta(currentPage: 1, lastPage: 3, total: 3, perPage: 10, from: null, links: [Link(url: null, label: null, active: null)], to: null),
);
when(mockFeedRepository.getFeedBlogPosts(query: null)).thenAnswer((_) async => firstPageSettings);
await feedCubit!.loadFeedBlogPostList();
await Future.delayed(const Duration(milliseconds: 100));
final exception = Exception('Network error');
when(mockFeedRepository.getFeedBlogPosts(page: 2, query: null)).thenThrow(exception);
await feedCubit!.loadNextPage();
await Future.delayed(const Duration(milliseconds: 100));
expect(feedCubit!.state, isA<FeedStateError>());
final errorState = feedCubit!.state as FeedStateError;
expect(errorState.errorMessage, contains('Exception: Network error'));
});
test('loadNextPage - сохраняет query при загрузке следующей страницы', () async {
feedCubit = FeedCubit(feedRepository: mockFeedRepository);
const query = 'test query';
final firstPageSettings = FeedBlogPostSettingsModel(
feedBlogPostList: [testBlogPost],
meta: const Meta(currentPage: 1, lastPage: 3, total: 3, perPage: 10, from: null, links: [Link(url: null, label: null, active: null)], to: null),
);
final secondPageSettings = FeedBlogPostSettingsModel(
feedBlogPostList: [testBlogPost2],
meta: const Meta(currentPage: 2, lastPage: 3, total: 3, perPage: 10, from: null, links: [Link(url: null, label: null, active: null)], to: null),
);
when(mockFeedRepository.getFeedBlogPosts(query: query)).thenAnswer((_) async => firstPageSettings);
await feedCubit!.loadFeedBlogPostList(query: query);
await Future.delayed(const Duration(milliseconds: 100));
when(mockFeedRepository.getFeedBlogPosts(page: 2, query: query)).thenAnswer((_) async => secondPageSettings);
await feedCubit!.loadNextPage();
await Future.delayed(const Duration(milliseconds: 100));
verify(mockFeedRepository.getFeedBlogPosts(page: 2, query: query)).called(1);
});
test('updateFeedBlogPostList - обновляет лайк поста', () async {
feedCubit = FeedCubit(feedRepository: mockFeedRepository);
final settings = FeedBlogPostSettingsModel(
feedBlogPostList: [testBlogPost],
meta: const Meta(currentPage: 1, lastPage: 1, total: 1, perPage: 10, from: null, links: [Link(url: null, label: null, active: null)], to: null),
);
when(mockFeedRepository.getFeedBlogPosts(query: null)).thenAnswer((_) async => settings);
await feedCubit!.loadFeedBlogPostList();
await Future.delayed(const Duration(milliseconds: 100));
feedCubit!.updateFeedBlogPostList(testBlogPost, true);
await Future.delayed(const Duration(milliseconds: 100));
expect(feedCubit!.state, isA<FeedStateLoaded>());
final loadedState = feedCubit!.state as FeedStateLoaded;
expect(loadedState.feedBlogPostList.first.wasLikedByMe, true);
expect(loadedState.feedBlogPostList.first.totalLikes, 11);
});
test('updateFeedBlogPostList - убирает лайк поста', () async {
feedCubit = FeedCubit(feedRepository: mockFeedRepository);
final settings = FeedBlogPostSettingsModel(
feedBlogPostList: [testBlogPost2],
meta: const Meta(currentPage: 1, lastPage: 1, total: 1, perPage: 10, from: null, links: [Link(url: null, label: null, active: null)], to: null),
);
when(mockFeedRepository.getFeedBlogPosts(query: null)).thenAnswer((_) async => settings);
await feedCubit!.loadFeedBlogPostList();
await Future.delayed(const Duration(milliseconds: 100));
feedCubit!.updateFeedBlogPostList(testBlogPost2, false);
await Future.delayed(const Duration(milliseconds: 100));
expect(feedCubit!.state, isA<FeedStateLoaded>());
final loadedState = feedCubit!.state as FeedStateLoaded;
expect(loadedState.feedBlogPostList.first.wasLikedByMe, false);
expect(loadedState.feedBlogPostList.first.totalLikes, 19);
});
test('updateFeedBlogPostList - не обновляет если пост не найден', () async {
feedCubit = FeedCubit(feedRepository: mockFeedRepository);
final settings = FeedBlogPostSettingsModel(
feedBlogPostList: [testBlogPost],
meta: const Meta(currentPage: 1, lastPage: 1, total: 1, perPage: 10, from: null, links: [Link(url: null, label: null, active: null)], to: null),
);
when(mockFeedRepository.getFeedBlogPosts(query: null)).thenAnswer((_) async => settings);
await feedCubit!.loadFeedBlogPostList();
await Future.delayed(const Duration(milliseconds: 100));
final nonExistentPost = testBlogPost.copyWith(id: 999);
final initialState = feedCubit!.state;
feedCubit!.updateFeedBlogPostList(nonExistentPost, true);
await Future.delayed(const Duration(milliseconds: 100));
expect(feedCubit!.state, initialState);
});
test('updateFeedBlogPostList - не обновляет если state не loaded', () {
feedCubit = FeedCubit(feedRepository: mockFeedRepository);
feedCubit!.updateFeedBlogPostList(testBlogPost, true);
expect(feedCubit!.state, const FeedState.loading());
});
test('updateFeedBlogPostList - не обновляет если post null', () async {
feedCubit = FeedCubit(feedRepository: mockFeedRepository);
final settings = FeedBlogPostSettingsModel(
feedBlogPostList: [testBlogPost],
meta: const Meta(currentPage: 1, lastPage: 1, total: 1, perPage: 10, from: null, links: [Link(url: null, label: null, active: null)], to: null),
);
when(mockFeedRepository.getFeedBlogPosts(query: null)).thenAnswer((_) async => settings);
await feedCubit!.loadFeedBlogPostList();
await Future.delayed(const Duration(milliseconds: 100));
final initialState = feedCubit!.state;
feedCubit!.updateFeedBlogPostList(null, true);
await Future.delayed(const Duration(milliseconds: 100));
expect(feedCubit!.state, initialState);
});
test('likePost - лайкает пост', () async {
feedCubit = FeedCubit(feedRepository: mockFeedRepository);
const postId = 1;
when(mockFeedRepository.likeBlogPost(id: postId)).thenAnswer((_) async => Future.value());
await feedCubit!.likePost(postId);
verify(mockFeedRepository.likeBlogPost(id: postId)).called(1);
});
test('likePost - обрабатывает ошибку без выбрасывания', () async {
feedCubit = FeedCubit(feedRepository: mockFeedRepository);
const postId = 1;
final exception = Exception('Network error');
when(mockFeedRepository.likeBlogPost(id: postId)).thenThrow(exception);
await feedCubit!.likePost(postId);
verify(mockFeedRepository.likeBlogPost(id: postId)).called(1);
});
test('unlikedPost - убирает лайк с поста', () async {
feedCubit = FeedCubit(feedRepository: mockFeedRepository);
const postId = 1;
when(mockFeedRepository.unlikedBlogPost(id: postId)).thenAnswer((_) async => Future.value());
await feedCubit!.unlikedPost(postId);
verify(mockFeedRepository.unlikedBlogPost(id: postId)).called(1);
});
test('viewPost - отправляет просмотр поста', () async {
feedCubit = FeedCubit(feedRepository: mockFeedRepository);
const postId = 1;
when(mockFeedRepository.viewPost(postId)).thenAnswer((_) async => Future.value());
await feedCubit!.viewPost(postId);
verify(mockFeedRepository.viewPost(postId)).called(1);
});
test('viewRecipe - отправляет просмотр рецепта', () async {
feedCubit = FeedCubit(feedRepository: mockFeedRepository);
const slug = 'recipe-1';
when(mockFeedRepository.viewRecipe(slug)).thenAnswer((_) async => Future.value());
await feedCubit!.viewRecipe(slug);
verify(mockFeedRepository.viewRecipe(slug)).called(1);
});
test('viewDiet - отправляет просмотр диеты', () async {
feedCubit = FeedCubit(feedRepository: mockFeedRepository);
const dietId = 2;
when(mockFeedRepository.viewDiet(dietId)).thenAnswer((_) async => Future.value());
await feedCubit!.viewDiet(dietId);
verify(mockFeedRepository.viewDiet(dietId)).called(1);
});
test('viewPlan - отправляет просмотр плана', () async {
feedCubit = FeedCubit(feedRepository: mockFeedRepository);
const slug = 'plan-1';
when(mockFeedRepository.viewPlan(slug)).thenAnswer((_) async => Future.value());
await feedCubit!.viewPlan(slug);
verify(mockFeedRepository.viewPlan(slug)).called(1);
});
});
}