forked from eatvio/eatvio_app
feat(auth): tests Issue-761
This commit is contained in:
138
test/features/auth/presentation/bloc/auth_cubit_test.dart
Normal file
138
test/features/auth/presentation/bloc/auth_cubit_test.dart
Normal file
@@ -0,0 +1,138 @@
|
||||
import 'package:eatvio_app/features/auth/models/token_model.dart';
|
||||
import 'package:eatvio_app/features/auth/presentation/bloc/auth_cubit/auth_cubit.dart';
|
||||
import 'package:eatvio_app/features/auth/presentation/bloc/auth_cubit/auth_state.dart';
|
||||
import 'package:eatvio_app/features/auth/repositories/auth_repository.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/annotations.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import 'auth_cubit_test.mocks.dart';
|
||||
|
||||
@GenerateNiceMocks([MockSpec<AuthRepository>()])
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('AuthCubit', () {
|
||||
AuthCubit? authCubit;
|
||||
late MockAuthRepository mockAuthRepository;
|
||||
|
||||
const testToken = TokenModel(token: 'test_token_12345');
|
||||
|
||||
setUp(() async {
|
||||
mockAuthRepository = MockAuthRepository();
|
||||
SharedPreferences.setMockInitialValues({});
|
||||
|
||||
// Mock Fluttertoast plugin
|
||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||
.setMockMethodCallHandler(
|
||||
const MethodChannel('PonnamKarthik/fluttertoast'),
|
||||
(MethodCall methodCall) async {
|
||||
return null;
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
tearDown(() async {
|
||||
if (authCubit != null && !authCubit!.isClosed) {
|
||||
await Future.delayed(const Duration(milliseconds: 100));
|
||||
authCubit!.close();
|
||||
authCubit = null;
|
||||
}
|
||||
|
||||
// Clear mock handlers
|
||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||
.setMockMethodCallHandler(
|
||||
const MethodChannel('PonnamKarthik/fluttertoast'),
|
||||
null,
|
||||
);
|
||||
});
|
||||
|
||||
test('начальное состояние - AuthState', () {
|
||||
authCubit = AuthCubit(authRepository: mockAuthRepository);
|
||||
expect(authCubit!.state, const AuthState());
|
||||
});
|
||||
|
||||
test('getTokenByCredentials - успешная авторизация', () async {
|
||||
authCubit = AuthCubit(authRepository: mockAuthRepository);
|
||||
when(mockAuthRepository.getTokenByCredentials(any, any))
|
||||
.thenAnswer((_) async => testToken);
|
||||
|
||||
final result = await authCubit!.getTokenByCredentials('test@example.com', 'password123');
|
||||
|
||||
expect(result, true);
|
||||
await Future.delayed(const Duration(milliseconds: 100));
|
||||
expect(authCubit!.state, isA<AuthStateLoaded>());
|
||||
final loadedState = authCubit!.state as AuthStateLoaded;
|
||||
expect(loadedState.token, 'test_token_12345');
|
||||
verify(mockAuthRepository.getTokenByCredentials('test@example.com', 'password123')).called(1);
|
||||
});
|
||||
|
||||
test('getTokenByCredentials - переход в состояние loading', () async {
|
||||
authCubit = AuthCubit(authRepository: mockAuthRepository);
|
||||
when(mockAuthRepository.getTokenByCredentials(any, any))
|
||||
.thenAnswer((_) async => Future.delayed(const Duration(milliseconds: 50)).then((_) => testToken));
|
||||
|
||||
final future = authCubit!.getTokenByCredentials('test@example.com', 'password123');
|
||||
await Future.delayed(const Duration(milliseconds: 10));
|
||||
|
||||
expect(authCubit!.state, isA<AuthStateLoading>());
|
||||
await future;
|
||||
});
|
||||
|
||||
test('getTokenByCredentials - обработка ошибки авторизации', () async {
|
||||
authCubit = AuthCubit(authRepository: mockAuthRepository);
|
||||
when(mockAuthRepository.getTokenByCredentials(any, any))
|
||||
.thenThrow(Exception('Неверный адрес электронной почты или пароль'));
|
||||
|
||||
final result = await authCubit!.getTokenByCredentials('wrong@example.com', 'wrongpassword');
|
||||
|
||||
expect(result, false);
|
||||
await Future.delayed(const Duration(milliseconds: 100));
|
||||
expect(authCubit!.state, isA<AuthStateError>());
|
||||
final errorState = authCubit!.state as AuthStateError;
|
||||
expect(errorState.errorMessage, contains('Exception'));
|
||||
verify(mockAuthRepository.getTokenByCredentials('wrong@example.com', 'wrongpassword')).called(1);
|
||||
});
|
||||
|
||||
test('getTokenByCredentials - обработка различных типов ошибок', () async {
|
||||
authCubit = AuthCubit(authRepository: mockAuthRepository);
|
||||
when(mockAuthRepository.getTokenByCredentials(any, any))
|
||||
.thenThrow(FormatException('Invalid credentials format'));
|
||||
|
||||
final result = await authCubit!.getTokenByCredentials('invalid', 'pass');
|
||||
|
||||
expect(result, false);
|
||||
await Future.delayed(const Duration(milliseconds: 100));
|
||||
expect(authCubit!.state, isA<AuthStateError>());
|
||||
final errorState = authCubit!.state as AuthStateError;
|
||||
expect(errorState.errorMessage, contains('FormatException'));
|
||||
});
|
||||
|
||||
test('getTokenByCredentials - проверка вызова с правильными параметрами', () async {
|
||||
authCubit = AuthCubit(authRepository: mockAuthRepository);
|
||||
when(mockAuthRepository.getTokenByCredentials(any, any))
|
||||
.thenAnswer((_) async => testToken);
|
||||
|
||||
await authCubit!.getTokenByCredentials('user@test.com', 'securePassword123');
|
||||
|
||||
verify(mockAuthRepository.getTokenByCredentials('user@test.com', 'securePassword123')).called(1);
|
||||
verifyNever(mockAuthRepository.getTokenByCredentials('other@test.com', 'otherPassword'));
|
||||
});
|
||||
|
||||
test('getTokenByCredentials - множественные вызовы', () async {
|
||||
authCubit = AuthCubit(authRepository: mockAuthRepository);
|
||||
when(mockAuthRepository.getTokenByCredentials(any, any))
|
||||
.thenAnswer((_) async => testToken);
|
||||
|
||||
await authCubit!.getTokenByCredentials('first@test.com', 'password1');
|
||||
await Future.delayed(const Duration(milliseconds: 100));
|
||||
await authCubit!.getTokenByCredentials('second@test.com', 'password2');
|
||||
|
||||
verify(mockAuthRepository.getTokenByCredentials('first@test.com', 'password1')).called(1);
|
||||
verify(mockAuthRepository.getTokenByCredentials('second@test.com', 'password2')).called(1);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
413
test/features/auth/presentation/widgets/basic_widget_test.dart
Normal file
413
test/features/auth/presentation/widgets/basic_widget_test.dart
Normal file
@@ -0,0 +1,413 @@
|
||||
// ignore_for_file: deprecated_member_use
|
||||
|
||||
import 'package:eatvio_app/app_keys.dart';
|
||||
import 'package:eatvio_app/core/theme/theme_helper.dart';
|
||||
import 'package:eatvio_app/features/auth/presentation/bloc/auth_cubit/auth_cubit.dart';
|
||||
import 'package:eatvio_app/features/auth/repositories/auth_repository.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mockito/annotations.dart';
|
||||
|
||||
import 'basic_widget_test.mocks.dart';
|
||||
|
||||
@GenerateMocks([AuthRepository])
|
||||
void main() {
|
||||
group('Auth Basic Widget Tests', () {
|
||||
late MockAuthRepository mockAuthRepository;
|
||||
late AuthCubit authCubit;
|
||||
|
||||
setUp(() {
|
||||
mockAuthRepository = MockAuthRepository();
|
||||
authCubit = AuthCubit(authRepository: mockAuthRepository);
|
||||
});
|
||||
|
||||
tearDown(() {
|
||||
authCubit.close();
|
||||
});
|
||||
|
||||
Widget createTestWidget(Widget child) {
|
||||
return MaterialApp(
|
||||
theme: ThemeHelper().themeData(),
|
||||
home: BlocProvider<AuthCubit>(
|
||||
create: (context) => authCubit,
|
||||
child: Scaffold(body: SingleChildScrollView(child: child)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
testWidgets('should create AuthCubit successfully', (WidgetTester tester) async {
|
||||
// Arrange & Act
|
||||
final cubit = AuthCubit(authRepository: mockAuthRepository);
|
||||
|
||||
// Assert
|
||||
expect(cubit, isA<AuthCubit>());
|
||||
|
||||
cubit.close();
|
||||
});
|
||||
|
||||
testWidgets('should display email text field', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
Widget emailWidget = TextFormField(
|
||||
key: AppKeys.emailTextFieldAuthScreen,
|
||||
decoration: const InputDecoration(labelText: 'Email', hintText: 'Введите email'),
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
);
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(emailWidget));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Assert
|
||||
expect(find.byKey(AppKeys.emailTextFieldAuthScreen), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('should display password text field', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
Widget passwordWidget = TextFormField(
|
||||
key: AppKeys.passwordTextFieldAuthScreen,
|
||||
decoration: const InputDecoration(labelText: 'Пароль', hintText: 'Введите пароль'),
|
||||
obscureText: true,
|
||||
);
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(passwordWidget));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Assert
|
||||
expect(find.byKey(AppKeys.passwordTextFieldAuthScreen), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('should display sign in button', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
Widget buttonWidget = FloatingActionButton.extended(
|
||||
key: AppKeys.signInButtonAuthScreen,
|
||||
onPressed: () {},
|
||||
label: const Text('Войти'),
|
||||
);
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(buttonWidget));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Assert
|
||||
expect(find.byKey(AppKeys.signInButtonAuthScreen), findsOneWidget);
|
||||
expect(find.text('Войти'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('should update email when user types in email field', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
Widget emailWidget = TextFormField(
|
||||
key: AppKeys.emailTextFieldAuthScreen,
|
||||
decoration: const InputDecoration(labelText: 'Email', hintText: 'Введите email'),
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
);
|
||||
|
||||
await tester.pumpWidget(createTestWidget(emailWidget));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
const testEmail = 'test@example.com';
|
||||
|
||||
// Act
|
||||
await tester.enterText(find.byKey(AppKeys.emailTextFieldAuthScreen), testEmail);
|
||||
await tester.pump();
|
||||
|
||||
// Assert
|
||||
expect(find.text(testEmail), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('should update password when user types in password field', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
Widget passwordWidget = TextFormField(
|
||||
key: AppKeys.passwordTextFieldAuthScreen,
|
||||
decoration: const InputDecoration(labelText: 'Пароль', hintText: 'Введите пароль'),
|
||||
obscureText: true,
|
||||
);
|
||||
|
||||
await tester.pumpWidget(createTestWidget(passwordWidget));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
const testPassword = 'password123';
|
||||
|
||||
// Act
|
||||
await tester.enterText(find.byKey(AppKeys.passwordTextFieldAuthScreen), testPassword);
|
||||
await tester.pump();
|
||||
|
||||
// Assert
|
||||
expect(find.text(testPassword), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('should display complete auth form', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
Widget completeForm = Column(
|
||||
children: [
|
||||
TextFormField(
|
||||
key: AppKeys.emailTextFieldAuthScreen,
|
||||
decoration: const InputDecoration(labelText: 'Email', hintText: 'Введите email'),
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
TextFormField(
|
||||
key: AppKeys.passwordTextFieldAuthScreen,
|
||||
decoration: const InputDecoration(labelText: 'Пароль', hintText: 'Введите пароль'),
|
||||
obscureText: true,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
FloatingActionButton.extended(
|
||||
key: AppKeys.signInButtonAuthScreen,
|
||||
onPressed: () {},
|
||||
label: const Text('Войти'),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(completeForm));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Assert
|
||||
expect(find.byKey(AppKeys.emailTextFieldAuthScreen), findsOneWidget);
|
||||
expect(find.byKey(AppKeys.passwordTextFieldAuthScreen), findsOneWidget);
|
||||
expect(find.byKey(AppKeys.signInButtonAuthScreen), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('should display loading state', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
Widget loadingWidget = const Center(child: CircularProgressIndicator());
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(loadingWidget));
|
||||
await tester.pump();
|
||||
|
||||
// Assert
|
||||
expect(find.byType(CircularProgressIndicator), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('should display error message', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
Widget errorWidget = const Center(child: Text('Неверный адрес электронной почты или пароль'));
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(errorWidget));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Assert
|
||||
expect(find.text('Неверный адрес электронной почты или пароль'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('should handle sign in button tap', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
bool buttonPressed = false;
|
||||
Widget buttonWidget = FloatingActionButton.extended(
|
||||
key: AppKeys.signInButtonAuthScreen,
|
||||
onPressed: () {
|
||||
buttonPressed = true;
|
||||
},
|
||||
label: const Text('Войти'),
|
||||
);
|
||||
|
||||
await tester.pumpWidget(createTestWidget(buttonWidget));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Act
|
||||
await tester.tap(find.byKey(AppKeys.signInButtonAuthScreen));
|
||||
await tester.pump();
|
||||
|
||||
// Assert
|
||||
expect(buttonPressed, isTrue);
|
||||
});
|
||||
|
||||
testWidgets('should display password visibility toggle', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
Widget passwordWidget = TextFormField(
|
||||
key: AppKeys.passwordTextFieldAuthScreen,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Пароль',
|
||||
hintText: 'Введите пароль',
|
||||
suffix: IconButton(
|
||||
onPressed: null,
|
||||
icon: Icon(Icons.visibility_outlined),
|
||||
),
|
||||
),
|
||||
obscureText: true,
|
||||
);
|
||||
|
||||
await tester.pumpWidget(createTestWidget(passwordWidget));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Assert
|
||||
expect(find.byIcon(Icons.visibility_outlined), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('should display "Забыли пароль?" link', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
Widget forgotPasswordWidget = GestureDetector(
|
||||
onTap: () {},
|
||||
child: const Text(
|
||||
'Забыли пароль?',
|
||||
style: TextStyle(decoration: TextDecoration.underline),
|
||||
),
|
||||
);
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(forgotPasswordWidget));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Assert
|
||||
expect(find.text('Забыли пароль?'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('should display registration link', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
Widget registrationLinkWidget = Row(
|
||||
children: [
|
||||
const Text('Нет аккаунта? '),
|
||||
GestureDetector(
|
||||
onTap: () {},
|
||||
child: const Text(
|
||||
'Зарегистрироваться',
|
||||
style: TextStyle(decoration: TextDecoration.underline),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(registrationLinkWidget));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Assert
|
||||
expect(find.text('Нет аккаунта? '), findsOneWidget);
|
||||
expect(find.text('Зарегистрироваться'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('should display social auth buttons', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
Widget socialButtonsWidget = Column(
|
||||
children: [
|
||||
const Text('Авторизация через'),
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () {},
|
||||
child: Container(
|
||||
height: 48,
|
||||
width: 80,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
border: Border.all(color: const Color(0xFFDBDBDB), width: 1),
|
||||
),
|
||||
child: const Center(child: Text('VK')),
|
||||
),
|
||||
),
|
||||
InkWell(
|
||||
onTap: () {},
|
||||
child: Container(
|
||||
height: 48,
|
||||
width: 80,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
border: Border.all(color: const Color(0xFFDBDBDB), width: 1),
|
||||
),
|
||||
child: const Center(child: Text('OK')),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(socialButtonsWidget));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Assert
|
||||
expect(find.text('Авторизация через'), findsOneWidget);
|
||||
expect(find.text('VK'), findsOneWidget);
|
||||
expect(find.text('OK'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('should validate email format', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
Widget emailWidget = TextFormField(
|
||||
key: AppKeys.emailTextFieldAuthScreen,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Email',
|
||||
hintText: 'Введите email',
|
||||
errorText: 'Введите корректный email',
|
||||
),
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
);
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(emailWidget));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Assert
|
||||
expect(find.text('Введите корректный email'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('should disable sign in button when loading', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
Widget buttonWidget = FloatingActionButton.extended(
|
||||
key: AppKeys.signInButtonAuthScreen,
|
||||
onPressed: null, // Disabled
|
||||
label: const Text('Войти'),
|
||||
);
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(buttonWidget));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Assert
|
||||
final button = tester.widget(find.byKey(AppKeys.signInButtonAuthScreen));
|
||||
expect(button, isA<FloatingActionButton>());
|
||||
});
|
||||
|
||||
testWidgets('should display form labels correctly', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
Widget formWidget = const Column(
|
||||
children: [
|
||||
Text('Email'),
|
||||
Text('Пароль'),
|
||||
],
|
||||
);
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(formWidget));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Assert
|
||||
expect(find.text('Email'), findsOneWidget);
|
||||
expect(find.text('Пароль'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('should display placeholder text in fields', (WidgetTester tester) async {
|
||||
// Arrange
|
||||
Widget formWidget = Column(
|
||||
children: [
|
||||
TextFormField(
|
||||
key: AppKeys.emailTextFieldAuthScreen,
|
||||
decoration: const InputDecoration(labelText: 'Email', hintText: 'Введите email'),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
TextFormField(
|
||||
key: AppKeys.passwordTextFieldAuthScreen,
|
||||
decoration: const InputDecoration(labelText: 'Пароль', hintText: 'Введите пароль'),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
// Act
|
||||
await tester.pumpWidget(createTestWidget(formWidget));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Assert
|
||||
expect(find.text('Введите email'), findsOneWidget);
|
||||
expect(find.text('Введите пароль'), findsOneWidget);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user