forked from eatvio/eatvio_app
405 lines
17 KiB
Dart
405 lines
17 KiB
Dart
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);
|
||
});
|
||
});
|
||
}
|