2025-06-06 16:45:40 +03:00
import ' package:flutter/material.dart ' ;
import ' package:flutter_test/flutter_test.dart ' ;
import ' package:friflex_starter/app/ui_kit/app_snackbar.dart ' ;
/// Тесты для виджета AppSnackBar
void main ( ) {
group ( ' AppSnackBar Widget Tests ' , ( ) {
late Widget testApp ;
/// Создание мок-темы с необходимыми цветами для снекбара
ColorScheme createMockColorScheme ( ) {
return const ColorScheme . light ( ) . copyWith (
2025-06-07 19:03:13 +03:00
error: Colors . red ,
primary: Colors . blue ,
secondary: Colors . green ,
2025-06-06 16:45:40 +03:00
) ;
}
/// Создание мок-темы с правильными стилями текста
TextTheme createMockTextTheme ( ) {
2025-06-20 16:50:48 +03:00
return const TextTheme ( bodyMedium: TextStyle ( fontSize: 14 ) ) ;
2025-06-06 16:45:40 +03:00
}
setUp ( ( ) {
testApp = MaterialApp (
theme: ThemeData (
colorScheme: createMockColorScheme ( ) ,
textTheme: createMockTextTheme ( ) ,
) ,
home: const Scaffold ( body: Center ( child: Text ( ' Тестовый экран ' ) ) ) ,
) ;
} ) ;
testTester ( String description , WidgetTesterCallback callback ) {
testWidgets ( description , ( tester ) async {
await tester . pumpWidget ( testApp ) ;
await callback ( tester ) ;
} ) ;
}
2025-06-07 19:03:13 +03:00
group ( ' AppSnackBar.showInfo ' , ( ) {
2025-06-20 16:50:48 +03:00
testTester ( ' показывает снекбар с информацией и правильными стилями ' , (
tester ,
) async {
2025-06-07 19:03:13 +03:00
const infoMessage = ' Это просто сообщение ' ;
AppSnackBar . showInfo (
tester . element ( find . byType ( Scaffold ) ) ,
message: infoMessage ,
) ;
await tester . pump ( ) ;
await tester . pump ( const Duration ( milliseconds: 100 ) ) ;
expect ( find . byType ( AppSnackBar ) , findsOneWidget ) ;
expect ( find . text ( infoMessage ) , findsOneWidget ) ;
2025-06-20 16:50:48 +03:00
2025-06-07 19:03:13 +03:00
// Проверяем иконку и её цвет
final iconFinder = find . byType ( Icon ) ;
expect ( iconFinder , findsOneWidget ) ;
final icon = tester . widget < Icon > ( iconFinder ) ;
expect ( icon . color , equals ( Colors . black ) ) ; // Info иконка черная
// Проверяем цвет фона
final container = tester . widget < Container > (
find . descendant (
of: find . byType ( GestureDetector ) ,
matching: find . byType ( Container ) ,
) ,
) ;
final decoration = container . decoration as BoxDecoration ;
expect ( decoration . color , equals ( const Color ( 0xFFE6E6E6 ) ) ) ; // Info фон
} ) ;
} ) ;
2025-06-06 16:45:40 +03:00
group ( ' AppSnackBar.showError ' , ( ) {
2025-06-20 16:50:48 +03:00
testTester ( ' показывает снекбар с ошибкой и правильными стилями ' , (
tester ,
) async {
2025-06-06 16:45:40 +03:00
const errorMessage = ' Произошла ошибка ' ;
AppSnackBar . showError (
tester . element ( find . byType ( Scaffold ) ) ,
message: errorMessage ,
) ;
await tester . pump ( ) ;
await tester . pump ( const Duration ( milliseconds: 100 ) ) ;
expect ( find . byType ( AppSnackBar ) , findsOneWidget ) ;
expect ( find . text ( errorMessage ) , findsOneWidget ) ;
2025-06-07 19:03:13 +03:00
// Проверяем иконку и её цвет
final iconFinder = find . byType ( Icon ) ;
expect ( iconFinder , findsOneWidget ) ;
final icon = tester . widget < Icon > ( iconFinder ) ;
expect ( icon . color , equals ( Colors . white ) ) ; // Error иконка белая
// Проверяем цвет фона
final container = tester . widget < Container > (
find . descendant (
of: find . byType ( GestureDetector ) ,
matching: find . byType ( Container ) ,
) ,
) ;
final decoration = container . decoration as BoxDecoration ;
expect ( decoration . color , equals ( const Color ( 0xFFD24720 ) ) ) ; // Error фон
2025-06-06 16:45:40 +03:00
} ) ;
testTester ( ' показывает снекбар с кастомной продолжительностью ' , (
tester ,
) async {
const errorMessage = ' Кастомная ошибка ' ;
const customDuration = Duration ( seconds: 5 ) ;
AppSnackBar . showError (
tester . element ( find . byType ( Scaffold ) ) ,
message: errorMessage ,
displayDuration: customDuration ,
) ;
await tester . pump ( ) ;
await tester . pump ( const Duration ( milliseconds: 100 ) ) ;
expect ( find . byType ( AppSnackBar ) , findsOneWidget ) ;
expect ( find . text ( errorMessage ) , findsOneWidget ) ;
} ) ;
testTester ( ' заменяет предыдущий снекбар при повторном вызове ' , (
tester ,
) async {
const firstMessage = ' Первая ошибка ' ;
const secondMessage = ' Вторая ошибка ' ;
// Показываем первый снекбар
AppSnackBar . showError (
tester . element ( find . byType ( Scaffold ) ) ,
message: firstMessage ,
) ;
await tester . pump ( ) ;
await tester . pump ( const Duration ( milliseconds: 100 ) ) ;
expect ( find . text ( firstMessage ) , findsOneWidget ) ;
// Показываем второй снекбар
AppSnackBar . showError (
tester . element ( find . byType ( Scaffold ) ) ,
message: secondMessage ,
) ;
await tester . pump ( ) ;
await tester . pump ( const Duration ( milliseconds: 100 ) ) ;
// Проверяем, что показывается только второй снекбар
expect ( find . text ( firstMessage ) , findsNothing ) ;
expect ( find . text ( secondMessage ) , findsOneWidget ) ;
expect ( find . byType ( AppSnackBar ) , findsOneWidget ) ;
} ) ;
} ) ;
group ( ' AppSnackBar.showSuccess ' , ( ) {
2025-06-20 16:50:48 +03:00
testTester ( ' показывает снекбар с успехом и правильными стилями ' , (
tester ,
) async {
2025-06-06 16:45:40 +03:00
const successMessage = ' Операция выполнена успешно ' ;
AppSnackBar . showSuccess (
context: tester . element ( find . byType ( Scaffold ) ) ,
message: successMessage ,
) ;
await tester . pump ( ) ;
await tester . pump ( const Duration ( milliseconds: 100 ) ) ;
expect ( find . byType ( AppSnackBar ) , findsOneWidget ) ;
expect ( find . text ( successMessage ) , findsOneWidget ) ;
2025-06-07 19:03:13 +03:00
// Проверяем иконку и её цвет
final iconFinder = find . byType ( Icon ) ;
expect ( iconFinder , findsOneWidget ) ;
final icon = tester . widget < Icon > ( iconFinder ) ;
expect ( icon . color , equals ( Colors . white ) ) ; // Success иконка белая
// Проверяем цвет фона
final container = tester . widget < Container > (
find . descendant (
of: find . byType ( GestureDetector ) ,
matching: find . byType ( Container ) ,
) ,
) ;
final decoration = container . decoration as BoxDecoration ;
2025-06-20 16:50:48 +03:00
expect (
decoration . color ,
equals ( const Color ( 0xFF6FB62C ) ) ,
) ; // Success фон
2025-06-06 16:45:40 +03:00
} ) ;
testTester ( ' показывает снекбар с кастомной продолжительностью ' , (
tester ,
) async {
const successMessage = ' Кастомный успех ' ;
const customDuration = Duration ( seconds: 2 ) ;
AppSnackBar . showSuccess (
context: tester . element ( find . byType ( Scaffold ) ) ,
message: successMessage ,
displayDuration: customDuration ,
) ;
await tester . pump ( ) ;
await tester . pump ( const Duration ( milliseconds: 100 ) ) ;
expect ( find . byType ( AppSnackBar ) , findsOneWidget ) ;
expect ( find . text ( successMessage ) , findsOneWidget ) ;
} ) ;
} ) ;
group ( ' AppSnackBar виджет поведение ' , ( ) {
2025-06-20 16:50:48 +03:00
testTester (
' показывает анимацию появления с правильной последовательностью ' ,
( tester ) async {
const message = ' Тестовое сообщение ' ;
2025-06-06 16:45:40 +03:00
2025-06-20 16:50:48 +03:00
AppSnackBar . showError (
tester . element ( find . byType ( Scaffold ) ) ,
message: message ,
) ;
// Проверяем начальное состояние
await tester . pump ( ) ;
final initialPosition = tester . widget < Positioned > (
find . byType ( Positioned ) ,
) ;
expect ( initialPosition . top ? ? 0 , lessThan ( 0 ) ) ;
// Проверяем промежуточное состояние
await tester . pump ( const Duration ( milliseconds: 150 ) ) ;
final middlePosition = tester . widget < Positioned > (
find . byType ( Positioned ) ,
) ;
expect (
middlePosition . top ? ? 0 ,
greaterThan ( initialPosition . top ? ? 0 ) ,
) ;
// Проверяем конечное состояние
await tester . pump ( const Duration ( milliseconds: 150 ) ) ;
final finalPosition = tester . widget < Positioned > (
find . byType ( Positioned ) ,
) ;
expect ( finalPosition . top ? ? 0 , greaterThan ( 0 ) ) ;
} ,
) ;
2025-06-06 16:45:40 +03:00
testTester ( ' закрывается при тапе ' , ( tester ) async {
const message = ' Тап для закрытия ' ;
AppSnackBar . showError (
tester . element ( find . byType ( Scaffold ) ) ,
message: message ,
) ;
await tester . pump ( ) ;
await tester . pump ( const Duration ( milliseconds: 350 ) ) ;
expect ( find . byType ( AppSnackBar ) , findsOneWidget ) ;
// Тапаем по снекбару
await tester . tap ( find . byType ( GestureDetector ) ) ;
await tester . pump ( ) ;
// Ждем завершения анимации закрытия и удаления из Overlay
await tester . pumpAndSettle ( ) ;
// Проверяем, что снекбар исчез
expect ( find . byType ( AppSnackBar ) , findsNothing ) ;
} ) ;
testTester ( ' автоматически закрывается через указанное время ' , (
tester ,
) async {
const message = ' Автозакрытие ' ;
const duration = Duration ( milliseconds: 500 ) ;
AppSnackBar . showError (
tester . element ( find . byType ( Scaffold ) ) ,
message: message ,
displayDuration: duration ,
) ;
await tester . pump ( ) ;
await tester . pump ( const Duration ( milliseconds: 350 ) ) ;
expect ( find . byType ( AppSnackBar ) , findsOneWidget ) ;
// Ждем автоматического закрытия
await tester . pump ( duration ) ;
await tester . pumpAndSettle ( ) ;
expect ( find . byType ( AppSnackBar ) , findsNothing ) ;
} ) ;
testTester ( ' отображает длинное сообщение с ellipsis ' , ( tester ) async {
const longMessage =
' Это очень длинное сообщение, которое должно отображаться в нескольких строках и обрезаться с помощью ellipsis если оно слишком длинное для отображения в виджете снекбара ' ;
AppSnackBar . showError (
tester . element ( find . byType ( Scaffold ) ) ,
message: longMessage ,
) ;
await tester . pump ( ) ;
await tester . pump ( const Duration ( milliseconds: 100 ) ) ;
expect ( find . byType ( AppSnackBar ) , findsOneWidget ) ;
// Проверяем, что текст имеет ограничение по строкам
final textWidget = tester . widget < Text > ( find . text ( longMessage ) ) ;
expect ( textWidget . maxLines , equals ( 3 ) ) ;
expect ( textWidget . overflow , equals ( TextOverflow . ellipsis ) ) ;
} ) ;
testTester ( ' имеет правильную структуру виджета ' , ( tester ) async {
const message = ' Структура ' ;
AppSnackBar . showError (
tester . element ( find . byType ( Scaffold ) ) ,
message: message ,
) ;
await tester . pump ( ) ;
await tester . pump ( const Duration ( milliseconds: 100 ) ) ;
// Проверяем основную структуру
expect ( find . byType ( Positioned ) , findsOneWidget ) ;
expect ( find . byType ( Material ) , findsAtLeastNWidgets ( 1 ) ) ;
expect ( find . byType ( GestureDetector ) , findsOneWidget ) ;
expect ( find . byType ( Container ) , findsAtLeastNWidgets ( 1 ) ) ;
expect ( find . byType ( Row ) , findsOneWidget ) ;
expect ( find . byType ( Icon ) , findsOneWidget ) ;
expect ( find . byType ( Text ) , findsAtLeastNWidgets ( 1 ) ) ;
} ) ;
2025-06-20 16:50:48 +03:00
testTester ( ' имеет правильные отступы и размеры на разных экранах ' , (
tester ,
) async {
2025-06-06 16:45:40 +03:00
const message = ' Размеры ' ;
2025-06-07 19:03:13 +03:00
// Тестируем на маленьком экране
await tester . binding . setSurfaceSize ( const Size ( 320 , 480 ) ) ;
2025-06-06 16:45:40 +03:00
AppSnackBar . showError (
tester . element ( find . byType ( Scaffold ) ) ,
message: message ,
) ;
await tester . pump ( ) ;
await tester . pump ( const Duration ( milliseconds: 100 ) ) ;
2025-06-07 19:03:13 +03:00
var container = tester . widget < Container > (
2025-06-06 16:45:40 +03:00
find . descendant (
of: find . byType ( GestureDetector ) ,
matching: find . byType ( Container ) ,
) ,
) ;
2025-06-07 19:03:13 +03:00
// Н а маленьком экране максимальная ширина должна быть 350
expect ( container . constraints ? . maxWidth , equals ( 350 ) ) ;
2025-06-06 16:45:40 +03:00
2025-06-07 19:03:13 +03:00
// Тестируем на большом экране
await tester . binding . setSurfaceSize ( const Size ( 1280 , 720 ) ) ;
await tester . pumpWidget ( testApp ) ;
AppSnackBar . showError (
tester . element ( find . byType ( Scaffold ) ) ,
message: message ,
2025-06-06 16:45:40 +03:00
) ;
2025-06-07 19:03:13 +03:00
await tester . pump ( ) ;
await tester . pump ( const Duration ( milliseconds: 100 ) ) ;
container = tester . widget < Container > (
find . descendant (
of: find . byType ( GestureDetector ) ,
matching: find . byType ( Container ) ,
) ,
2025-06-06 16:45:40 +03:00
) ;
2025-06-20 16:50:48 +03:00
expect (
container . constraints ? . maxWidth ,
equals ( 350 ) ,
) ; // Максимальная ширина
2025-06-06 16:45:40 +03:00
} ) ;
testTester ( ' имеет правильное скругление углов ' , ( tester ) async {
const message = ' Скругление ' ;
AppSnackBar . showError (
tester . element ( find . byType ( Scaffold ) ) ,
message: message ,
) ;
await tester . pump ( ) ;
await tester . pump ( const Duration ( milliseconds: 100 ) ) ;
final container = tester . widget < Container > (
find . descendant (
of: find . byType ( GestureDetector ) ,
matching: find . byType ( Container ) ,
) ,
) ;
final decoration = container . decoration as BoxDecoration ;
expect ( decoration . borderRadius , equals ( BorderRadius . circular ( 16 ) ) ) ;
} ) ;
} ) ;
2025-06-07 19:03:13 +03:00
group ( ' Доступность ' , ( ) {
testTester ( ' имеет правильные семантические метки ' , ( tester ) async {
const message = ' Доступность ' ;
AppSnackBar . showError (
tester . element ( find . byType ( Scaffold ) ) ,
message: message ,
) ;
await tester . pump ( ) ;
await tester . pump ( const Duration ( milliseconds: 100 ) ) ;
// Проверяем наличие текста сообщения
expect ( find . text ( message ) , findsOneWidget ) ;
// Проверяем наличие GestureDetector для закрытия
expect ( find . byType ( GestureDetector ) , findsOneWidget ) ;
} ) ;
} ) ;
2025-06-06 16:45:40 +03:00
group ( ' Управление состоянием ' , ( ) {
testTester ( ' правильно обрабатывает отсутствие mounted контекста ' , (
tester ,
) async {
const message = ' Unmounted контекст ' ;
// Создаем виджет, который будет удален
await tester . pumpWidget (
MaterialApp (
home: Builder (
builder: ( context ) {
return Scaffold (
body: ElevatedButton (
onPressed: ( ) {
AppSnackBar . showError ( context , message: message ) ;
} ,
child: const Text ( ' Show SnackBar ' ) ,
) ,
) ;
} ,
) ,
) ,
) ;
// Нажимаем кнопку для показа снекбара
await tester . tap ( find . byType ( ElevatedButton ) ) ;
await tester . pump ( ) ;
// Заменяем весь виджет (контекст становится unmounted)
await tester . pumpWidget (
const MaterialApp ( home: Scaffold ( body: Text ( ' New Screen ' ) ) ) ,
) ;
// Проверяем, что ошибки не возникает
expect ( tester . takeException ( ) , isNull ) ;
} ) ;
testTester ( ' правильно очищает ресурсы при dispose ' , ( tester ) async {
const message = ' Очистка ресурсов ' ;
AppSnackBar . showError (
tester . element ( find . byType ( Scaffold ) ) ,
message: message ,
) ;
await tester . pump ( ) ;
await tester . pump ( const Duration ( milliseconds: 100 ) ) ;
expect ( find . byType ( AppSnackBar ) , findsOneWidget ) ;
// Заменяем весь виджет для вызова dispose
await tester . pumpWidget (
const MaterialApp ( home: Scaffold ( body: Text ( ' New Screen ' ) ) ) ,
) ;
// Проверяем, что ошибки не возникает при dispose
expect ( tester . takeException ( ) , isNull ) ;
} ) ;
2025-06-07 19:03:13 +03:00
2025-06-20 16:50:48 +03:00
testTester ( ' правильно обрабатывает быстрые последовательные вызовы ' , (
tester ,
) async {
2025-06-07 19:03:13 +03:00
const messages = [ ' Сообщение 1 ' , ' Сообщение 2 ' , ' Сообщение 3 ' ] ;
for ( final message in messages ) {
AppSnackBar . showError (
tester . element ( find . byType ( Scaffold ) ) ,
message: message ,
) ;
await tester . pump ( ) ;
}
await tester . pump ( const Duration ( milliseconds: 100 ) ) ;
// Проверяем, что показывается только последнее сообщение
expect ( find . text ( messages [ 0 ] ) , findsNothing ) ;
expect ( find . text ( messages [ 1 ] ) , findsNothing ) ;
expect ( find . text ( messages [ 2 ] ) , findsOneWidget ) ;
} ) ;
2025-06-06 16:45:40 +03:00
} ) ;
group ( ' Управление снекбарами ' , ( ) {
testTester ( ' правильно заменяет предыдущий снекбар новым ' , ( tester ) async {
const firstMessage = ' Первый снекбар ' ;
const secondMessage = ' Второй снекбар ' ;
// Показываем первый снекбар
AppSnackBar . showError (
tester . element ( find . byType ( Scaffold ) ) ,
message: firstMessage ,
) ;
await tester . pump ( ) ;
await tester . pump ( const Duration ( milliseconds: 100 ) ) ;
expect ( find . text ( firstMessage ) , findsOneWidget ) ;
// Показываем второй снекбар (должен заменить первый)
AppSnackBar . showError (
tester . element ( find . byType ( Scaffold ) ) ,
message: secondMessage ,
) ;
await tester . pump ( ) ;
await tester . pump ( const Duration ( milliseconds: 100 ) ) ;
// Проверяем, что остался только второй снекбар
expect ( find . text ( firstMessage ) , findsNothing ) ;
expect ( find . text ( secondMessage ) , findsOneWidget ) ;
} ) ;
testTester ( ' правильно заменяет снекбар error на success ' , ( tester ) async {
const errorMessage = ' Ошибка ' ;
const successMessage = ' Успех ' ;
// Показываем снекбар с ошибкой
AppSnackBar . showError (
tester . element ( find . byType ( Scaffold ) ) ,
message: errorMessage ,
) ;
await tester . pump ( ) ;
await tester . pump ( const Duration ( milliseconds: 100 ) ) ;
expect ( find . text ( errorMessage ) , findsOneWidget ) ;
// Показываем снекбар с успехом
AppSnackBar . showSuccess (
context: tester . element ( find . byType ( Scaffold ) ) ,
message: successMessage ,
) ;
await tester . pump ( ) ;
await tester . pump ( const Duration ( milliseconds: 100 ) ) ;
// Проверяем, что показывается только снекбар с успехом
expect ( find . text ( errorMessage ) , findsNothing ) ;
expect ( find . text ( successMessage ) , findsOneWidget ) ;
} ) ;
} ) ;
group ( ' Анимация ' , ( ) {
testTester ( ' анимация появления работает корректно ' , ( tester ) async {
const message = ' Анимация появления ' ;
AppSnackBar . showError (
tester . element ( find . byType ( Scaffold ) ) ,
message: message ,
) ;
// В начале анимации снекбар должен находиться за пределами экрана
await tester . pump ( ) ;
final positionedWidget = tester . widget < Positioned > (
find . byType ( Positioned ) ,
) ;
expect ( positionedWidget . top , lessThan ( 0 ) ) ;
// После завершения анимации снекбар должен быть виден
await tester . pump ( const Duration ( milliseconds: 300 ) ) ;
final positionedWidgetAfter = tester . widget < Positioned > (
find . byType ( Positioned ) ,
) ;
expect ( positionedWidgetAfter . top , greaterThan ( 0 ) ) ;
} ) ;
testTester ( ' анимация скрытия работает корректно при тапе ' , (
tester ,
) async {
const message = ' Анимация скрытия ' ;
AppSnackBar . showError (
tester . element ( find . byType ( Scaffold ) ) ,
message: message ,
) ;
await tester . pump ( ) ;
await tester . pump ( const Duration ( milliseconds: 350 ) ) ;
expect ( find . byType ( AppSnackBar ) , findsOneWidget ) ;
// Тапаем для закрытия
await tester . tap ( find . byType ( GestureDetector ) ) ;
await tester . pump ( ) ;
// Анимация закрытия должна начаться
expect ( find . byType ( AppSnackBar ) , findsOneWidget ) ;
// После завершения анимации снекбар должен исчезнуть
await tester . pumpAndSettle ( ) ;
expect ( find . byType ( AppSnackBar ) , findsNothing ) ;
} ) ;
} ) ;
group ( ' Интеграция с MediaQuery ' , ( ) {
testTester ( ' правильно позиционируется с учетом SafeArea ' , ( tester ) async {
const message = ' SafeArea test ' ;
// Создаем приложение с кастомными отступами
await tester . pumpWidget (
MaterialApp (
home: MediaQuery (
data: const MediaQueryData (
padding: EdgeInsets . only ( top: 50 ) , // Симулируем статус б а р
) ,
child: const Scaffold ( body: Center ( child: Text ( ' Test ' ) ) ) ,
) ,
) ,
) ;
AppSnackBar . showError (
tester . element ( find . byType ( Scaffold ) ) ,
message: message ,
) ;
await tester . pump ( ) ;
await tester . pump ( const Duration ( milliseconds: 350 ) ) ;
// Проверяем, что снекбар отображается (позиция может варьироваться в зависимости от того,
// когда именно MediaQuery применяется)
final positionedWidget = tester . widget < Positioned > (
find . byType ( Positioned ) ,
) ;
expect (
positionedWidget . top ,
greaterThan ( 0 ) ,
) ; // Снекбар должен быть видимым
expect ( find . byType ( AppSnackBar ) , findsOneWidget ) ;
} ) ;
} ) ;
} ) ;
}