feat(dependencies): add rxdart package and improve string extension methods for better handling of empty strings and gender parsing

This commit is contained in:
mso7nueh
2025-12-04 15:45:54 +03:00
parent 0cd8e662d9
commit 953230b387
100 changed files with 7202 additions and 14 deletions

View File

@@ -0,0 +1,54 @@
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
import 'package:eatvio_app/features/feed/models/plan_pagination_model.dart';
import 'package:eatvio_app/features/plans/enums/chosen_plan_state_enum.dart';
import 'package:eatvio_app/features/plans/models/chosen_plan_model.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
group('ChosenPlanModel', () {
test('создает ChosenPlanModel с правильными значениями', () {
const plan = 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,
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,
isFavorite: false,
minPriceOfOptions: 1000,
maxPriceOfOptions: 2000,
wasLikedByMe: false,
);
final chosenPlan = ChosenPlanModel(
id: 1,
optionType: 'options1',
startDate: DateTime(2024, 1, 1),
chosenPlanState: ChosenPlanStateEnum.stateInited,
chatId: 'chat-1',
consultantId: 1,
plan: plan,
);
expect(chosenPlan.id, 1);
expect(chosenPlan.optionType, 'options1');
expect(chosenPlan.chosenPlanState, ChosenPlanStateEnum.stateInited);
expect(chosenPlan.plan.id, 1);
});
});
}

View File

@@ -0,0 +1,168 @@
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
import 'package:eatvio_app/features/feed/models/plan_pagination_model.dart';
import 'package:eatvio_app/features/plans/presentation/cubit/about_plan_cubit.dart';
import 'package:eatvio_app/features/plans/presentation/cubit/about_plan_state.dart';
import 'package:eatvio_app/features/plans/repositories/plan_repository.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'about_plan_cubit_test.mocks.dart';
@GenerateNiceMocks([MockSpec<PlanRepository>()])
void main() {
group('AboutPlanCubit', () {
AboutPlanCubit? aboutPlanCubit;
late MockPlanRepository mockPlanRepository;
setUp(() {
mockPlanRepository = MockPlanRepository();
});
tearDown(() async {
if (aboutPlanCubit != null && !aboutPlanCubit!.isClosed) {
await aboutPlanCubit!.close();
aboutPlanCubit = null;
}
});
test('начальное состояние - initial', () {
aboutPlanCubit = AboutPlanCubit(planRepository: mockPlanRepository);
expect(aboutPlanCubit!.state, isA<AboutPlanState>());
expect(aboutPlanCubit!.state.isLoading, isFalse);
expect(aboutPlanCubit!.state.planModel, isNull);
});
test('getPlan - успешная загрузка плана', () async {
aboutPlanCubit = AboutPlanCubit(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 description',
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,
);
when(mockPlanRepository.getPlan('test-plan'))
.thenAnswer((_) async => testPlan);
await aboutPlanCubit!.getPlan('test-plan');
expect(aboutPlanCubit!.state.planModel, isNotNull);
expect(aboutPlanCubit!.state.planModel!.id, 1);
expect(aboutPlanCubit!.state.error, isNull);
});
test('getPlanWithLoading - устанавливает loading состояние', () async {
aboutPlanCubit = AboutPlanCubit(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 description',
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,
);
when(mockPlanRepository.getPlan('test-plan'))
.thenAnswer((_) async => testPlan);
await aboutPlanCubit!.getPlanWithLoading('test-plan');
expect(aboutPlanCubit!.state.planModel, isNotNull);
expect(aboutPlanCubit!.state.isLoading, isFalse);
});
test('choseOption - изменяет выбранную опцию', () {
aboutPlanCubit = AboutPlanCubit(planRepository: mockPlanRepository);
aboutPlanCubit!.choseOption(2);
expect(aboutPlanCubit!.state.chosenOptionIndex, 2);
});
test('likePlan - лайк плана', () async {
aboutPlanCubit = AboutPlanCubit(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 description',
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,
);
when(mockPlanRepository.getPlan('test-plan'))
.thenAnswer((_) async => testPlan);
when(mockPlanRepository.likePlan('test-plan'))
.thenAnswer((_) async => {});
await aboutPlanCubit!.getPlan('test-plan');
await aboutPlanCubit!.likePlan(index: null, slug: 'test-plan');
await Future.delayed(const Duration(milliseconds: 1200));
expect(aboutPlanCubit!.state.planModel!.wasLikedByMe, isTrue);
expect(aboutPlanCubit!.state.planModel!.totalLikes, 11);
});
});
}

View File

@@ -0,0 +1,57 @@
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
import 'package:eatvio_app/features/feed/models/plan_pagination_model.dart';
import 'package:eatvio_app/features/plans/presentation/cubit/about_plan_state.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
group('AboutPlanState', () {
test('initial создает начальное состояние', () {
final state = AboutPlanState.initial();
expect(state.isLoading, isFalse);
expect(state.error, isNull);
expect(state.planModel, isNull);
expect(state.chosenOptionIndex, 0);
});
test('создает AboutPlanState с планом', () {
const plan = 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,
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,
isFavorite: false,
minPriceOfOptions: 1000.0,
maxPriceOfOptions: 2000.0,
wasLikedByMe: false,
);
const state = AboutPlanState(
isLoading: false,
error: null,
planModel: plan,
chosenOptionIndex: 1,
);
expect(state.planModel, plan);
expect(state.chosenOptionIndex, 1);
});
});
}

