feat(tests): updated tests coverage to 66.5%

This commit is contained in:
misha
2026-01-14 18:47:11 +03:00
parent 497ff19cfa
commit 13ccf56df0
102 changed files with 10146 additions and 379 deletions

View File

@@ -1,3 +1,4 @@
import 'package:easy_debounce/easy_debounce.dart';
import 'package:eatvio_app/core/models/pagination_model.dart';
import 'package:eatvio_app/features/diets/repositories/diet_repository.dart';
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
@@ -34,6 +35,9 @@ void main() {
});
tearDown(() async {
EasyDebounce.cancel('loadPlans');
EasyDebounce.cancel('like_plan');
EasyDebounce.cancel('add_or_remove_plan_from_favorite');
if (plansCubit != null && !plansCubit!.isClosed) {
await Future.delayed(const Duration(milliseconds: 100));
await plansCubit!.close();
@@ -153,6 +157,427 @@ void main() {
expect(plansCubit!.state.plans.first.id, 1);
expect(plansCubit!.state.errorMessage, isNull);
});
test('loadPlans - обработка ошибки', () async {
plansCubit = PlansCubit(
feedRepository: mockFeedRepository,
planRepository: mockPlanRepository,
);
when(mockFeedRepository.getPlans(
query: anyNamed('query'),
types: anyNamed('types'),
duration: anyNamed('duration'),
minPrice: anyNamed('minPrice'),
maxPrice: anyNamed('maxPrice'),
)).thenThrow(Exception('Ошибка загрузки'));
await plansCubit!.loadPlans();
await Future.delayed(const Duration(milliseconds: 200));
expect(plansCubit!.state.errorMessage, isNotNull);
});
test('loadNextPage - загружает следующую страницу', () async {
plansCubit = PlansCubit(
feedRepository: mockFeedRepository,
planRepository: mockPlanRepository,
);
const testPlan1 = PlanModel(
id: 1,
slug: 'test-plan-1',
name: 'Test Plan 1',
image: 'https://example.com/image1.jpg',
duration: 7,
state: 'active',
type: PlanType(fit: true, slim: false, behavior: false, wellness: false),
details: 'Test details 1',
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 testPlan2 = PlanModel(
id: 2,
slug: 'test-plan-2',
name: 'Test Plan 2',
image: 'https://example.com/image2.jpg',
duration: 14,
state: 'active',
type: PlanType(fit: false, slim: true, behavior: false, wellness: false),
details: 'Test details 2',
packetsCount: 5,
minPriceOfOptions: 2000.0,
maxPriceOfOptions: 3000.0,
author: Author(authorSlug: 'author', authorName: 'Author Name', authorProfileIcon: 'https://example.com/icon.jpg', authorIsMe: false, isEntitySubscribedByMe: null, authorPlaceInRating: null),
totalLikes: 20,
totalViews: 200,
totalComments: 10,
wasLikedByMe: false,
isFavorite: false,
);
const firstPagePagination = PlanPaginationModel(
data: [testPlan1],
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, totalMinPrice: 1000, totalMaxPrice: 3000),
);
const secondPagePagination = PlanPaginationModel(
data: [testPlan2],
links: Links(first: 'https://example.com?page=1', last: 'https://example.com?page=2', prev: 'https://example.com?page=1', next: null),
meta: Meta(currentPage: 2, from: 2, lastPage: 2, links: [], perPage: 10, to: 2, total: 2, totalMinPrice: 1000, totalMaxPrice: 3000),
);
when(mockFeedRepository.getPlans(
query: anyNamed('query'),
types: anyNamed('types'),
duration: anyNamed('duration'),
minPrice: anyNamed('minPrice'),
maxPrice: anyNamed('maxPrice'),
)).thenAnswer((_) async => firstPagePagination);
await plansCubit!.loadPlans();
await Future.delayed(const Duration(milliseconds: 200));
when(mockFeedRepository.getPlans(
page: 2,
query: anyNamed('query'),
)).thenAnswer((_) async => secondPagePagination);
await plansCubit!.loadNextPage();
await Future.delayed(const Duration(milliseconds: 300));
expect(plansCubit!.state.plans.length, 2);
expect(plansCubit!.state.plans[0].id, 1);
expect(plansCubit!.state.plans[1].id, 2);
});
test('loadNextPage - не загружает если уже загружается', () 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 firstPagePagination = PlanPaginationModel(
data: [testPlan],
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, totalMinPrice: 1000, totalMaxPrice: 2000),
);
when(mockFeedRepository.getPlans(
query: anyNamed('query'),
types: anyNamed('types'),
duration: anyNamed('duration'),
minPrice: anyNamed('minPrice'),
maxPrice: anyNamed('maxPrice'),
)).thenAnswer((_) async => firstPagePagination);
when(mockFeedRepository.getPlans(
page: 2,
query: anyNamed('query'),
)).thenAnswer((_) async {
await Future.delayed(const Duration(milliseconds: 100));
return firstPagePagination;
});
await plansCubit!.loadPlans();
await Future.delayed(const Duration(milliseconds: 200));
final firstLoad = plansCubit!.loadNextPage();
await plansCubit!.loadNextPage();
await firstLoad;
await Future.delayed(const Duration(milliseconds: 200));
verify(mockFeedRepository.getPlans(page: 2, query: anyNamed('query'))).called(1);
});
test('addPlanToFavorite - добавляет план в избранное', () 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.addToFavorite('test-plan')).thenAnswer((_) async => {});
await plansCubit!.loadPlans();
await Future.delayed(const Duration(milliseconds: 200));
await plansCubit!.addPlanToFavorite('test-plan', 0);
await Future.delayed(const Duration(milliseconds: 100));
expect(plansCubit!.state.plans[0].isFavorite, true);
});
test('removePlanFromFavorite - убирает план из избранного', () 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: true,
);
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.removeFromFavorite('test-plan')).thenAnswer((_) async => {});
await plansCubit!.loadPlans();
await Future.delayed(const Duration(milliseconds: 200));
await plansCubit!.removePlanFromFavorite('test-plan', 0);
await Future.delayed(const Duration(milliseconds: 100));
expect(plansCubit!.state.plans[0].isFavorite, false);
});
test('loadNextPage - обработка ошибки', () 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 firstPagePagination = PlanPaginationModel(
data: [testPlan],
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, totalMinPrice: 1000, totalMaxPrice: 2000),
);
when(mockFeedRepository.getPlans(
query: anyNamed('query'),
types: anyNamed('types'),
duration: anyNamed('duration'),
minPrice: anyNamed('minPrice'),
maxPrice: anyNamed('maxPrice'),
)).thenAnswer((_) async => firstPagePagination);
when(mockFeedRepository.getPlans(
page: 2,
query: anyNamed('query'),
)).thenThrow(Exception('Ошибка загрузки'));
await plansCubit!.loadPlans();
await Future.delayed(const Duration(milliseconds: 200));
await plansCubit!.loadNextPage();
await Future.delayed(const Duration(milliseconds: 200));
expect(plansCubit!.state.errorMessage, isNotNull);
});
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')).thenAnswer((_) async => {});
await plansCubit!.loadPlans();
await Future.delayed(const Duration(milliseconds: 200));
await plansCubit!.likePlan('test-plan', 0);
await Future.delayed(const Duration(milliseconds: 100));
expect(plansCubit!.state.plans[0].wasLikedByMe, true);
expect(plansCubit!.state.plans[0].totalLikes, 11);
});
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: true,
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')).thenAnswer((_) async => {});
await plansCubit!.loadPlans();
await Future.delayed(const Duration(milliseconds: 200));
await plansCubit!.likePlan('test-plan', 0);
await Future.delayed(const Duration(milliseconds: 100));
expect(plansCubit!.state.plans[0].wasLikedByMe, false);
expect(plansCubit!.state.plans[0].totalLikes, 9);
});
});
}

