forked from eatvio/eatvio_app
feat(tests): tests coverage 67.2
This commit is contained in:
@@ -578,6 +578,233 @@ void main() {
|
||||
|
||||
expect(result, false);
|
||||
});
|
||||
|
||||
test('loadNextPage - обработка ошибки', () async {
|
||||
dietsCubit = DietsCubit(
|
||||
feedRepository: mockFeedRepository,
|
||||
dietsRepository: mockDietRepository,
|
||||
);
|
||||
|
||||
const testDiet = Diet(
|
||||
id: 1,
|
||||
slug: 'test-diet',
|
||||
name: 'Test Diet',
|
||||
image: 'https://example.com/image.jpg',
|
||||
duration: 7,
|
||||
state: DietState.active,
|
||||
type: DietType(veg: true, light: false, carb: false, meat: false),
|
||||
details: 'Test details',
|
||||
dailySchedule: {},
|
||||
author: Author(authorSlug: 'author', authorName: 'Author Name', authorProfileIcon: 'https://example.com/icon.jpg', authorIsMe: false, isEntitySubscribedByMe: null, authorPlaceInRating: null),
|
||||
totalLikes: 10,
|
||||
totalViews: 100,
|
||||
totalComments: 5,
|
||||
wasLikedByMe: false,
|
||||
isFavorite: false,
|
||||
dailyNutrition: [],
|
||||
);
|
||||
|
||||
const firstPagePagination = DietsPagination(
|
||||
data: [testDiet],
|
||||
links: Links(first: 'https://example.com?page=1', last: 'https://example.com?page=2', prev: null, next: 'https://example.com?page=2'),
|
||||
meta: Meta(currentPage: 1, from: 1, lastPage: 2, links: [], perPage: 10, to: 1, total: 2),
|
||||
);
|
||||
|
||||
when(mockFeedRepository.getDiets(
|
||||
query: anyNamed('query'),
|
||||
types: anyNamed('types'),
|
||||
duration: anyNamed('duration'),
|
||||
sorting: anyNamed('sorting'),
|
||||
)).thenAnswer((_) async => firstPagePagination);
|
||||
|
||||
when(mockFeedRepository.getDiets(
|
||||
page: 2,
|
||||
query: anyNamed('query'),
|
||||
types: anyNamed('types'),
|
||||
duration: anyNamed('duration'),
|
||||
sorting: anyNamed('sorting'),
|
||||
)).thenThrow(Exception('Ошибка загрузки'));
|
||||
|
||||
await dietsCubit!.loadDiets();
|
||||
await Future.delayed(const Duration(milliseconds: 200));
|
||||
|
||||
await dietsCubit!.loadNextPage();
|
||||
await Future.delayed(const Duration(milliseconds: 200));
|
||||
|
||||
expect(dietsCubit!.state.errorMessage, isNotNull);
|
||||
});
|
||||
|
||||
test('loadNextPage - не загружает если lastPage == currentPage', () async {
|
||||
dietsCubit = DietsCubit(
|
||||
feedRepository: mockFeedRepository,
|
||||
dietsRepository: mockDietRepository,
|
||||
);
|
||||
|
||||
const testDiet = Diet(
|
||||
id: 1,
|
||||
slug: 'test-diet',
|
||||
name: 'Test Diet',
|
||||
image: 'https://example.com/image.jpg',
|
||||
duration: 7,
|
||||
state: DietState.active,
|
||||
type: DietType(veg: true, light: false, carb: false, meat: false),
|
||||
details: 'Test details',
|
||||
dailySchedule: {},
|
||||
author: Author(authorSlug: 'author', authorName: 'Author Name', authorProfileIcon: 'https://example.com/icon.jpg', authorIsMe: false, isEntitySubscribedByMe: null, authorPlaceInRating: null),
|
||||
totalLikes: 10,
|
||||
totalViews: 100,
|
||||
totalComments: 5,
|
||||
wasLikedByMe: false,
|
||||
isFavorite: false,
|
||||
dailyNutrition: [],
|
||||
);
|
||||
|
||||
const dietsPagination = DietsPagination(
|
||||
data: [testDiet],
|
||||
links: Links(first: 'https://example.com?page=1', last: 'https://example.com?page=1', prev: null, next: null),
|
||||
meta: Meta(currentPage: 1, from: 1, lastPage: 1, links: [], perPage: 10, to: 1, total: 1),
|
||||
);
|
||||
|
||||
when(mockFeedRepository.getDiets(
|
||||
query: anyNamed('query'),
|
||||
types: anyNamed('types'),
|
||||
duration: anyNamed('duration'),
|
||||
sorting: anyNamed('sorting'),
|
||||
)).thenAnswer((_) async => dietsPagination);
|
||||
|
||||
await dietsCubit!.loadDiets();
|
||||
await Future.delayed(const Duration(milliseconds: 200));
|
||||
|
||||
await dietsCubit!.loadNextPage();
|
||||
await Future.delayed(const Duration(milliseconds: 200));
|
||||
|
||||
// Не должно вызывать getDiets для страницы 2
|
||||
verifyNever(mockFeedRepository.getDiets(page: 2, query: anyNamed('query'), types: anyNamed('types'), duration: anyNamed('duration'), sorting: anyNamed('sorting')));
|
||||
});
|
||||
|
||||
test('loadDiets - загружает диеты с query', () async {
|
||||
dietsCubit = DietsCubit(
|
||||
feedRepository: mockFeedRepository,
|
||||
dietsRepository: mockDietRepository,
|
||||
);
|
||||
|
||||
const testDiet = Diet(
|
||||
id: 1,
|
||||
slug: 'test-diet',
|
||||
name: 'Test Diet',
|
||||
image: 'https://example.com/image.jpg',
|
||||
duration: 7,
|
||||
state: DietState.active,
|
||||
type: DietType(veg: true, light: false, carb: false, meat: false),
|
||||
details: 'Test details',
|
||||
dailySchedule: {},
|
||||
author: Author(authorSlug: 'author', authorName: 'Author Name', authorProfileIcon: 'https://example.com/icon.jpg', authorIsMe: false, isEntitySubscribedByMe: null, authorPlaceInRating: null),
|
||||
totalLikes: 10,
|
||||
totalViews: 100,
|
||||
totalComments: 5,
|
||||
wasLikedByMe: false,
|
||||
isFavorite: false,
|
||||
dailyNutrition: [],
|
||||
);
|
||||
|
||||
const dietsPagination = DietsPagination(
|
||||
data: [testDiet],
|
||||
links: Links(first: 'https://example.com?page=1', last: 'https://example.com?page=1', prev: null, next: null),
|
||||
meta: Meta(currentPage: 1, from: 1, lastPage: 1, links: [], perPage: 10, to: 1, total: 1),
|
||||
);
|
||||
|
||||
when(mockFeedRepository.getDiets(
|
||||
query: anyNamed('query'),
|
||||
types: anyNamed('types'),
|
||||
duration: anyNamed('duration'),
|
||||
sorting: anyNamed('sorting'),
|
||||
)).thenAnswer((_) async => dietsPagination);
|
||||
|
||||
await dietsCubit!.loadDiets(query: 'test');
|
||||
await Future.delayed(const Duration(milliseconds: 200));
|
||||
|
||||
verify(mockFeedRepository.getDiets(
|
||||
query: 'test',
|
||||
types: anyNamed('types'),
|
||||
duration: anyNamed('duration'),
|
||||
sorting: anyNamed('sorting'),
|
||||
)).called(1);
|
||||
});
|
||||
|
||||
test('close - отменяет подписки', () async {
|
||||
dietsCubit = DietsCubit(
|
||||
feedRepository: mockFeedRepository,
|
||||
dietsRepository: mockDietRepository,
|
||||
);
|
||||
|
||||
final likesStateSubject = BehaviorSubject<LikesState>();
|
||||
final favoriteStateSubject = BehaviorSubject<FavoriteState>();
|
||||
final commentsStateSubject = BehaviorSubject<CommentsState>();
|
||||
|
||||
when(mockDietRepository.dietLikeState).thenAnswer((_) => likesStateSubject.stream);
|
||||
when(mockDietRepository.dietFavoriteState).thenAnswer((_) => favoriteStateSubject.stream);
|
||||
when(mockDietRepository.dietCommentsState).thenAnswer((_) => commentsStateSubject.stream);
|
||||
|
||||
await dietsCubit!.close();
|
||||
|
||||
expect(likesStateSubject.isClosed, false); // Stream не закрывается, но подписка отменяется
|
||||
expect(dietsCubit!.isClosed, true);
|
||||
|
||||
await likesStateSubject.close();
|
||||
await favoriteStateSubject.close();
|
||||
await commentsStateSubject.close();
|
||||
});
|
||||
|
||||
test('likeDiet - обрабатывает ошибку', () async {
|
||||
dietsCubit = DietsCubit(
|
||||
feedRepository: mockFeedRepository,
|
||||
dietsRepository: mockDietRepository,
|
||||
);
|
||||
|
||||
const testDiet = Diet(
|
||||
id: 1,
|
||||
slug: 'test-diet',
|
||||
name: 'Test Diet',
|
||||
image: 'https://example.com/image.jpg',
|
||||
duration: 7,
|
||||
state: DietState.active,
|
||||
type: DietType(veg: true, light: false, carb: false, meat: false),
|
||||
details: 'Test details',
|
||||
dailySchedule: {},
|
||||
author: Author(authorSlug: 'author', authorName: 'Author Name', authorProfileIcon: 'https://example.com/icon.jpg', authorIsMe: false, isEntitySubscribedByMe: null, authorPlaceInRating: null),
|
||||
totalLikes: 10,
|
||||
totalViews: 100,
|
||||
totalComments: 5,
|
||||
wasLikedByMe: false,
|
||||
isFavorite: false,
|
||||
dailyNutrition: [],
|
||||
);
|
||||
|
||||
const dietsPagination = DietsPagination(
|
||||
data: [testDiet],
|
||||
links: Links(first: 'https://example.com?page=1', last: 'https://example.com?page=1', prev: null, next: null),
|
||||
meta: Meta(currentPage: 1, from: 1, lastPage: 1, links: [], perPage: 10, to: 1, total: 1),
|
||||
);
|
||||
|
||||
when(mockFeedRepository.getDiets(
|
||||
query: anyNamed('query'),
|
||||
types: anyNamed('types'),
|
||||
duration: anyNamed('duration'),
|
||||
sorting: anyNamed('sorting'),
|
||||
)).thenAnswer((_) async => dietsPagination);
|
||||
|
||||
when(mockDietRepository.likeDiet(1)).thenThrow(Exception('Ошибка'));
|
||||
|
||||
await dietsCubit!.loadDiets();
|
||||
await Future.delayed(const Duration(milliseconds: 200));
|
||||
|
||||
await dietsCubit!.likeDiet(1, 0);
|
||||
await Future.delayed(const Duration(milliseconds: 400));
|
||||
|
||||
// После ошибки состояние должно вернуться к исходному
|
||||
expect(dietsCubit!.state.dietsList[0].wasLikedByMe, false);
|
||||
expect(dietsCubit!.state.dietsList[0].totalLikes, 10);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@ import 'plans_cubit_test.mocks.dart';
|
||||
MockSpec<PlanRepository>(),
|
||||
])
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('PlansCubit', () {
|
||||
PlansCubit? plansCubit;
|
||||
late MockFeedRepository mockFeedRepository;
|
||||
@@ -578,6 +580,220 @@ void main() {
|
||||
expect(plansCubit!.state.plans[0].wasLikedByMe, false);
|
||||
expect(plansCubit!.state.plans[0].totalLikes, 9);
|
||||
});
|
||||
|
||||
test('changePriceFilter - с disableDebounce = true', () async {
|
||||
plansCubit = PlansCubit(
|
||||
feedRepository: mockFeedRepository,
|
||||
planRepository: mockPlanRepository,
|
||||
);
|
||||
|
||||
const testPlan = PlanModel(
|
||||
id: 1,
|
||||
slug: 'test-plan',
|
||||
name: 'Test Plan',
|
||||
image: 'https://example.com/image.jpg',
|
||||
duration: 7,
|
||||
state: 'active',
|
||||
type: PlanType(fit: true, slim: false, behavior: false, wellness: false),
|
||||
details: 'Test details',
|
||||
packetsCount: 3,
|
||||
minPriceOfOptions: 1000.0,
|
||||
maxPriceOfOptions: 2000.0,
|
||||
author: Author(authorSlug: 'author', authorName: 'Author Name', authorProfileIcon: 'https://example.com/icon.jpg', authorIsMe: false, isEntitySubscribedByMe: null, authorPlaceInRating: null),
|
||||
totalLikes: 10,
|
||||
totalViews: 100,
|
||||
totalComments: 5,
|
||||
wasLikedByMe: false,
|
||||
isFavorite: false,
|
||||
);
|
||||
|
||||
const plansPagination = PlanPaginationModel(
|
||||
data: [testPlan],
|
||||
links: Links(first: 'https://example.com?page=1', last: 'https://example.com?page=1', prev: null, next: null),
|
||||
meta: Meta(currentPage: 1, from: 1, lastPage: 1, links: [], perPage: 10, to: 1, total: 1, totalMinPrice: 1000, totalMaxPrice: 2000),
|
||||
);
|
||||
|
||||
when(mockFeedRepository.getPlans(
|
||||
query: anyNamed('query'),
|
||||
types: anyNamed('types'),
|
||||
duration: anyNamed('duration'),
|
||||
minPrice: anyNamed('minPrice'),
|
||||
maxPrice: anyNamed('maxPrice'),
|
||||
)).thenAnswer((_) async => plansPagination);
|
||||
|
||||
plansCubit!.changePriceFilter(100, 500, disableDebounce: true);
|
||||
await Future.delayed(const Duration(milliseconds: 200));
|
||||
|
||||
expect(plansCubit!.state.filterState.priceFilter, (100, 500));
|
||||
verify(mockFeedRepository.getPlans(
|
||||
query: anyNamed('query'),
|
||||
types: anyNamed('types'),
|
||||
duration: anyNamed('duration'),
|
||||
minPrice: anyNamed('minPrice'),
|
||||
maxPrice: anyNamed('maxPrice'),
|
||||
)).called(1);
|
||||
});
|
||||
|
||||
// Тесты для stream обновлений сложны для unit-тестирования, так как требуют пересоздания cubit
|
||||
// Эти методы тестируются через интеграционные тесты или через прямой вызов методов
|
||||
|
||||
// Тесты для обработки ошибок в addPlanToFavorite и removePlanFromFavorite
|
||||
// требуют Flutter binding для showErrorToast, поэтому пропускаем их в unit-тестах
|
||||
|
||||
test('likePlan - обрабатывает ошибку', () async {
|
||||
plansCubit = PlansCubit(
|
||||
feedRepository: mockFeedRepository,
|
||||
planRepository: mockPlanRepository,
|
||||
);
|
||||
|
||||
const testPlan = PlanModel(
|
||||
id: 1,
|
||||
slug: 'test-plan',
|
||||
name: 'Test Plan',
|
||||
image: 'https://example.com/image.jpg',
|
||||
duration: 7,
|
||||
state: 'active',
|
||||
type: PlanType(fit: true, slim: false, behavior: false, wellness: false),
|
||||
details: 'Test details',
|
||||
packetsCount: 3,
|
||||
minPriceOfOptions: 1000.0,
|
||||
maxPriceOfOptions: 2000.0,
|
||||
author: Author(authorSlug: 'author', authorName: 'Author Name', authorProfileIcon: 'https://example.com/icon.jpg', authorIsMe: false, isEntitySubscribedByMe: null, authorPlaceInRating: null),
|
||||
totalLikes: 10,
|
||||
totalViews: 100,
|
||||
totalComments: 5,
|
||||
wasLikedByMe: false,
|
||||
isFavorite: false,
|
||||
);
|
||||
|
||||
const plansPagination = PlanPaginationModel(
|
||||
data: [testPlan],
|
||||
links: Links(first: 'https://example.com?page=1', last: 'https://example.com?page=1', prev: null, next: null),
|
||||
meta: Meta(currentPage: 1, from: 1, lastPage: 1, links: [], perPage: 10, to: 1, total: 1, totalMinPrice: 1000, totalMaxPrice: 2000),
|
||||
);
|
||||
|
||||
when(mockFeedRepository.getPlans(
|
||||
query: anyNamed('query'),
|
||||
types: anyNamed('types'),
|
||||
duration: anyNamed('duration'),
|
||||
minPrice: anyNamed('minPrice'),
|
||||
maxPrice: anyNamed('maxPrice'),
|
||||
)).thenAnswer((_) async => plansPagination);
|
||||
|
||||
when(mockPlanRepository.likePlan('test-plan')).thenThrow(Exception('Ошибка'));
|
||||
|
||||
await plansCubit!.loadPlans();
|
||||
await Future.delayed(const Duration(milliseconds: 200));
|
||||
|
||||
await plansCubit!.likePlan('test-plan', 0);
|
||||
await Future.delayed(const Duration(milliseconds: 400));
|
||||
|
||||
// После ошибки состояние должно вернуться к исходному
|
||||
expect(plansCubit!.state.plans[0].wasLikedByMe, false);
|
||||
expect(plansCubit!.state.plans[0].totalLikes, 10);
|
||||
});
|
||||
|
||||
test('loadNextPage - не загружает если lastPage == currentPage', () async {
|
||||
plansCubit = PlansCubit(
|
||||
feedRepository: mockFeedRepository,
|
||||
planRepository: mockPlanRepository,
|
||||
);
|
||||
|
||||
const testPlan = PlanModel(
|
||||
id: 1,
|
||||
slug: 'test-plan',
|
||||
name: 'Test Plan',
|
||||
image: 'https://example.com/image.jpg',
|
||||
duration: 7,
|
||||
state: 'active',
|
||||
type: PlanType(fit: true, slim: false, behavior: false, wellness: false),
|
||||
details: 'Test details',
|
||||
packetsCount: 3,
|
||||
minPriceOfOptions: 1000.0,
|
||||
maxPriceOfOptions: 2000.0,
|
||||
author: Author(authorSlug: 'author', authorName: 'Author Name', authorProfileIcon: 'https://example.com/icon.jpg', authorIsMe: false, isEntitySubscribedByMe: null, authorPlaceInRating: null),
|
||||
totalLikes: 10,
|
||||
totalViews: 100,
|
||||
totalComments: 5,
|
||||
wasLikedByMe: false,
|
||||
isFavorite: false,
|
||||
);
|
||||
|
||||
const plansPagination = PlanPaginationModel(
|
||||
data: [testPlan],
|
||||
links: Links(first: 'https://example.com?page=1', last: 'https://example.com?page=1', prev: null, next: null),
|
||||
meta: Meta(currentPage: 1, from: 1, lastPage: 1, links: [], perPage: 10, to: 1, total: 1, totalMinPrice: 1000, totalMaxPrice: 2000),
|
||||
);
|
||||
|
||||
when(mockFeedRepository.getPlans(
|
||||
query: anyNamed('query'),
|
||||
types: anyNamed('types'),
|
||||
duration: anyNamed('duration'),
|
||||
minPrice: anyNamed('minPrice'),
|
||||
maxPrice: anyNamed('maxPrice'),
|
||||
)).thenAnswer((_) async => plansPagination);
|
||||
|
||||
await plansCubit!.loadPlans();
|
||||
await Future.delayed(const Duration(milliseconds: 200));
|
||||
|
||||
await plansCubit!.loadNextPage();
|
||||
await Future.delayed(const Duration(milliseconds: 200));
|
||||
|
||||
// Не должно вызывать getPlans для страницы 2
|
||||
verifyNever(mockFeedRepository.getPlans(page: 2, query: anyNamed('query')));
|
||||
});
|
||||
|
||||
test('loadPlans - загружает планы с query', () async {
|
||||
plansCubit = PlansCubit(
|
||||
feedRepository: mockFeedRepository,
|
||||
planRepository: mockPlanRepository,
|
||||
);
|
||||
|
||||
const testPlan = PlanModel(
|
||||
id: 1,
|
||||
slug: 'test-plan',
|
||||
name: 'Test Plan',
|
||||
image: 'https://example.com/image.jpg',
|
||||
duration: 7,
|
||||
state: 'active',
|
||||
type: PlanType(fit: true, slim: false, behavior: false, wellness: false),
|
||||
details: 'Test details',
|
||||
packetsCount: 3,
|
||||
minPriceOfOptions: 1000.0,
|
||||
maxPriceOfOptions: 2000.0,
|
||||
author: Author(authorSlug: 'author', authorName: 'Author Name', authorProfileIcon: 'https://example.com/icon.jpg', authorIsMe: false, isEntitySubscribedByMe: null, authorPlaceInRating: null),
|
||||
totalLikes: 10,
|
||||
totalViews: 100,
|
||||
totalComments: 5,
|
||||
wasLikedByMe: false,
|
||||
isFavorite: false,
|
||||
);
|
||||
|
||||
const plansPagination = PlanPaginationModel(
|
||||
data: [testPlan],
|
||||
links: Links(first: 'https://example.com?page=1', last: 'https://example.com?page=1', prev: null, next: null),
|
||||
meta: Meta(currentPage: 1, from: 1, lastPage: 1, links: [], perPage: 10, to: 1, total: 1, totalMinPrice: 1000, totalMaxPrice: 2000),
|
||||
);
|
||||
|
||||
when(mockFeedRepository.getPlans(
|
||||
query: anyNamed('query'),
|
||||
types: anyNamed('types'),
|
||||
duration: anyNamed('duration'),
|
||||
minPrice: anyNamed('minPrice'),
|
||||
maxPrice: anyNamed('maxPrice'),
|
||||
)).thenAnswer((_) async => plansPagination);
|
||||
|
||||
await plansCubit!.loadPlans(query: 'test');
|
||||
await Future.delayed(const Duration(milliseconds: 200));
|
||||
|
||||
verify(mockFeedRepository.getPlans(
|
||||
query: 'test',
|
||||
types: anyNamed('types'),
|
||||
duration: anyNamed('duration'),
|
||||
minPrice: anyNamed('minPrice'),
|
||||
maxPrice: anyNamed('maxPrice'),
|
||||
)).called(1);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -547,5 +547,207 @@ void main() {
|
||||
await Future.delayed(const Duration(milliseconds: 300));
|
||||
verify(mockAboutRecipeRepository.likeRecipe('test-recipe')).called(1);
|
||||
});
|
||||
|
||||
test('loadRecipesList - загружает рецепты с query', () async {
|
||||
recipesCubit = RecipesCubit(feedRepository: mockFeedRepository, recipeRepository: mockRecipeRepository, aboutRecipeRepository: mockAboutRecipeRepository);
|
||||
|
||||
const testRecipe = Recipe(
|
||||
id: 1,
|
||||
image: 'https://example.com/image.jpg',
|
||||
state: 'active',
|
||||
cookingTime: 30,
|
||||
activeCookingTime: 15,
|
||||
ingredients: [],
|
||||
description: 'Test description',
|
||||
steps: [],
|
||||
videoUrl: null,
|
||||
createdAt: null,
|
||||
updatedAt: null,
|
||||
title: 'Test Recipe',
|
||||
type: 'main',
|
||||
slug: 'test-recipe',
|
||||
commentsCount: 5,
|
||||
wasLikedByMe: false,
|
||||
isFavorite: false,
|
||||
calories: 300.0,
|
||||
servings: 4,
|
||||
likesCount: 10,
|
||||
viewsCount: 100,
|
||||
tags: [],
|
||||
author: Author(authorSlug: 'author', authorName: 'Author Name', authorProfileIcon: 'https://example.com/icon.jpg', authorIsMe: false, isEntitySubscribedByMe: null, authorPlaceInRating: null),
|
||||
);
|
||||
|
||||
final testTags = [RecipeTag(id: 1, name: 'Tag 1', recipesCount: 10, createdAt: DateTime(2024, 1, 1), updatedAt: DateTime(2024, 1, 2))];
|
||||
|
||||
when(mockFeedRepository.getRecipes(tags: anyNamed('tags'), query: anyNamed('query'))).thenAnswer((_) async => [testRecipe]);
|
||||
when(mockRecipeRepository.getRecipesTags()).thenAnswer((_) async => testTags);
|
||||
|
||||
await recipesCubit!.loadRecipesList(query: 'test');
|
||||
|
||||
await Future.delayed(const Duration(milliseconds: 200));
|
||||
verify(mockFeedRepository.getRecipes(tags: anyNamed('tags'), query: 'test')).called(1);
|
||||
});
|
||||
|
||||
test('loadRecipesList - загружает рецепты с выбранными тегами', () async {
|
||||
recipesCubit = RecipesCubit(feedRepository: mockFeedRepository, recipeRepository: mockRecipeRepository, aboutRecipeRepository: mockAboutRecipeRepository);
|
||||
|
||||
const testRecipe = Recipe(
|
||||
id: 1,
|
||||
image: 'https://example.com/image.jpg',
|
||||
state: 'active',
|
||||
cookingTime: 30,
|
||||
activeCookingTime: 15,
|
||||
ingredients: [],
|
||||
description: 'Test description',
|
||||
steps: [],
|
||||
videoUrl: null,
|
||||
createdAt: null,
|
||||
updatedAt: null,
|
||||
title: 'Test Recipe',
|
||||
type: 'main',
|
||||
slug: 'test-recipe',
|
||||
commentsCount: 5,
|
||||
wasLikedByMe: false,
|
||||
isFavorite: false,
|
||||
calories: 300.0,
|
||||
servings: 4,
|
||||
likesCount: 10,
|
||||
viewsCount: 100,
|
||||
tags: [],
|
||||
author: Author(authorSlug: 'author', authorName: 'Author Name', authorProfileIcon: 'https://example.com/icon.jpg', authorIsMe: false, isEntitySubscribedByMe: null, authorPlaceInRating: null),
|
||||
);
|
||||
|
||||
final testTags = [
|
||||
RecipeTag(id: 1, name: 'Tag 1', recipesCount: 10, createdAt: DateTime(2024, 1, 1), updatedAt: DateTime(2024, 1, 2)),
|
||||
RecipeTag(id: 2, name: 'Tag 2', recipesCount: 20, createdAt: DateTime(2024, 1, 1), updatedAt: DateTime(2024, 1, 2)),
|
||||
];
|
||||
|
||||
final tag1 = testTags[0];
|
||||
recipesCubit!.updateTags(tag1);
|
||||
|
||||
when(mockFeedRepository.getRecipes(tags: anyNamed('tags'), query: anyNamed('query'))).thenAnswer((_) async => [testRecipe]);
|
||||
when(mockRecipeRepository.getRecipesTags()).thenAnswer((_) async => testTags);
|
||||
|
||||
await recipesCubit!.loadRecipesList();
|
||||
|
||||
await Future.delayed(const Duration(milliseconds: 200));
|
||||
verify(mockFeedRepository.getRecipes(tags: [1], query: anyNamed('query'))).called(1);
|
||||
});
|
||||
|
||||
test('loadNextPage - не загружает если уже загружается', () async {
|
||||
recipesCubit = RecipesCubit(feedRepository: mockFeedRepository, recipeRepository: mockRecipeRepository, aboutRecipeRepository: mockAboutRecipeRepository);
|
||||
|
||||
const testRecipe = Recipe(
|
||||
id: 1,
|
||||
image: 'https://example.com/image.jpg',
|
||||
state: 'active',
|
||||
cookingTime: 30,
|
||||
activeCookingTime: 15,
|
||||
ingredients: [],
|
||||
description: 'Test description',
|
||||
steps: [],
|
||||
videoUrl: null,
|
||||
createdAt: null,
|
||||
updatedAt: null,
|
||||
title: 'Test Recipe',
|
||||
type: 'main',
|
||||
slug: 'test-recipe',
|
||||
commentsCount: 5,
|
||||
wasLikedByMe: false,
|
||||
isFavorite: false,
|
||||
calories: 300.0,
|
||||
servings: 4,
|
||||
likesCount: 10,
|
||||
viewsCount: 100,
|
||||
tags: [],
|
||||
author: Author(authorSlug: 'author', authorName: 'Author Name', authorProfileIcon: 'https://example.com/icon.jpg', authorIsMe: false, isEntitySubscribedByMe: null, authorPlaceInRating: null),
|
||||
);
|
||||
|
||||
final testTags = [RecipeTag(id: 1, name: 'Tag 1', recipesCount: 10, createdAt: DateTime(2024, 1, 1), updatedAt: DateTime(2024, 1, 2))];
|
||||
|
||||
when(mockFeedRepository.getRecipes(tags: anyNamed('tags'), query: anyNamed('query'))).thenAnswer((_) async => [testRecipe]);
|
||||
when(mockRecipeRepository.getRecipesTags()).thenAnswer((_) async => testTags);
|
||||
|
||||
await recipesCubit!.loadRecipesList();
|
||||
await Future.delayed(const Duration(milliseconds: 200));
|
||||
|
||||
recipesCubit!.lastPage = 2;
|
||||
when(mockFeedRepository.getRecipes(page: 2, tags: anyNamed('tags'), query: anyNamed('query'))).thenAnswer((_) async {
|
||||
await Future.delayed(const Duration(milliseconds: 100));
|
||||
return [testRecipe];
|
||||
});
|
||||
|
||||
final firstLoad = recipesCubit!.loadNextPage();
|
||||
await recipesCubit!.loadNextPage();
|
||||
await firstLoad;
|
||||
await Future.delayed(const Duration(milliseconds: 200));
|
||||
|
||||
verify(mockFeedRepository.getRecipes(page: 2, tags: anyNamed('tags'), query: anyNamed('query'))).called(1);
|
||||
});
|
||||
|
||||
test('likeRecipe - обрабатывает ошибку', () async {
|
||||
recipesCubit = RecipesCubit(feedRepository: mockFeedRepository, recipeRepository: mockRecipeRepository, aboutRecipeRepository: mockAboutRecipeRepository);
|
||||
|
||||
const testRecipe = Recipe(
|
||||
id: 1,
|
||||
image: 'https://example.com/image.jpg',
|
||||
state: 'active',
|
||||
cookingTime: 30,
|
||||
activeCookingTime: 15,
|
||||
ingredients: [],
|
||||
description: 'Test description',
|
||||
steps: [],
|
||||
videoUrl: null,
|
||||
createdAt: null,
|
||||
updatedAt: null,
|
||||
title: 'Test Recipe',
|
||||
type: 'main',
|
||||
slug: 'test-recipe',
|
||||
commentsCount: 5,
|
||||
wasLikedByMe: false,
|
||||
isFavorite: false,
|
||||
calories: 300.0,
|
||||
servings: 4,
|
||||
likesCount: 10,
|
||||
viewsCount: 100,
|
||||
tags: [],
|
||||
author: Author(authorSlug: 'author', authorName: 'Author Name', authorProfileIcon: 'https://example.com/icon.jpg', authorIsMe: false, isEntitySubscribedByMe: null, authorPlaceInRating: null),
|
||||
);
|
||||
|
||||
final testTags = [RecipeTag(id: 1, name: 'Tag 1', recipesCount: 10, createdAt: DateTime(2024, 1, 1), updatedAt: DateTime(2024, 1, 2))];
|
||||
|
||||
when(mockFeedRepository.getRecipes(tags: anyNamed('tags'), query: anyNamed('query'))).thenAnswer((_) async => [testRecipe]);
|
||||
when(mockRecipeRepository.getRecipesTags()).thenAnswer((_) async => testTags);
|
||||
when(mockAboutRecipeRepository.likeRecipe('test-recipe')).thenThrow(Exception('Ошибка'));
|
||||
|
||||
await recipesCubit!.loadRecipesList();
|
||||
await Future.delayed(const Duration(milliseconds: 200));
|
||||
|
||||
await recipesCubit!.likeRecipe('test-recipe', 0);
|
||||
await Future.delayed(const Duration(milliseconds: 400));
|
||||
|
||||
// После ошибки состояние должно вернуться к исходному
|
||||
expect(recipesCubit!.state.recipesList[0].wasLikedByMe, false);
|
||||
expect(recipesCubit!.state.recipesList[0].likesCount, 10);
|
||||
});
|
||||
|
||||
test('updateTags - работает с несколькими тегами', () {
|
||||
recipesCubit = RecipesCubit(feedRepository: mockFeedRepository, recipeRepository: mockRecipeRepository, aboutRecipeRepository: mockAboutRecipeRepository);
|
||||
|
||||
final tag1 = RecipeTag(id: 1, name: 'Tag 1', recipesCount: 10, createdAt: DateTime(2024, 1, 1), updatedAt: DateTime(2024, 1, 2));
|
||||
final tag2 = RecipeTag(id: 2, name: 'Tag 2', recipesCount: 20, createdAt: DateTime(2024, 1, 1), updatedAt: DateTime(2024, 1, 2));
|
||||
|
||||
recipesCubit!.updateTags(tag1);
|
||||
recipesCubit!.updateTags(tag2);
|
||||
|
||||
expect(recipesCubit!.state.selectedTags.contains(1), isTrue);
|
||||
expect(recipesCubit!.state.selectedTags.contains(2), isTrue);
|
||||
expect(recipesCubit!.state.selectedTagsCounter, 2);
|
||||
|
||||
recipesCubit!.updateTags(tag1);
|
||||
expect(recipesCubit!.state.selectedTags.contains(1), isFalse);
|
||||
expect(recipesCubit!.state.selectedTags.contains(2), isTrue);
|
||||
expect(recipesCubit!.state.selectedTagsCounter, 1);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user