import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:miler/views/Dashboard/home/homepage_banner.dart'; import 'package:miler/widget/Bottom_page.dart'; /// The live strip at the foot of Home and Bookings. /// /// ── The bug being pinned ── /// /// Both pages pinned it to `bottom: 0`, and the nav bar covered it. The bar /// *floats* — a `Positioned` pill inside the shell's `Stack` rather than a /// `bottomNavigationBar` — so the Scaffold reserves no room for it and /// `SafeArea` cannot see it. Nothing in the type system says "you are about to /// draw underneath the navigation", which is why it survived on two screens at /// once, so it is measured here instead. void main() { const safeBottom = 34.0; // a home-indicator phone, where it is worst Future pump( WidgetTester tester, { double width = 390, double textScale = 1.0, int count = 1, String address = '19, Thadagam Road, Venkitapuram, Coimbatore 641025', }) async { tester.view.physicalSize = Size(width * 3, 844 * 3); tester.view.devicePixelRatio = 3.0; addTearDown(tester.view.resetPhysicalSize); addTearDown(tester.view.resetDevicePixelRatio); await tester.pumpWidget( ScreenUtilInit( designSize: const Size(390, 844), builder: (_, _) => MaterialApp( builder: (context, child) => MediaQuery( data: MediaQuery.of(context).copyWith( padding: const EdgeInsets.only(bottom: safeBottom), textScaler: TextScaler.linear(textScale), ), child: child!, ), home: Scaffold( body: Stack( children: [ const Positioned.fill(child: ColoredBox(color: Colors.white)), // Exactly how both pages mount it. Positioned( left: 0, right: 0, bottom: 0, child: ActivePickupBanner( activePickups: [ for (var i = 0; i < count; i++) { 'orderid': 'ORD${1000 + i}', 'pickupaddress': address, 'orderstatus': 'arrived', }, ], onTap: (_) {}, ), ), ], ), ), ), ), ); // Not `pumpAndSettle`: the LIVE dot pulses forever by design, so nothing on // this widget ever settles. Two pumps past the longest transition is enough. await tester.pump(); await tester.pump(const Duration(milliseconds: 400)); } /// Gap between the bottom of the visible card and the bottom of the screen. double gapBelowCard(WidgetTester tester) { final screen = tester.view.physicalSize.height / tester.view.devicePixelRatio; return screen - tester.getRect(find.byType(Material).last).bottom; } testWidgets('clears the floating nav bar while the bar is showing', (tester) async { await pump(tester); final context = tester.element(find.byType(ActivePickupBanner)); final required = BottomPage.bottomInset(context); expect(gapBelowCard(tester), greaterThanOrEqualTo(required), reason: 'the card must sit above the nav bar, which claims ' '${required.toStringAsFixed(1)}pt at the foot of the page. It was ' 'pinned to bottom: 0 and the bar covered it.'); }); testWidgets('sits at one height rather than chasing the bar', (tester) async { // The banner used to follow a `navVisible` notifier down when the bar slid // off on scroll and back up when it returned. The bar compacts now instead // of leaving, so there is no state in which that room is free — and a // banner that re-laid itself out on every change of scroll direction was // moving for a 20pt gap the rider would never have noticed. await pump(tester); final resting = gapBelowCard(tester); await tester.pump(const Duration(milliseconds: 400)); expect(gapBelowCard(tester), closeTo(resting, 0.5)); final context = tester.element(find.byType(ActivePickupBanner)); expect(resting, greaterThanOrEqualTo(BottomPage.bottomInset(context)), reason: 'it must clear the bar at its resting height, which is the ' 'height the bar returns to the moment he scrolls back up'); }); testWidgets('lines up with the nav bar rather than running full width', (tester) async { await pump(tester); final card = tester.getRect(find.byType(Material).last); // The nav bar sits on a 16 gutter; the banner stacks directly under it. expect(card.left, closeTo(16.w, 1)); expect(390 - card.right, closeTo(16.w, 1)); }); group('lays out without overflowing', () { for (final width in [320.0, 360.0, 390.0, 430.0]) { for (final scale in [1.0, 1.3, 1.5, 2.0]) { testWidgets('at ${width.toInt()}pt / ${scale}x', (tester) async { // A long address plus a plural title is the widest this row ever gets, // and the title used to sit unflexed beside a fixed-width LIVE chip. await pump( tester, width: width, textScale: scale, count: 3, address: '19/4B, Thadagam Main Road, Venkitapuram, Coimbatore, ' 'Tamil Nadu 641025', ); expect(tester.takeException(), isNull, reason: 'the banner overflowed at ${width.toInt()}pt / ${scale}x'); }); } } }); testWidgets('says nothing when there is nothing running', (tester) async { await pump(tester, count: 0); expect(find.text('Active Pickup'), findsNothing); expect(find.text('LIVE'), findsNothing); }); testWidgets('names the count and the stop status', (tester) async { await pump(tester, count: 3); expect(find.text('3 Active Pickups'), findsOneWidget); expect(find.textContaining('At the stop'), findsOneWidget, reason: 'the rider should see how far into the stop he already is. The ' 'wording comes from StopStatusX.label, the one place the app names ' 'its statuses — and it is "At the stop" rather than the banner\'s ' 'old private "At pickup", which was wrong on a delivery'); }); }