View File

@@ -0,0 +1,176 @@
import 'package:eatvio_app/features/diets/repositories/diet_repository.dart';
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
import 'package:eatvio_app/features/feed/models/plan_pagination_model.dart';
import 'package:eatvio_app/features/plans/enums/chosen_plan_state_enum.dart';
import 'package:eatvio_app/features/plans/models/chosen_plan_model.dart';
import 'package:eatvio_app/features/plans/repositories/plan_repository.dart';
import 'package:eatvio_app/features/plans/services/plan_service.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'plan_repository_test.mocks.dart';
@GenerateNiceMocks([MockSpec<PlanService>()])
void main() {
group('PlanRepository', () {
late PlanRepository repository;
late MockPlanService mockService;
setUp(() {
mockService = MockPlanService();
repository = PlanRepository(planService: mockService);
});
test('getPlan - успешное получение плана', () async {
const slug = 'test-plan';
const expectedPlan = PlanModel(
id: 1,
slug: slug,
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,
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,
isFavorite: false,
minPriceOfOptions: 1000.0,
maxPriceOfOptions: 2000.0,
wasLikedByMe: false,
);
when(mockService.getPlan(slug))
.thenAnswer((_) async => expectedPlan);
final result = await repository.getPlan(slug);
expect(result, equals(expectedPlan));
expect(result.id, 1);
verify(mockService.getPlan(slug)).called(1);
});
test('likePlan - успешный лайк', () async {
const slug = 'test-plan';
when(mockService.likePlan(slug: slug))
.thenAnswer((_) async => Future.value());
await repository.likePlan(slug);
verify(mockService.likePlan(slug: slug)).called(1);
});
test('addToFavorite - успешное добавление в избранное', () async {
const slug = 'test-plan';
when(mockService.addToFavorite(slug: slug))
.thenAnswer((_) async => Future.value());
await repository.addToFavorite(slug);
verify(mockService.addToFavorite(slug: slug)).called(1);
});
test('removeFromFavorite - успешное удаление из избранного', () async {
const slug = 'test-plan';
when(mockService.removeFromFavorite(slug: slug))
.thenAnswer((_) async => Future.value());
await repository.removeFromFavorite(slug);
verify(mockService.removeFromFavorite(slug: slug)).called(1);
});
test('chosePlan - успешный выбор плана', () async {
const slug = 'test-plan';
final option = {'option_type': 'options1'};
final expectedChosenPlan = ChosenPlanModel(
id: 1,
optionType: 'options1',
startDate: DateTime(2024, 1, 1),
chosenPlanState: ChosenPlanStateEnum.stateInited,
chatId: 'chat-1',
consultantId: 1,
plan: const PlanModel(
id: 1,
slug: slug,
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,
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,
isFavorite: false,
minPriceOfOptions: 1000.0,
maxPriceOfOptions: 2000.0,
wasLikedByMe: false,
),
);
when(mockService.chosePlan(slug: slug, option: option))
.thenAnswer((_) async => expectedChosenPlan);
final result = await repository.chosePlan(slug, option);
expect(result, equals(expectedChosenPlan));
verify(mockService.chosePlan(slug: slug, option: option)).called(1);
});
test('emitLikesStateToFeed - отправляет состояние лайков', () async {
const state = LikesState(
wasLikedByMe: true,
totalLikes: 10,
index: 0,
);
expectLater(repository.planLikeState, emits(state));
repository.emitLikesStateToFeed(state);
});
test('emitFavoriteStateToFeed - отправляет состояние избранного', () async {
const state = FavoriteState(
isFavorite: true,
index: 0,
);
expectLater(repository.planFavoriteState, emits(state));
repository.emitFavoriteStateToFeed(state);
});
test('emitCommentsStateToFeed - отправляет состояние комментариев', () async {
const state = CommentsState(
index: 0,
totalComments: 5,
);
expectLater(repository.planCommentsState, emits(state));
repository.emitCommentsStateToFeed(state);
});
});
}

View File

@@ -0,0 +1,67 @@
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/plan_pagination_model.dart';
import 'package:eatvio_app/features/plans/repositories/plans_repository.dart';
import 'package:eatvio_app/features/plans/services/plans_service.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'plans_repository_test.mocks.dart';
@GenerateNiceMocks([MockSpec<PlansService>()])
void main() {
group('PlansRepository', () {
late PlansRepository repository;
late MockPlansService mockService;
setUp(() {
mockService = MockPlansService();
repository = PlansRepository(plansService: mockService);
});
test('getPlansBySlug - успешное получение планов', () async {
const slug = 'test-author';
const plan = 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,
author: Author(
authorSlug: 'test-author',
authorName: 'Author Name',
authorProfileIcon: 'https://example.com/icon.jpg',
authorIsMe: false,
isEntitySubscribedByMe: null,
authorPlaceInRating: null,
),
totalLikes: 10,
totalViews: 100,
totalComments: 5,
isFavorite: false,
minPriceOfOptions: 1000.0,
maxPriceOfOptions: 2000.0,
wasLikedByMe: false,
);
const pagination = PlanPaginationModel(
data: [plan],
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(mockService.getPlansBySlug(author: slug)).thenAnswer((_) async => pagination);
final result = await repository.getPlansBySlug(slug);
expect(result.length, 1);
expect(result.first.slug, 'test-plan');
verify(mockService.getPlansBySlug(author: slug)).called(1);
});
});
}