feat(tests): increased tests coverage

This commit is contained in:
misha
2025-12-25 12:56:38 +03:00
parent 5b705dfb72
commit 99659f7ea1
132 changed files with 10304 additions and 6226 deletions

View File

@@ -0,0 +1,381 @@
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/theme/theme_helper.dart';
import 'package:eatvio_app/core/widgets/custom_text_form_field.dart';
import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_cubit.dart';
import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_state.dart';
import 'package:eatvio_app/features/create_diet/presentation/widgets/add_meal_widget.dart';
import 'package:eatvio_app/features/diary/presentation/bloc/diary_cubit.dart';
import 'package:eatvio_app/features/diary/presentation/bloc/diary_state.dart';
import 'package:eatvio_app/features/glossary/models/glossary_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'add_meal_widget_test.mocks.dart';
@GenerateNiceMocks([MockSpec<CreateDietCubit>(), MockSpec<DiaryCubit>()])
void main() {
group('AddMealWidget smoke', () {
setUpAll(() async {
dotenv.loadFromString(envString: 'DEV_BASE_URL=http://localhost\nBASE_URL=http://localhost');
});
setUp(() async {
await sl.reset();
sl.registerLazySingleton<AppConfig>(() => AppConfig(buildType: AppBuildType.tests)..setBaseUrl('http://localhost'));
});
Widget wrap(Widget child) {
return MaterialApp(
theme: ThemeHelper().themeData(),
home: Scaffold(body: child),
);
}
testWidgets('renders add meal widget', (tester) async {
final createDietCubit = MockCreateDietCubit();
final diaryCubit = MockDiaryCubit();
when(createDietCubit.state).thenReturn(CreateDietState.initial());
when(createDietCubit.stream).thenAnswer((_) => const Stream.empty());
when(createDietCubit.close()).thenAnswer((_) async => Future<void>.value());
when(createDietCubit.getGlossaryListByQuery(any)).thenAnswer((_) async => []);
when(diaryCubit.state).thenReturn(const DiaryState());
when(diaryCubit.stream).thenAnswer((_) => const Stream.empty());
when(diaryCubit.close()).thenAnswer((_) async => Future<void>.value());
await tester.pumpWidget(
MultiBlocProvider(
providers: [
BlocProvider<CreateDietCubit>.value(value: createDietCubit),
BlocProvider<DiaryCubit>.value(value: diaryCubit),
],
child: wrap(AddMealWidget(createDietCubit: createDietCubit, dayIndex: 0, mealIndex: 0)),
),
);
await tester.pumpAndSettle();
expect(find.byType(AddMealWidget), findsOneWidget);
expect(find.text('Добавление блюда'), findsOneWidget);
});
testWidgets('renders add meal widget with initial text', (tester) async {
final createDietCubit = MockCreateDietCubit();
final diaryCubit = MockDiaryCubit();
when(createDietCubit.state).thenReturn(CreateDietState.initial());
when(createDietCubit.stream).thenAnswer((_) => const Stream.empty());
when(createDietCubit.close()).thenAnswer((_) async => Future<void>.value());
when(createDietCubit.getGlossaryListByQuery(any)).thenAnswer((_) async => []);
when(diaryCubit.state).thenReturn(const DiaryState());
when(diaryCubit.stream).thenAnswer((_) => const Stream.empty());
when(diaryCubit.close()).thenAnswer((_) async => Future<void>.value());
await tester.pumpWidget(
MultiBlocProvider(
providers: [
BlocProvider<CreateDietCubit>.value(value: createDietCubit),
BlocProvider<DiaryCubit>.value(value: diaryCubit),
],
child: wrap(
AddMealWidget(
createDietCubit: createDietCubit,
dayIndex: 0,
mealIndex: 0,
productNameControllerText: 'Test Product',
quantityControllerText: '100',
),
),
),
);
await tester.pumpAndSettle();
expect(find.byType(AddMealWidget), findsOneWidget);
});
testWidgets('renders edit meal widget with portion index', (tester) async {
final createDietCubit = MockCreateDietCubit();
final diaryCubit = MockDiaryCubit();
when(createDietCubit.state).thenReturn(CreateDietState.initial());
when(createDietCubit.stream).thenAnswer((_) => const Stream.empty());
when(createDietCubit.close()).thenAnswer((_) async => Future<void>.value());
when(createDietCubit.getGlossaryListByQuery(any)).thenAnswer((_) async => []);
when(createDietCubit.removePortion(day: anyNamed('day'), meal: anyNamed('meal'), portionIndex: anyNamed('portionIndex'))).thenReturn(null);
when(diaryCubit.state).thenReturn(const DiaryState());
when(diaryCubit.stream).thenAnswer((_) => const Stream.empty());
when(diaryCubit.close()).thenAnswer((_) async => Future<void>.value());
await tester.pumpWidget(
MultiBlocProvider(
providers: [
BlocProvider<CreateDietCubit>.value(value: createDietCubit),
BlocProvider<DiaryCubit>.value(value: diaryCubit),
],
child: wrap(
AddMealWidget(
createDietCubit: createDietCubit,
dayIndex: 0,
mealIndex: 0,
portionIndex: 0,
productNameControllerText: 'Test Product',
quantityControllerText: '100',
),
),
),
);
await tester.pumpAndSettle();
expect(find.byType(AddMealWidget), findsOneWidget);
expect(find.text('Сохранить'), findsOneWidget);
expect(find.byIcon(Icons.delete_outline_rounded), findsOneWidget);
});
testWidgets('searches glossary items when typing in product name field', (tester) async {
final createDietCubit = MockCreateDietCubit();
final diaryCubit = MockDiaryCubit();
final testGlossaries = [
const GlossaryModel(
id: 1,
name: 'Test Product 1',
grInPortion: 100,
energyOn100Gr: 100.0,
carbohydratesOn100Gr: 20.0,
proteinsOn100Gr: 10.0,
fatsOn100Gr: 5.0,
energyOnPortion: 100.0,
carbohydratesOnPortion: 20.0,
proteinsOnPortion: 10.0,
fatsOnPortion: 5.0,
),
const GlossaryModel(
id: 2,
name: 'Test Product 2',
grInPortion: 0,
energyOn100Gr: 200.0,
carbohydratesOn100Gr: 30.0,
proteinsOn100Gr: 15.0,
fatsOn100Gr: 10.0,
energyOnPortion: 0.0,
carbohydratesOnPortion: 0.0,
proteinsOnPortion: 0.0,
fatsOnPortion: 0.0,
),
];
when(createDietCubit.state).thenReturn(CreateDietState.initial());
when(createDietCubit.stream).thenAnswer((_) => const Stream.empty());
when(createDietCubit.close()).thenAnswer((_) async => Future<void>.value());
when(createDietCubit.getGlossaryListByQuery('test')).thenAnswer((_) async => testGlossaries);
when(createDietCubit.addPortion(dayIndex: anyNamed('dayIndex'), mealIndex: anyNamed('mealIndex'), portion: anyNamed('portion'))).thenReturn(null);
when(diaryCubit.state).thenReturn(const DiaryState());
when(diaryCubit.stream).thenAnswer((_) => const Stream.empty());
when(diaryCubit.close()).thenAnswer((_) async => Future<void>.value());
await tester.pumpWidget(
MultiBlocProvider(
providers: [
BlocProvider<CreateDietCubit>.value(value: createDietCubit),
BlocProvider<DiaryCubit>.value(value: diaryCubit),
],
child: wrap(AddMealWidget(createDietCubit: createDietCubit, dayIndex: 0, mealIndex: 0)),
),
);
await tester.pumpAndSettle();
final productNameField = find.byType(CustomTextFormField).first;
await tester.enterText(productNameField, 'test');
await tester.pump(const Duration(milliseconds: 300));
await tester.pumpAndSettle();
expect(find.text('Test Product 1'), findsOneWidget);
expect(find.text('Test Product 2'), findsOneWidget);
});
testWidgets('selects glossary item from list', (tester) async {
final createDietCubit = MockCreateDietCubit();
final diaryCubit = MockDiaryCubit();
final testGlossaries = [
const GlossaryModel(
id: 1,
name: 'Test Product',
grInPortion: 100,
energyOn100Gr: 100.0,
carbohydratesOn100Gr: 20.0,
proteinsOn100Gr: 10.0,
fatsOn100Gr: 5.0,
energyOnPortion: 100.0,
carbohydratesOnPortion: 20.0,
proteinsOnPortion: 10.0,
fatsOnPortion: 5.0,
),
];
when(createDietCubit.state).thenReturn(CreateDietState.initial());
when(createDietCubit.stream).thenAnswer((_) => const Stream.empty());
when(createDietCubit.close()).thenAnswer((_) async => Future<void>.value());
when(createDietCubit.getGlossaryListByQuery('test')).thenAnswer((_) async => testGlossaries);
when(createDietCubit.addPortion(dayIndex: anyNamed('dayIndex'), mealIndex: anyNamed('mealIndex'), portion: anyNamed('portion'))).thenReturn(null);
when(diaryCubit.state).thenReturn(const DiaryState());
when(diaryCubit.stream).thenAnswer((_) => const Stream.empty());
when(diaryCubit.close()).thenAnswer((_) async => Future<void>.value());
await tester.pumpWidget(
MultiBlocProvider(
providers: [
BlocProvider<CreateDietCubit>.value(value: createDietCubit),
BlocProvider<DiaryCubit>.value(value: diaryCubit),
],
child: wrap(AddMealWidget(createDietCubit: createDietCubit, dayIndex: 0, mealIndex: 0)),
),
);
await tester.pumpAndSettle();
final productNameField = find.byType(CustomTextFormField).first;
await tester.enterText(productNameField, 'test');
await tester.pump(const Duration(milliseconds: 300));
await tester.pumpAndSettle();
await tester.tap(find.text('Test Product'));
await tester.pumpAndSettle();
expect(find.text('Test Product'), findsOneWidget);
});
testWidgets('displays correct suffix text for grams when glossary has grInPortion 0', (tester) async {
final createDietCubit = MockCreateDietCubit();
final diaryCubit = MockDiaryCubit();
final testGlossaries = [
const GlossaryModel(
id: 1,
name: 'Test Product',
grInPortion: 0,
energyOn100Gr: 100.0,
carbohydratesOn100Gr: 20.0,
proteinsOn100Gr: 10.0,
fatsOn100Gr: 5.0,
energyOnPortion: 0.0,
carbohydratesOnPortion: 0.0,
proteinsOnPortion: 0.0,
fatsOnPortion: 0.0,
),
];
when(createDietCubit.state).thenReturn(CreateDietState.initial());
when(createDietCubit.stream).thenAnswer((_) => const Stream.empty());
when(createDietCubit.close()).thenAnswer((_) async => Future<void>.value());
when(createDietCubit.getGlossaryListByQuery('test')).thenAnswer((_) async => testGlossaries);
when(createDietCubit.addPortion(dayIndex: anyNamed('dayIndex'), mealIndex: anyNamed('mealIndex'), portion: anyNamed('portion'))).thenReturn(null);
when(diaryCubit.state).thenReturn(const DiaryState());
when(diaryCubit.stream).thenAnswer((_) => const Stream.empty());
when(diaryCubit.close()).thenAnswer((_) async => Future<void>.value());
await tester.pumpWidget(
MultiBlocProvider(
providers: [
BlocProvider<CreateDietCubit>.value(value: createDietCubit),
BlocProvider<DiaryCubit>.value(value: diaryCubit),
],
child: wrap(AddMealWidget(createDietCubit: createDietCubit, dayIndex: 0, mealIndex: 0)),
),
);
await tester.pumpAndSettle();
final productNameField = find.byType(CustomTextFormField).first;
await tester.enterText(productNameField, 'test');
await tester.pump(const Duration(milliseconds: 300));
await tester.pumpAndSettle();
await tester.tap(find.text('Test Product'));
await tester.pumpAndSettle();
expect(find.text('гр'), findsOneWidget);
});
testWidgets('displays correct suffix text for pieces when glossary has grInPortion not 0', (tester) async {
final createDietCubit = MockCreateDietCubit();
final diaryCubit = MockDiaryCubit();
final testGlossaries = [
const GlossaryModel(
id: 1,
name: 'Test Product',
grInPortion: 100,
energyOn100Gr: 100.0,
carbohydratesOn100Gr: 20.0,
proteinsOn100Gr: 10.0,
fatsOn100Gr: 5.0,
energyOnPortion: 100.0,
carbohydratesOnPortion: 20.0,
proteinsOnPortion: 10.0,
fatsOnPortion: 5.0,
),
];
when(createDietCubit.state).thenReturn(CreateDietState.initial());
when(createDietCubit.stream).thenAnswer((_) => const Stream.empty());
when(createDietCubit.close()).thenAnswer((_) async => Future<void>.value());
when(createDietCubit.getGlossaryListByQuery('test')).thenAnswer((_) async => testGlossaries);
when(createDietCubit.addPortion(dayIndex: anyNamed('dayIndex'), mealIndex: anyNamed('mealIndex'), portion: anyNamed('portion'))).thenReturn(null);
when(diaryCubit.state).thenReturn(const DiaryState());
when(diaryCubit.stream).thenAnswer((_) => const Stream.empty());
when(diaryCubit.close()).thenAnswer((_) async => Future<void>.value());
await tester.pumpWidget(
MultiBlocProvider(
providers: [
BlocProvider<CreateDietCubit>.value(value: createDietCubit),
BlocProvider<DiaryCubit>.value(value: diaryCubit),
],
child: wrap(AddMealWidget(createDietCubit: createDietCubit, dayIndex: 0, mealIndex: 0)),
),
);
await tester.pumpAndSettle();
final productNameField = find.byType(CustomTextFormField).first;
await tester.enterText(productNameField, 'test');
await tester.pump(const Duration(milliseconds: 300));
await tester.pumpAndSettle();
await tester.tap(find.text('Test Product'));
await tester.pumpAndSettle();
expect(find.text('шт'), findsOneWidget);
});
testWidgets('calls removePortion when delete button is tapped', (tester) async {
final createDietCubit = MockCreateDietCubit();
final diaryCubit = MockDiaryCubit();
when(createDietCubit.state).thenReturn(CreateDietState.initial());
when(createDietCubit.stream).thenAnswer((_) => const Stream.empty());
when(createDietCubit.close()).thenAnswer((_) async => Future<void>.value());
when(createDietCubit.getGlossaryListByQuery(any)).thenAnswer((_) async => []);
when(createDietCubit.removePortion(day: 0, meal: 0, portionIndex: 0)).thenReturn(null);
when(diaryCubit.state).thenReturn(const DiaryState());
when(diaryCubit.stream).thenAnswer((_) => const Stream.empty());
when(diaryCubit.close()).thenAnswer((_) async => Future<void>.value());
await tester.pumpWidget(
MultiBlocProvider(
providers: [
BlocProvider<CreateDietCubit>.value(value: createDietCubit),
BlocProvider<DiaryCubit>.value(value: diaryCubit),
],
child: wrap(AddMealWidget(createDietCubit: createDietCubit, dayIndex: 0, mealIndex: 0, portionIndex: 0)),
),
);
await tester.pumpAndSettle();
final deleteButton = find.byIcon(Icons.delete_outline_rounded);
await tester.tap(deleteButton);
await tester.pumpAndSettle();
verify(createDietCubit.removePortion(day: 0, meal: 0, portionIndex: 0)).called(1);
});
});
}

