Files
doormile_milderapp/test/active_pickup_banner_test.dart
2026-08-11 13:16:33 +05:30

160 lines
6.3 KiB
Dart

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<void> 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');
});
}