feat(personalized_feed): added fixtures for new feed, added new feed parsing and layout, added webview player for post videos

This commit is contained in:
misha
2026-07-20 16:47:19 +03:00
parent bd2de07b8f
commit 48a984f5e9
23 changed files with 1330 additions and 903 deletions

View File

@@ -1,7 +1,5 @@
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';
@@ -13,211 +11,52 @@ import 'feed_cubit_test.mocks.dart';
@GenerateNiceMocks([MockSpec<FeedRepository>()])
void main() {
group('FeedCubit', () {
FeedCubit? feedCubit;
late MockFeedRepository mockFeedRepository;
late FeedBlogPostSettingsModel feedBlogPostsFixture;
group('FeedCubit cursor flow', () {
late FeedCubit cubit;
late MockFeedRepository repository;
final post = FeedBlogPostModel(
id: 10,
state: 'active',
image: '',
description: 'description',
author: const Author(authorSlug: 'slug', authorName: 'name', authorProfileIcon: '', authorIsMe: false),
videoUrl: '',
createdAt: DateTime(2024, 1, 1),
updatedAt: DateTime(2024, 1, 1),
title: 'title',
totalLikes: 1,
totalViews: 1,
totalComments: 1,
wasLikedByMe: false,
);
setUp(() {
mockFeedRepository = MockFeedRepository();
// Создаём тестовые данные
final testPosts = [
FeedBlogPostModel(
id: 1,
state: 'published',
image: 'https://example.com/image1.jpg',
description: 'Описание поста 1',
author: const Author(
authorSlug: 'author-1',
authorName: 'Автор 1',
authorProfileIcon: 'https://example.com/photo1.jpg',
authorIsMe: false,
isEntitySubscribedByMe: null,
authorPlaceInRating: null,
),
videoUrl: '',
createdAt: DateTime.now(),
updatedAt: DateTime.now(),
title: 'Тестовый пост 1',
totalLikes: 10,
totalViews: 100,
totalComments: 5,
wasLikedByMe: false,
),
FeedBlogPostModel(
id: 2,
state: 'published',
image: 'https://example.com/image2.jpg',
description: 'Описание поста 2',
author: const Author(
authorSlug: 'author-2',
authorName: 'Автор 2',
authorProfileIcon: 'https://example.com/photo2.jpg',
authorIsMe: false,
isEntitySubscribedByMe: null,
authorPlaceInRating: null,
),
videoUrl: '',
createdAt: DateTime.now(),
updatedAt: DateTime.now(),
title: 'Тестовый пост 2',
totalLikes: 20,
totalViews: 200,
totalComments: 10,
wasLikedByMe: true,
),
];
feedBlogPostsFixture = FeedBlogPostSettingsModel(
feedBlogPostList: testPosts,
meta: const Meta(
currentPage: 1,
from: null,
lastPage: 3,
links: [],
path: null,
perPage: 10,
to: null,
total: 25,
totalMinPrice: null,
totalMaxPrice: null,
),
);
repository = MockFeedRepository();
cubit = FeedCubit(feedRepository: repository);
});
tearDown(() async {
if (feedCubit != null && !feedCubit!.isClosed) {
await Future.delayed(const Duration(milliseconds: 100));
feedCubit!.close();
feedCubit = null;
}
await cubit.close();
});
test('начальное состояние - FeedStateLoading', () {
feedCubit = FeedCubit(feedRepository: mockFeedRepository);
expect(feedCubit!.state, const FeedState.loading());
test('error state on load failure', () async {
when(repository.getFeed(search: null, cursor: null, currentUserId: null)).thenThrow(Exception('boom'));
await cubit.loadFeedBlogPostList();
expect(cubit.state, isA<FeedStateError>());
});
test('loadFeedBlogPostList - успешная загрузка постов', () async {
feedCubit = FeedCubit(feedRepository: mockFeedRepository);
test('updateFeedBlogPostList updates likes in loaded state', () async {
when(repository.getFeed(search: null, cursor: null, currentUserId: null)).thenAnswer((_) async => (posts: [post], nextCursor: null));
await cubit.loadFeedBlogPostList();
when(mockFeedRepository.getFeedBlogPosts(query: null))
.thenAnswer((_) async => feedBlogPostsFixture);
cubit.updateFeedBlogPostList(post, true);
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, 2);
expect(loadedState.isLoading, false);
expect(feedCubit!.lastPage, 3);
});
test('loadFeedBlogPostList - загрузка с запросом', () async {
feedCubit = FeedCubit(feedRepository: mockFeedRepository);
const query = 'тест';
when(mockFeedRepository.getFeedBlogPosts(query: query))
.thenAnswer((_) async => feedBlogPostsFixture);
await feedCubit!.loadFeedBlogPostList(query: query);
await Future.delayed(const Duration(milliseconds: 100));
expect(feedCubit!.state, isA<FeedStateLoaded>());
verify(mockFeedRepository.getFeedBlogPosts(query: query)).called(1);
});
test('loadFeedBlogPostList - обработка ошибки', () async {
feedCubit = FeedCubit(feedRepository: mockFeedRepository);
when(mockFeedRepository.getFeedBlogPosts(query: anyNamed('query')))
.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'));
});
test('updateFeedBlogPostList - обновление лайка поста', () {
feedCubit = FeedCubit(feedRepository: mockFeedRepository);
// Сначала загружаем посты
when(mockFeedRepository.getFeedBlogPosts(query: anyNamed('query')))
.thenAnswer((_) async => feedBlogPostsFixture);
// Устанавливаем состояние loaded вручную для теста
feedCubit!.emit(FeedState.loaded(
feedBlogPostList: feedBlogPostsFixture.feedBlogPostList,
isLoading: false,
));
feedCubit!.lastPage = 3;
final postToUpdate = feedBlogPostsFixture.feedBlogPostList.first;
feedCubit!.updateFeedBlogPostList(postToUpdate, true);
expect(feedCubit!.state, isA<FeedStateLoaded>());
final loadedState = feedCubit!.state as FeedStateLoaded;
final updatedPost = loadedState.feedBlogPostList.firstWhere((p) => p.id == postToUpdate.id);
expect(updatedPost.wasLikedByMe, true);
expect(updatedPost.totalLikes, postToUpdate.totalLikes + 1);
});
test('updateFeedBlogPostList - снятие лайка', () {
feedCubit = FeedCubit(feedRepository: mockFeedRepository);
feedCubit!.emit(FeedState.loaded(
feedBlogPostList: feedBlogPostsFixture.feedBlogPostList,
isLoading: false,
));
feedCubit!.lastPage = 3;
final postToUpdate = feedBlogPostsFixture.feedBlogPostList[1]; // У этого wasLikedByMe = true
final initialLikes = postToUpdate.totalLikes;
feedCubit!.updateFeedBlogPostList(postToUpdate, false);
expect(feedCubit!.state, isA<FeedStateLoaded>());
final loadedState = feedCubit!.state as FeedStateLoaded;
final updatedPost = loadedState.feedBlogPostList.firstWhere((p) => p.id == postToUpdate.id);
expect(updatedPost.wasLikedByMe, false);
expect(updatedPost.totalLikes, initialLikes - 1);
});
test('updateFeedBlogPostList - игнорирует обновление если состояние не loaded', () {
feedCubit = FeedCubit(feedRepository: mockFeedRepository);
// Состояние loading
feedCubit!.emit(const FeedState.loading());
final postToUpdate = feedBlogPostsFixture.feedBlogPostList.first;
feedCubit!.updateFeedBlogPostList(postToUpdate, true);
// Состояние не должно измениться
expect(feedCubit!.state, const FeedState.loading());
});
test('updateFeedBlogPostList - игнорирует обновление если пост не найден', () {
feedCubit = FeedCubit(feedRepository: mockFeedRepository);
feedCubit!.emit(FeedState.loaded(
feedBlogPostList: feedBlogPostsFixture.feedBlogPostList,
isLoading: false,
));
feedCubit!.lastPage = 3;
// Создаём пост с несуществующим ID
final nonExistentPost = feedBlogPostsFixture.feedBlogPostList.first.copyWith(id: 999);
feedCubit!.updateFeedBlogPostList(nonExistentPost, true);
// Список не должен измениться
final loadedState = feedCubit!.state as FeedStateLoaded;
expect(loadedState.feedBlogPostList.length, 2);
final loaded = cubit.state as FeedStateLoaded;
expect(loaded.feedBlogPostList.first.wasLikedByMe, true);
expect(loaded.feedBlogPostList.first.totalLikes, 2);
});
});
}

View File

@@ -1,404 +1,66 @@
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;
late MockFeedRepository repository;
late FeedCubit cubit;
final testBlogPost = FeedBlogPostModel(
final post = 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,
state: 'active',
image: 'https://example.com/image.png',
description: 'text',
author: const Author(authorSlug: 'a', authorName: 'Author', authorProfileIcon: 'https://example.com/avatar.png', authorIsMe: false),
videoUrl: '',
createdAt: DateTime(2024, 1, 1),
updatedAt: DateTime(2024, 1, 1),
title: 'Title',
totalLikes: 1,
totalViews: 2,
totalComments: 3,
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();
repository = MockFeedRepository();
cubit = FeedCubit(feedRepository: repository);
});
tearDown(() async {
if (feedCubit != null && !feedCubit!.isClosed) {
await Future.delayed(const Duration(milliseconds: 100));
feedCubit!.close();
feedCubit = null;
}
await cubit.close();
});
test('начальное состояние - loading', () {
feedCubit = FeedCubit(feedRepository: mockFeedRepository);
expect(feedCubit!.state, const FeedState.loading());
test('loadFeedBlogPostList emits loaded from getFeed', () async {
when(repository.getFeed(search: null, cursor: null, currentUserId: null)).thenAnswer((_) async => (posts: [post], nextCursor: 'next'));
await cubit.loadFeedBlogPostList();
expect(cubit.state, isA<FeedStateLoaded>());
final loaded = cubit.state as FeedStateLoaded;
expect(loaded.feedBlogPostList, hasLength(1));
verify(repository.getFeed(search: null, cursor: null, currentUserId: null)).called(1);
});
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),
);
test('loadNextPage appends posts', () async {
when(repository.getFeed(search: null, cursor: null, currentUserId: null)).thenAnswer((_) async => (posts: [post], nextCursor: 'next'));
when(repository.getFeed(search: null, cursor: 'next', currentUserId: null)).thenAnswer((_) async => (posts: [post.copyWith(id: 2)], nextCursor: null));
when(mockFeedRepository.getFeedBlogPosts(query: null)).thenAnswer((_) async => expectedSettings);
await cubit.loadFeedBlogPostList();
await cubit.loadNextPage();
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);
final loaded = cubit.state as FeedStateLoaded;
expect(loaded.feedBlogPostList, hasLength(2));
verify(repository.getFeed(search: null, cursor: 'next', currentUserId: null)).called(1);
});
});
}