View File

@@ -0,0 +1,221 @@
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/theme/theme_helper.dart';
import 'package:eatvio_app/core/widgets/cpfc_container.dart';
import 'package:eatvio_app/core/widgets/custom_outlined_button.dart';
import 'package:eatvio_app/features/create_diet/models/create_diet_model.dart';
import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_cubit.dart';
import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_state.dart';
import 'package:eatvio_app/features/create_diet/presentation/widgets/day_widget.dart';
import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
import 'package:eatvio_app/features/feed/presentation/widgets/choice_chip_widget.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'day_widget_test.mocks.dart';
@GenerateNiceMocks([MockSpec<CreateDietCubit>()])
void main() {
group('DayWidget smoke', () {
setUpAll(() async {
dotenv.loadFromString(envString: 'DEV_BASE_URL=http://localhost\nBASE_URL=http://localhost');
});
setUp(() async {
await sl.reset();
sl.registerLazySingleton<AppConfig>(() => AppConfig(buildType: AppBuildType.tests)..setBaseUrl('http://localhost'));
});
Widget wrap(Widget child) {
return MaterialApp(
theme: ThemeHelper().themeData(),
localizationsDelegates: const [
S.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: const [Locale('ru')],
home: Scaffold(body: child),
);
}
testWidgets('renders day widget', (tester) async {
final cubit = MockCreateDietCubit();
final state = CreateDietState.initial();
when(cubit.state).thenReturn(state);
when(cubit.stream).thenAnswer((_) => Stream.value(state));
when(cubit.close()).thenAnswer((_) async => Future<void>.value());
when(cubit.copyDay(any)).thenReturn(null);
await tester.pumpWidget(BlocProvider<CreateDietCubit>.value(value: cubit, child: wrap(const DayWidget(index: 0))));
await tester.pumpAndSettle();
expect(find.byType(DayWidget), findsOneWidget);
});
testWidgets('renders day widget with error', (tester) async {
final cubit = MockCreateDietCubit();
final state = CreateDietState.initial().copyWith(dailyScheduleErrors: List.filled(21, ''));
final stateWithError = state.copyWith(dailyScheduleErrors: List.generate(21, (index) => index == 0 ? 'Error message' : ''));
when(cubit.state).thenReturn(stateWithError);
when(cubit.stream).thenAnswer((_) => Stream.value(stateWithError));
when(cubit.close()).thenAnswer((_) async => Future<void>.value());
when(cubit.copyDay(any)).thenReturn(null);
await tester.pumpWidget(BlocProvider<CreateDietCubit>.value(value: cubit, child: wrap(const DayWidget(index: 0))));
await tester.pumpAndSettle();
expect(find.byType(DayWidget), findsOneWidget);
});
testWidgets('taps copy day button', (tester) async {
final cubit = MockCreateDietCubit();
final state = CreateDietState.initial();
when(cubit.state).thenReturn(state);
when(cubit.stream).thenAnswer((_) => Stream.value(state));
when(cubit.close()).thenAnswer((_) async => Future<void>.value());
when(cubit.copyDay(0)).thenReturn(null);
await tester.pumpWidget(BlocProvider<CreateDietCubit>.value(value: cubit, child: wrap(const DayWidget(index: 0))));
await tester.pumpAndSettle();
final copyGesture = find.byType(GestureDetector).first;
await tester.tap(copyGesture);
await tester.pumpAndSettle();
verify(cubit.copyDay(0)).called(1);
});
testWidgets('renders paste button', (tester) async {
final cubit = MockCreateDietCubit();
final state = CreateDietState.initial();
when(cubit.state).thenReturn(state);
when(cubit.stream).thenAnswer((_) => Stream.value(state));
when(cubit.close()).thenAnswer((_) async => Future<void>.value());
await tester.pumpWidget(BlocProvider<CreateDietCubit>.value(value: cubit, child: wrap(const DayWidget(index: 0))));
await tester.pumpAndSettle();
// Проверяем, что виджет отрендерился (paste button находится внутри)
expect(find.byType(DayWidget), findsOneWidget);
});
testWidgets('renders add meal button', (tester) async {
final cubit = MockCreateDietCubit();
final state = CreateDietState.initial();
when(cubit.state).thenReturn(state);
when(cubit.stream).thenAnswer((_) => Stream.value(state));
when(cubit.close()).thenAnswer((_) async => Future<void>.value());
await tester.pumpWidget(BlocProvider<CreateDietCubit>.value(value: cubit, child: wrap(const DayWidget(index: 0))));
await tester.pumpAndSettle();
// Проверяем, что кнопка "Добавить" присутствует
final addButtons = find.byType(CustomOutlinedButton);
expect(addButtons, findsAtLeastNWidgets(1));
});
testWidgets('selects different meal chips', (tester) async {
final cubit = MockCreateDietCubit();
final state = CreateDietState.initial();
when(cubit.state).thenReturn(state);
when(cubit.stream).thenAnswer((_) => Stream.value(state));
when(cubit.close()).thenAnswer((_) async => Future<void>.value());
await tester.pumpWidget(BlocProvider<CreateDietCubit>.value(value: cubit, child: wrap(const DayWidget(index: 0))));
await tester.pumpAndSettle();
final chips = find.byType(ChoiceChipWidget);
expect(chips, findsNWidgets(5));
await tester.tap(chips.at(2));
await tester.pumpAndSettle();
await tester.tap(chips.at(4));
await tester.pumpAndSettle();
});
testWidgets('displays day number correctly', (tester) async {
final cubit = MockCreateDietCubit();
final state = CreateDietState.initial();
when(cubit.state).thenReturn(state);
when(cubit.stream).thenAnswer((_) => Stream.value(state));
when(cubit.close()).thenAnswer((_) async => Future<void>.value());
await tester.pumpWidget(BlocProvider<CreateDietCubit>.value(value: cubit, child: wrap(const DayWidget(index: 2))));
await tester.pumpAndSettle();
expect(find.text('День 3'), findsOneWidget);
});
testWidgets('displays products in list when dailySchedule has data', (tester) async {
final cubit = MockCreateDietCubit();
final dailySchedule = [
[
[
const DailySchedule(
name: 'Test Product',
portion: Portion.grams,
quantity: 100,
dailyNutrition: DailyNutrition(energy: 100.0, proteins: 10.0, fats: 5.0, carbohydrates: 20.0),
),
],
],
];
final dietData = CreateDietModel(type: {}, dailySchedule: dailySchedule);
final state = CreateDietState.initial().copyWith(dietData: dietData);
when(cubit.state).thenReturn(state);
when(cubit.stream).thenAnswer((_) => Stream.value(state));
when(cubit.close()).thenAnswer((_) async => Future<void>.value());
await tester.pumpWidget(BlocProvider<CreateDietCubit>.value(value: cubit, child: wrap(const DayWidget(index: 0))));
await tester.pumpAndSettle();
// Проверяем, что виджет отрендерился с данными
expect(find.byType(DayWidget), findsOneWidget);
});
testWidgets('displays CPFC container with calculated values', (tester) async {
final cubit = MockCreateDietCubit();
final dailySchedule = [
[
[
const DailySchedule(
name: 'Test Product',
portion: Portion.grams,
quantity: 100,
dailyNutrition: DailyNutrition(energy: 100.0, proteins: 10.0, fats: 5.0, carbohydrates: 20.0),
),
],
],
];
final dietData = CreateDietModel(type: {}, dailySchedule: dailySchedule);
final state = CreateDietState.initial().copyWith(dietData: dietData);
when(cubit.state).thenReturn(state);
when(cubit.stream).thenAnswer((_) => Stream.value(state));
when(cubit.close()).thenAnswer((_) async => Future<void>.value());
await tester.pumpWidget(BlocProvider<CreateDietCubit>.value(value: cubit, child: wrap(const DayWidget(index: 0))));
await tester.pumpAndSettle();
expect(find.byType(CpfcContainer), findsOneWidget);
});
});
}

View File

@@ -0,0 +1,60 @@
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/theme/theme_helper.dart';
import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_cubit.dart';
import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_state.dart';
import 'package:eatvio_app/features/create_diet/presentation/widgets/diet_duration_drop_down_widget.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'diet_duration_drop_down_widget_test.mocks.dart';
@GenerateNiceMocks([MockSpec<CreateDietCubit>()])
void main() {
group('DietDurationDropDownWidget smoke', () {
setUpAll(() async {
dotenv.loadFromString(envString: 'DEV_BASE_URL=http://localhost\nBASE_URL=http://localhost');
});
setUp(() async {
await sl.reset();
sl.registerLazySingleton<AppConfig>(() => AppConfig(buildType: AppBuildType.tests)..setBaseUrl('http://localhost'));
});
Widget wrap(Widget child) {
return MaterialApp(
theme: ThemeHelper().themeData(),
localizationsDelegates: const [
S.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: const [Locale('ru')],
home: Scaffold(body: child),
);
}
testWidgets('renders diet duration drop down widget', (tester) async {
final cubit = MockCreateDietCubit();
final state = CreateDietState.initial();
when(cubit.state).thenReturn(state);
when(cubit.stream).thenAnswer((_) => Stream.value(state));
when(cubit.close()).thenAnswer((_) async => Future<void>.value());
when(cubit.setDuration(any)).thenReturn(null);
await tester.pumpWidget(BlocProvider<CreateDietCubit>.value(value: cubit, child: wrap(const DietDurationDropDownWidget())));
await tester.pumpAndSettle();
expect(find.byType(DietDurationDropDownWidget), findsOneWidget);
});
});
}

View File

@@ -0,0 +1,59 @@
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/theme/theme_helper.dart';
import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_cubit.dart';
import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_state.dart';
import 'package:eatvio_app/features/create_diet/presentation/widgets/diet_type_choice_widget.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'diet_type_choice_widget_test.mocks.dart';
@GenerateNiceMocks([MockSpec<CreateDietCubit>()])
void main() {
group('DietTypeChoiceWidget smoke', () {
setUpAll(() async {
dotenv.loadFromString(envString: 'DEV_BASE_URL=http://localhost\nBASE_URL=http://localhost');
});
setUp(() async {
await sl.reset();
sl.registerLazySingleton<AppConfig>(() => AppConfig(buildType: AppBuildType.tests)..setBaseUrl('http://localhost'));
});
Widget wrap(Widget child) {
return MaterialApp(
theme: ThemeHelper().themeData(),
localizationsDelegates: const [
S.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: const [Locale('ru')],
home: Scaffold(body: child),
);
}
testWidgets('renders diet type choice widget', (tester) async {
final cubit = MockCreateDietCubit();
final state = CreateDietState.initial();
when(cubit.state).thenReturn(state);
when(cubit.stream).thenAnswer((_) => Stream.value(state));
when(cubit.close()).thenAnswer((_) async => Future<void>.value());
await tester.pumpWidget(BlocProvider<CreateDietCubit>.value(value: cubit, child: wrap(const DietTypeChoiceWidget())));
await tester.pumpAndSettle();
expect(find.byType(DietTypeChoiceWidget), findsOneWidget);
});
});
}

View File

@@ -4,85 +4,20 @@ import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
group('ValidationErrorWidget', () {
testWidgets('displays error message', (tester) async {
await tester.pumpWidget(
MaterialApp(
theme: ThemeHelper().themeData(),
home: const Scaffold(
body: ValidationErrorWidget(error: 'Test error message'),
),
),
);
expect(find.byType(ValidationErrorWidget), findsOneWidget);
expect(find.text('Test error message'), findsOneWidget);
});
testWidgets('displays error icon', (tester) async {
await tester.pumpWidget(
MaterialApp(
theme: ThemeHelper().themeData(),
home: const Scaffold(
body: ValidationErrorWidget(error: 'Test error'),
),
),
);
expect(find.byType(ValidationErrorWidget), findsOneWidget);
});
testWidgets('handles long error messages', (tester) async {
final longError = 'A' * 100;
await tester.pumpWidget(
MaterialApp(
theme: ThemeHelper().themeData(),
home: Scaffold(
body: ValidationErrorWidget(error: longError),
),
),
);
expect(find.byType(ValidationErrorWidget), findsOneWidget);
expect(find.text(longError), findsOneWidget);
});
testWidgets('displays empty error message', (tester) async {
await tester.pumpWidget(
MaterialApp(
theme: ThemeHelper().themeData(),
home: const Scaffold(
body: ValidationErrorWidget(error: ''),
),
),
group('ValidationErrorWidget smoke', () {
Widget wrap(Widget child) {
return MaterialApp(
theme: ThemeHelper().themeData(),
home: Scaffold(body: child),
);
}
testWidgets('renders validation error widget', (tester) async {
await tester.pumpWidget(wrap(const ValidationErrorWidget(error: 'Test error')));
await tester.pump();
expect(find.byType(ValidationErrorWidget), findsOneWidget);
expect(find.text('Test error'), findsOneWidget);
});
});
}

View File

@@ -1,3 +1,6 @@
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/theme/theme_helper.dart';
import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_cubit.dart';
import 'package:eatvio_app/features/create_diet/presentation/cubit/create_diet_state.dart';
@@ -6,6 +9,7 @@ import 'package:eatvio_app/features/feed/models/diets_pagination_model.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
@@ -13,16 +17,19 @@ import 'package:mockito/mockito.dart';
import 'status_drop_down_widget_test.mocks.dart';
@GenerateMocks([CreateDietCubit])
@GenerateNiceMocks([MockSpec<CreateDietCubit>()])
void main() {
group('StatusDropDownWidget', () {
late MockCreateDietCubit mockCubit;
setUp(() {
mockCubit = MockCreateDietCubit();
group('StatusDropDownWidget smoke', () {
setUpAll(() async {
dotenv.loadFromString(envString: 'DEV_BASE_URL=http://localhost\nBASE_URL=http://localhost');
});
Widget createTestWidget({DietState? dietState}) {
setUp(() async {
await sl.reset();
sl.registerLazySingleton<AppConfig>(() => AppConfig(buildType: AppBuildType.tests)..setBaseUrl('http://localhost'));
});
Widget wrap(Widget child) {
return MaterialApp(
theme: ThemeHelper().themeData(),
localizationsDelegates: const [
@@ -32,50 +39,43 @@ void main() {
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: const [Locale('ru')],
home: BlocProvider<CreateDietCubit>.value(
value: mockCubit,
child: Scaffold(
body: StatusDropDownWidget(dietState: dietState),
),
),
home: Scaffold(body: child),
);
}
testWidgets('displays status dropdown widget', (tester) async {
when(mockCubit.state).thenReturn(
CreateDietState.initial(),
);
when(mockCubit.stream).thenAnswer((_) => Stream.value(
CreateDietState.initial(),
));
// ignore: return_of_invalid_type
when(mockCubit.setDietState(any)).thenAnswer((_) => () {});
testWidgets('renders status drop down widget', (tester) async {
final cubit = MockCreateDietCubit();
final state = CreateDietState.initial();
await tester.pumpWidget(createTestWidget());
when(cubit.state).thenReturn(state);
when(cubit.stream).thenAnswer((_) => Stream.value(state));
when(cubit.close()).thenAnswer((_) async => Future<void>.value());
when(cubit.setDietState(any)).thenReturn(null);
await tester.pumpWidget(BlocProvider<CreateDietCubit>.value(value: cubit, child: wrap(const StatusDropDownWidget())));
await tester.pumpAndSettle();
expect(find.byType(StatusDropDownWidget), findsOneWidget);
});
testWidgets('displays error when dietStateError is present', (tester) async {
when(mockCubit.state).thenReturn(
CreateDietState.initial().copyWith(
dietStateError: 'Error message',
testWidgets('renders status drop down widget with diet state', (tester) async {
final cubit = MockCreateDietCubit();
final state = CreateDietState.initial();
when(cubit.state).thenReturn(state);
when(cubit.stream).thenAnswer((_) => Stream.value(state));
when(cubit.close()).thenAnswer((_) async => Future<void>.value());
when(cubit.setDietState(any)).thenReturn(null);
await tester.pumpWidget(
BlocProvider<CreateDietCubit>.value(
value: cubit,
child: wrap(const StatusDropDownWidget(dietState: DietState.active)),
),
);
when(mockCubit.stream).thenAnswer((_) => Stream.value(
CreateDietState.initial().copyWith(
dietStateError: 'Error message',
),
));
// ignore: return_of_invalid_type
when(mockCubit.setDietState(any)).thenAnswer((_) => () {});
await tester.pumpWidget(createTestWidget());
await tester.pumpAndSettle();
expect(find.byType(StatusDropDownWidget), findsOneWidget);
});
});
}