import 'package:drift/native.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:glp1_companion/core/database/app_database.dart'; import 'package:glp1_companion/core/platform/notification_service.dart'; import 'package:glp1_companion/features/medication/domain/value_objects/glp1_compound.dart'; import 'package:glp1_companion/features/medication/presentation/controllers/medication_controller.dart'; import 'package:glp1_companion/features/medication/presentation/widgets/log_injection_dialog.dart'; /// No-op reminders: `flutter_local_notifications` has no test host. class _NoopNotificationService implements INotificationService { @override Future initialize() async {} @override Future requestPermissions() async => false; @override Future scheduleDoseReminder({ required DateTime scheduledAt, required String title, required String body, int id = kDoseReminderNotificationId, String? payload, }) async => false; @override Future cancelReminder(int id) async {} @override Future cancelAllReminders() async {} } /// Regression tests for the narrow-mobile overflow: the compound dropdown's /// long labels ("Tirzepatide (Mounjaro / Zepbound)") must ellipsize instead /// of painting yellow/black stripes on a phone-width dialog. /// /// RenderFlex overflows raise a [FlutterError] inside widget tests, so a /// clean run also proves the stripes are gone. void main() { late AppDatabase database; setUp(() { database = AppDatabase.forTesting(NativeDatabase.memory()); }); tearDown(() => database.close()); Future pumpDialog( WidgetTester tester, { required Size logicalSize, Glp1Compound? initialCompound, }) async { tester.view.physicalSize = logicalSize; tester.view.devicePixelRatio = 1.0; addTearDown(tester.view.resetPhysicalSize); addTearDown(tester.view.resetDevicePixelRatio); await tester.pumpWidget( ProviderScope( overrides: [ appDatabaseProvider.overrideWithValue(database), notificationServiceProvider .overrideWithValue(_NoopNotificationService()), ], child: MaterialApp( home: Builder( builder: (context) => Scaffold( body: Center( child: ElevatedButton( onPressed: () => showLogInjectionDialog( context, initialCompound: initialCompound, ), child: const Text('Open'), ), ), ), ), ), ), ); await tester.tap(find.text('Open')); await tester.pumpAndSettle(); } testWidgets( 'narrow phone dialog renders the long tirzepatide label without overflow', (tester) async { await pumpDialog( tester, logicalSize: const Size(320, 640), initialCompound: Glp1Compound.tirzepatide, ); // The longest compound label renders (ellipsized if needed). expect(find.text('Tirzepatide (Mounjaro / Zepbound)'), findsOneWidget); expect(tester.takeException(), isNull); }); testWidgets('narrow phone dialog renders the semaglutide label cleanly', (tester) async { await pumpDialog( tester, logicalSize: const Size(320, 640), initialCompound: Glp1Compound.semaglutide, ); expect(find.text('Semaglutide (Ozempic / Wegovy / Rybelsus)'), findsOneWidget); expect(tester.takeException(), isNull); }); testWidgets('standard mobile width still renders the full dialog', (tester) async { await pumpDialog(tester, logicalSize: const Size(390, 844)); // Title + save-button label both read "Log Injection". expect(find.text('Log Injection'), findsNWidgets(2)); expect(find.text('Injection site'), findsOneWidget); expect(tester.takeException(), isNull); }); }