View File

@@ -1,12 +1,10 @@
import 'package:eatvio_app/core/app_config.dart';
import 'package:eatvio_app/core/app_initializer.dart';
import 'package:eatvio_app/core/locator_service.dart';
import 'package:eatvio_app/core/models/pagination_model.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/core/routing/route_constants.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/presentation/widgets/blog_posts.dart';
@@ -378,12 +376,8 @@ void main() {
// Arrange
feedCubit.emit(FeedState.loaded(feedBlogPostList: [testBlogPost], isLoading: false));
when(mockFeedRepository.getFeedBlogPosts(query: null)).thenAnswer(
(_) async => 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.getFeed(search: null, cursor: null, currentUserId: null))
.thenAnswer((_) async => (posts: [testBlogPost, testBlogPost2], nextCursor: null));
final widget = BlogPosts(handleVisibility: ({required visibleFraction, required id, required type}) {});

View File

@@ -81,7 +81,7 @@ void main() {
when(cubit.stream).thenAnswer((_) => Stream.value(state));
when(cubit.close()).thenAnswer((_) async => Future<void>.value());
await tester.pumpWidget(BlocProvider<FeedCubit>.value(value: cubit, child: wrap(const BlogPostWidget(index: 0))));
await tester.pumpWidget(BlocProvider<FeedCubit>.value(value: cubit, child: wrap(BlogPostWidget(post: mockBlogPost))));
await tester.pump();
await tester.pump(const Duration(milliseconds: 100));