import 'package:flutter/material.dart'; import 'package:lucide_icons_flutter/lucide_icons.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:miler/Models/stop_status.dart'; import 'package:miler/data/bag_manifest.dart'; import 'package:miler/data/service_profile.dart'; import 'package:miler/views/Dashboard/home/duty_sheet.dart'; import 'package:miler/views/Dashboard/home/stop_action_sheet.dart'; import 'package:miler/views/helpers/constants/Colorconstants.dart'; import 'package:miler/views/helpers/widgets/miler_sheet_kit.dart'; /// ───────────────────────────────────────────────────────────────────────── /// THE SHEET KIT — the rules, held /// /// The kit exists because six presentation styles had grown around one kind /// of surface (see the audit in `miler_sheet_kit.dart`). What stops a seventh /// growing back is not the kit's existence but these tests: the shared handle /// on the sheets a rider actually uses, and the one inset rule that prevents /// the double-SafeArea CTA drift from returning. /// ───────────────────────────────────────────────────────────────────────── void main() { tearDown(() => ServiceProfile.setActive(ServiceProfile.parcel)); Future pump( WidgetTester tester, Widget child, { EdgeInsets viewInsets = EdgeInsets.zero, EdgeInsets viewPadding = EdgeInsets.zero, }) async { tester.view.physicalSize = const Size(1170, 2532); tester.view.devicePixelRatio = 3.0; addTearDown(tester.view.reset); await tester.pumpWidget( ScreenUtilInit( designSize: const Size(390, 844), builder: (_, _) => MaterialApp( home: MediaQuery( data: MediaQueryData( size: const Size(390, 844), viewInsets: viewInsets, viewPadding: viewPadding, ), // NOT a Scaffold: `resizeToAvoidBottomInset` consumes viewInsets // before the sheet sees them, which is exactly the environment a // modal route does NOT provide — the sheet must read the keyboard // itself, which is the behaviour under test. child: Material( color: Colors.black, child: Align(alignment: Alignment.bottomCenter, child: child), ), ), ), ), ); await tester.pump(); } group('the scaffold', () { testWidgets('pays the larger of keyboard and gesture bar, never both', ( tester, ) async { // A gesture-bar phone with the keyboard up. The old double-SafeArea bug // paid 300 + 34; the rule is max(300, 34). await pump( tester, const MilerSheetScaffold(child: SizedBox(height: 40)), viewInsets: const EdgeInsets.only(bottom: 300), viewPadding: const EdgeInsets.only(bottom: 34), ); final padding = tester.widget( find.byType(AnimatedPadding).first, ); expect( padding.padding, const EdgeInsets.only(bottom: 300), reason: 'the keyboard replaces the gesture bar — summing them floats ' 'the CTA a bar height above the keyboard', ); }); testWidgets('with no keyboard the gesture bar is still paid', ( tester, ) async { await pump( tester, const MilerSheetScaffold(child: SizedBox(height: 40)), viewPadding: const EdgeInsets.only(bottom: 34), ); final padding = tester.widget( find.byType(AnimatedPadding).first, ); expect(padding.padding, const EdgeInsets.only(bottom: 34)); }); testWidgets('draws the shared handle once', (tester) async { await pump(tester, const MilerSheetScaffold(child: SizedBox(height: 40))); expect(find.byType(MilerSheetHandle), findsOneWidget); }); }); group('the migrated sheets carry the kit', () { testWidgets('duty', (tester) async { await pump( tester, DutySheet(goingOnline: true, onConfirm: () async => false), ); expect(find.byType(MilerSheetScaffold), findsOneWidget); expect(find.byType(MilerSheetHandle), findsOneWidget); }); testWidgets('the rung confirmation', (tester) async { ServiceProfile.setActive(ServiceProfile.milkMan); await pump( tester, StopActionSheet( stage: StopStatus.accepted, placeName: 'Vidhya Kitchen', count: 2, lines: const [ BagLine(stop: {}, orderId: 'A', customer: 'Joe', bag: 'Bag 1'), BagLine(stop: {}, orderId: 'B', customer: 'Arun', bag: 'Bag 2'), ], onConfirm: (_) async => null, ), ); expect(find.byType(MilerSheetScaffold), findsOneWidget); expect(find.byType(MilerSheetHandle), findsOneWidget); // The manifest is still the sheet's content — the kit wraps, it does // not swallow. expect(find.text('Joe'), findsOneWidget); expect(find.text('Bag 2'), findsOneWidget); }); }); group('the choice row', () { testWidgets('selection is a soft fill and a tick, not a slab', ( tester, ) async { await pump( tester, Material( child: Column( mainAxisSize: MainAxisSize.min, children: const [ MilerSheetChoiceRow(label: 'Wrong address', selected: true), MilerSheetChoiceRow(label: 'Out of area', selected: false), ], ), ), ); // The tick marks the chosen answer… expect(find.byIcon(LucideIcons.check), findsNWidgets(2)); final ticks = tester .widgetList(find.byType(AnimatedOpacity)) .map((w) => w.opacity) .toList(); expect(ticks, [ 1.0, 0.0, ], reason: 'the tick shows on the selected row and only there'); // …and no row is ever a full-strength primary slab. The fill that broke // this rule outshouted the destructive CTA under it. final fills = tester .widgetList(find.byType(AnimatedContainer)) .map((c) => (c.decoration as BoxDecoration?)?.color) .whereType(); expect(fills, isNot(contains(ColorConstants.primary))); }); }); }