View File

@@ -59,6 +59,59 @@ void main() {
expect(state.plans.length, 1);
expect(state.isLoading, isFalse);
});
test('copyWith creates new instance with updated values', () {
const original = PlansState(
isLoading: true,
plans: [],
errorMessage: null,
filterState: PlansFilterState(
priceFilter: (null, null),
durationIndex: 0,
typeIndex: 0,
),
minPlanPrice: 0,
maxPlanPrice: 0,
);
final updated = original.copyWith(
isLoading: false,
errorMessage: 'Test error',
);
expect(updated.isLoading, isFalse);
expect(updated.errorMessage, 'Test error');
expect(updated.plans, original.plans);
expect(updated.filterState, original.filterState);
});
});
group('PlansFilterState', () {
test('creates PlansFilterState with values', () {
const state = PlansFilterState(
priceFilter: (1000, 2000),
durationIndex: 1,
typeIndex: 2,
);
expect(state.priceFilter.$1, 1000);
expect(state.priceFilter.$2, 2000);
expect(state.durationIndex, 1);
expect(state.typeIndex, 2);
});
test('copyWith creates new instance with updated values', () {
const original = PlansFilterState(
priceFilter: (null, null),
durationIndex: 0,
typeIndex: 0,
);
final updated = original.copyWith(durationIndex: 1);
expect(updated.durationIndex, 1);
expect(updated.typeIndex, original.typeIndex);
});
});
}