205 lines
7.4 KiB
Dart
205 lines
7.4 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/trip.dart';
|
||
import 'package:miler/views/Dashboard/home/trip_tabs.dart';
|
||
|
||
/// ─────────────────────────────────────────────────────────────────────────
|
||
/// THE SELECTOR STAYS PUT, AND IT STAYS ONE HEIGHT
|
||
///
|
||
/// This file used to guard a pinned `SliverPersistentHeader`: the delegate had
|
||
/// to declare a fixed extent, [TripSelector]'s content was type that grows with
|
||
/// the system font, and the two disagreed by a few pixels at large settings —
|
||
/// so `extentFor` derived an over-estimate and these tests measured it from
|
||
/// 1.0× to 2.0× to prove it was never short.
|
||
///
|
||
/// **None of that exists any more.** The selector moved into the brand header,
|
||
/// which is fixed by being the Column's first child rather than by being a
|
||
/// sliver, so there is no extent to declare and nothing to under-estimate.
|
||
///
|
||
/// What survives is the property the arithmetic was protecting, and it is now
|
||
/// the widget's own: the control is a **fixed height** that does not move with
|
||
/// the text scale. A header that grows a few points at 1.5× pushes the whole
|
||
/// page down and re-lays the route out under the rider's thumb. It cannot,
|
||
/// because nothing in the control is free-flowing type — the numeral is inside
|
||
/// a `FittedBox` that scales it down into the space it has.
|
||
/// ─────────────────────────────────────────────────────────────────────────
|
||
void main() {
|
||
Map<String, dynamic> stop(String id) => {
|
||
'orderid': id,
|
||
'pickupid': id,
|
||
'pickupcustomer': 'Sri Balaji Stores',
|
||
'pickupaddress': '19, Thadagam Road, Coimbatore',
|
||
'orderstatus': 'pending',
|
||
};
|
||
|
||
Future<void> pump(
|
||
WidgetTester tester, {
|
||
required int trips,
|
||
int selected = 0,
|
||
double textScale = 1.0,
|
||
}) async {
|
||
tester.view.physicalSize = const Size(390 * 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(textScaler: TextScaler.linear(textScale)),
|
||
child: child!,
|
||
),
|
||
home: Scaffold(
|
||
body: Padding(
|
||
padding: const EdgeInsets.all(16),
|
||
child: TripSelector(
|
||
trips: [
|
||
for (var i = 0; i < trips; i++)
|
||
Trip.fromStops(id: 't$i', stops: [stop('$i')]),
|
||
],
|
||
selectedIndex: selected,
|
||
onSelect: (_) {},
|
||
),
|
||
),
|
||
),
|
||
),
|
||
),
|
||
);
|
||
await tester.pump();
|
||
}
|
||
|
||
group('the control is one height at every text scale', () {
|
||
for (final scale in const [1.0, 1.3, 1.5, 2.0]) {
|
||
testWidgets('at ${scale}x', (tester) async {
|
||
await pump(tester, trips: 2, textScale: scale);
|
||
await tester.pump(const Duration(milliseconds: 400));
|
||
|
||
expect(
|
||
tester.getSize(find.byType(TripSelector)).height,
|
||
TripSelector.height,
|
||
reason:
|
||
'the header must not change height with the font, or the whole '
|
||
'route below it moves under the rider\'s thumb',
|
||
);
|
||
expect(tester.takeException(), isNull);
|
||
});
|
||
}
|
||
});
|
||
|
||
testWidgets('three slots render before the hub has filled them', (
|
||
tester,
|
||
) async {
|
||
await pump(tester, trips: 1);
|
||
for (final n in ['Trip 1', 'Trip 2', 'Trip 3']) {
|
||
expect(find.text(n), findsOneWidget);
|
||
}
|
||
});
|
||
|
||
testWidgets('exactly one segment is marked, and it is the selected one', (
|
||
tester,
|
||
) async {
|
||
await pump(tester, trips: 2, selected: 1);
|
||
await tester.pump(const Duration(milliseconds: 400));
|
||
|
||
final marks = find.byKey(TripSelector.markerKey);
|
||
expect(marks, findsOneWidget, reason: 'one trip is current, not two');
|
||
|
||
// The rule is a short centred mark under its pill rather than a full-width
|
||
// underline, so what has to hold is that it is centred on the *right*
|
||
// pill — not that it spans it.
|
||
final marker = tester.getRect(marks);
|
||
final second = tester.getRect(find.text('Trip 2'));
|
||
expect(
|
||
marker.center.dx,
|
||
closeTo(second.center.dx, 6),
|
||
reason: 'the rule sits under the pill it belongs to',
|
||
);
|
||
expect(
|
||
marker.top,
|
||
greaterThan(second.bottom),
|
||
reason: 'and under it, not through it',
|
||
);
|
||
});
|
||
|
||
testWidgets('the indicator is a rule, not a filled tab', (tester) async {
|
||
// The tabs sit straight on the brand ground: no card, no wash, no outline.
|
||
// Selection is the travelling rule and the ink weight, and nothing else.
|
||
await pump(tester, trips: 2);
|
||
await tester.pump(const Duration(milliseconds: 400));
|
||
|
||
final marker = tester.getSize(find.byKey(TripSelector.markerKey));
|
||
expect(
|
||
marker.height,
|
||
lessThanOrEqualTo(4),
|
||
reason: 'a 2-3dp rule, not a segment-sized fill',
|
||
);
|
||
});
|
||
|
||
testWidgets('a segment is tappable and reports its own index', (
|
||
tester,
|
||
) async {
|
||
int? chosen;
|
||
tester.view.physicalSize = const Size(390 * 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(
|
||
home: Scaffold(
|
||
body: Padding(
|
||
padding: const EdgeInsets.all(16),
|
||
child: TripSelector(
|
||
trips: [
|
||
Trip.fromStops(id: 't0', stops: [stop('0')]),
|
||
Trip.fromStops(id: 't1', stops: [stop('1')]),
|
||
],
|
||
selectedIndex: 0,
|
||
onSelect: (i) => chosen = i,
|
||
),
|
||
),
|
||
),
|
||
),
|
||
),
|
||
);
|
||
await tester.pump();
|
||
|
||
await tester.tap(find.text('Trip 2'));
|
||
expect(chosen, 1);
|
||
});
|
||
|
||
// ── What the panel holds when a slot is empty ──
|
||
//
|
||
// Nothing, as far as this control is concerned. [TripSelector] draws the tab
|
||
// strip and whatever body it is handed; Home hands it the selected trip's
|
||
// brief, and for a slot the hub has not filled that is the *same* brief built
|
||
// from an empty trip — `0 stops left` and an em dash under every figure. See
|
||
// `trip_unassigned_brief_test.dart`.
|
||
//
|
||
// So there is no "empty" state in here to test, and nothing in this control
|
||
// says `Not assigned yet` any more: that line made the panel a stub under a
|
||
// tab twice its height, and repeated a sentence the empty state below already
|
||
// carried.
|
||
group('with no body', () {
|
||
testWidgets('the control is just the tab strip', (tester) async {
|
||
await pump(tester, trips: 1, selected: 2);
|
||
|
||
expect(find.text('Not assigned yet'), findsNothing);
|
||
expect(
|
||
tester.getSize(find.byType(TripSelector)).height,
|
||
TripSelector.height,
|
||
);
|
||
expect(tester.takeException(), isNull);
|
||
});
|
||
});
|
||
}
|
||
|