import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:ikar/core/config/app_message_status_size.dart'; import 'package:ikar/core/utils/haptics.dart'; import 'package:ikar/frontend/screens/profile/customization_section.dart'; import 'package:ikar/frontend/screens/profile/message_status_size_screen.dart'; import 'package:ikar/l10n/app_localizations.dart'; import 'package:ikar/main.dart' show api; import 'package:shared_preferences/shared_preferences.dart'; final _slider = find.byKey(const ValueKey('message-status-size-slider')); final _percent = find.byKey(const ValueKey('message-status-size-percent')); Future _pump( WidgetTester tester, { Widget home = const MessageStatusSizeScreen(), Brightness brightness = Brightness.light, Size size = const Size(360, 800), double textScale = 1, }) async { tester.view.physicalSize = size; tester.view.devicePixelRatio = 1; addTearDown(tester.view.reset); await tester.pumpWidget( MaterialApp( locale: const Locale('ru'), localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, theme: ThemeData( colorScheme: ColorScheme.fromSeed( seedColor: Colors.blue, brightness: brightness, ), ), builder: (context, child) => MediaQuery( data: MediaQuery.of( context, ).copyWith(textScaler: TextScaler.linear(textScale)), child: child!, ), home: home, ), ); await tester.pump(); } Future _finishAnimations(WidgetTester tester) async { // The first frame lays out the expanded content and starts AnimatedSize. await tester.pump(); await tester.pump(const Duration(milliseconds: 600)); await tester.pump(); } void _expectPreviewSize(WidgetTester tester, double scale) { for (final status in ['sent', 'delivered', 'read']) { final receipt = find.byKey(ValueKey('message-status-preview-$status')); final icon = tester.widget( find.descendant(of: receipt, matching: find.byType(Icon)), ); expect(icon.size, closeTo(14 * scale, 0.000001), reason: status); } } void main() { late bool hapticsEnabled; late bool fatalHitTestWarnings; setUpAll(() => api.state); setUp(() { SharedPreferences.setMockInitialValues({}); AppMessageStatusSize.current.value = AppMessageStatusSize.defaultScale; hapticsEnabled = Haptics.enabled; Haptics.enabled = false; fatalHitTestWarnings = WidgetController.hitTestWarningShouldBeFatal; WidgetController.hitTestWarningShouldBeFatal = true; }); tearDown(() { AppMessageStatusSize.current.value = AppMessageStatusSize.defaultScale; Haptics.enabled = hapticsEnabled; WidgetController.hitTestWarningShouldBeFatal = fatalHitTestWarnings; }); testWidgets('Russian customization opens the continuous size slider', ( tester, ) async { await _pump( tester, home: const Scaffold( body: SingleChildScrollView(child: CustomizationSection()), ), ); expect(find.text('Галочки сообщений'), findsNothing); await tester.tap(find.text('Кастомизация')); await _finishAnimations(tester); await tester.tap(find.text('Галочки сообщений')); await _finishAnimations(tester); expect(find.byType(MessageStatusSizeScreen), findsOneWidget); final slider = tester.widget(_slider); expect(slider.min, 1.0); expect(slider.max, 1.5); expect(slider.divisions, isNull); expect(slider.value, 1.0); expect(tester.widget(_percent).data, '100%'); expect(find.text('Отправлено'), findsOneWidget); expect(find.text('Доставлено'), findsOneWidget); expect(find.text('Прочитано'), findsOneWidget); _expectPreviewSize(tester, 1.0); expect( tester .widget(find.widgetWithText(TextButton, 'Сбросить')) .onPressed, isNull, ); expect(tester.takeException(), isNull); }); testWidgets('drag updates previews and release saves before the debounce', ( tester, ) async { await _pump(tester); final prefs = await SharedPreferences.getInstance(); final rect = tester.getRect(_slider); final gesture = await tester.startGesture(rect.center); await gesture.moveTo(rect.center + Offset(rect.width * 0.08, 0)); await tester.pump(); final intermediate = AppMessageStatusSize.current.value; expect(intermediate, greaterThan(1.0)); expect(intermediate, lessThan(1.5)); expect(tester.widget(_slider).value, intermediate); expect( tester.widget(_percent).data, '${(intermediate * 100).round()}%', ); _expectPreviewSize(tester, intermediate); expect(prefs.getDouble(AppMessageStatusSize.prefKey), isNull); await gesture.up(); await tester.pump(); expect(prefs.getDouble(AppMessageStatusSize.prefKey), intermediate); await tester.drag(_slider, const Offset(2000, 0)); await tester.pump(); expect(AppMessageStatusSize.current.value, 1.5); expect(prefs.getDouble(AppMessageStatusSize.prefKey), 1.5); expect(tester.widget(_percent).data, '150%'); _expectPreviewSize(tester, 1.5); await tester.tap(find.widgetWithText(TextButton, 'Сбросить')); await tester.pump(); expect(AppMessageStatusSize.current.value, 1.0); expect(prefs.getDouble(AppMessageStatusSize.prefKey), 1.0); expect(tester.widget(_slider).value, 1.0); _expectPreviewSize(tester, 1.0); expect(tester.takeException(), isNull); }); testWidgets('leaving the screen flushes a size and reopening restores it', ( tester, ) async { await _pump( tester, home: const Scaffold( body: SingleChildScrollView(child: CustomizationSection()), ), ); await tester.tap(find.text('Кастомизация')); await _finishAnimations(tester); await tester.tap(find.text('Галочки сообщений')); await _finishAnimations(tester); tester.widget(_slider).onChanged!(1.237); await tester.pump(); Navigator.of(tester.element(find.byType(MessageStatusSizeScreen))).pop(); await _finishAnimations(tester); final prefs = await SharedPreferences.getInstance(); expect(prefs.getDouble(AppMessageStatusSize.prefKey), 1.237); AppMessageStatusSize.current.value = AppMessageStatusSize.defaultScale; expect(await AppMessageStatusSize.load(), 1.237); await tester.tap(find.text('Галочки сообщений')); await _finishAnimations(tester); expect(tester.widget(_slider).value, 1.237); expect(tester.widget(_percent).data, '124%'); _expectPreviewSize(tester, 1.237); expect(tester.takeException(), isNull); }); for (final brightness in Brightness.values) { testWidgets('maximum size fits a small phone in ${brightness.name}', ( tester, ) async { AppMessageStatusSize.current.value = AppMessageStatusSize.maxScale; await _pump( tester, brightness: brightness, size: const Size(320, 568), textScale: 1.3, ); _expectPreviewSize(tester, 1.5); expect(tester.widget(_percent).data, '150%'); expect(tester.takeException(), isNull); await tester.ensureVisible(find.widgetWithText(TextButton, 'Сбросить')); await tester.pump(); await tester.tap(find.widgetWithText(TextButton, 'Сбросить')); await tester.pump(); expect(AppMessageStatusSize.current.value, 1.0); expect(tester.takeException(), isNull); }); } }