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

170 lines
6.6 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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_progress_rail.dart';
/// The whole point of replacing the node rail is that route length no longer
/// changes the widget's shape. A hub can assign 6 stops or 40; the summary at
/// the top of Bookings has to look the same either way.
void main() {
Widget app(List<StopProgress> progress) {
return ScreenUtilInit(
designSize: const Size(390, 844),
builder: (_, _) => MaterialApp(
home: Scaffold(
body: SingleChildScrollView(
child: TripProgressRail(
progress: progress,
slotLabel: '10:00 AM – 1:00 PM',
remaining: const Duration(minutes: 130),
),
),
),
),
);
}
Future<void> pump(WidgetTester tester, List<StopProgress> progress) async {
tester.view.physicalSize = const Size(1170, 2532);
tester.view.devicePixelRatio = 3.0;
addTearDown(tester.view.resetPhysicalSize);
addTearDown(tester.view.resetDevicePixelRatio);
await tester.pumpWidget(app(progress));
await tester.pumpAndSettle();
}
List<StopProgress> route(int done, int skipped, int pending,
{bool current = true}) {
return [
for (var i = 0; i < done; i++) StopProgress.done,
if (current) StopProgress.current,
for (var i = 0; i < skipped; i++) StopProgress.skipped,
for (var i = 0; i < pending; i++) StopProgress.pending,
];
}
testWidgets('height does not grow with the number of stops',
(tester) async {
await pump(tester, route(2, 1, 3));
final short = tester.getSize(find.byType(TripProgressRail)).height;
await pump(tester, route(8, 3, 28)); // 40 stops
final long = tester.getSize(find.byType(TripProgressRail)).height;
expect(long, short,
reason: 'the summary must not stretch or squeeze with route length');
});
testWidgets('never overflows, even on a long route', (tester) async {
await pump(tester, route(20, 5, 60)); // 86 stops
expect(tester.takeException(), isNull);
});
// Position, skips and time left are ONE line now, not two ends of a Row.
// They are one sentence about one trip, and splitting them left a gap in the
// middle wide enough to read as two unrelated facts.
testWidgets('reports position, skips and the count', (tester) async {
await pump(tester, route(4, 2, 11)); // 18 stops, current is #5
expect(find.text('4 of 18 done'), findsOneWidget);
expect(
find.text('On stop 5 of 18 · 2 skipped · 2h 10m left, then hub'),
findsOneWidget,
);
});
testWidgets('drops the skip clause when nothing was skipped',
(tester) async {
await pump(tester, route(3, 0, 5));
expect(find.text('On stop 4 of 9 · 2h 10m left, then hub'), findsOneWidget);
});
// The card, its border, its shadow and its padding are gone — the rail sits
// straight on the page above the stop list. This is the assertion that keeps
// it that way: a future "let's give it an edge" change has to come here first.
testWidgets('draws no box of its own', (tester) async {
await pump(tester, route(4, 2, 11));
final boxes = tester
.widgetList<Container>(find.descendant(
of: find.byType(TripProgressRail),
matching: find.byType(Container),
))
.map((c) => c.decoration)
.whereType<BoxDecoration>();
for (final d in boxes) {
expect(d.border, isNull, reason: 'no bordered container inside the rail');
expect(d.boxShadow, anyOf(isNull, isEmpty),
reason: 'no elevation inside the rail');
}
});
testWidgets('a finished route says head back to the hub', (tester) async {
await pump(tester, route(6, 0, 0, current: false));
expect(find.text('All stops done'), findsOneWidget);
expect(find.text('Head back to the hub'), findsOneWidget);
});
// The rail numbers the stops the same way the list below it does, and the
// finished ones are the coloured ones. This is the whole point of the nodes.
testWidgets('draws a numbered node per stop, and one scooter', (tester) async {
await pump(tester, route(2, 0, 2)); // 5 stops, current is #3
// Every stop keeps its number, including the one he is on — the scooter
// rides above the node rather than replacing its digit.
for (final n in ['1', '2', '3', '4', '5']) {
expect(find.text(n), findsOneWidget, reason: 'stop $n is on the rail');
}
expect(find.byIcon(Icons.two_wheeler_rounded), findsOneWidget);
});
// The point of the overlay: finishing a stop *moves* the marker, visibly,
// instead of repainting two circles.
testWidgets('the scooter rides forward when a stop is completed',
(tester) async {
Finder scooter() => find.byIcon(Icons.two_wheeler_rounded);
await pump(tester, route(2, 0, 2)); // on stop 3 of 5
final from = tester.getCenter(scooter()).dx;
// Not `pump()` — that settles, and the whole assertion is about the frames
// in between.
await tester.pumpWidget(app(route(3, 0, 1))); // stop 3 done, now on 4
await tester.pump(const Duration(milliseconds: 120));
final moving = tester.getCenter(scooter()).dx;
await tester.pumpAndSettle();
final to = tester.getCenter(scooter()).dx;
expect(to, greaterThan(from), reason: 'it advanced one stop');
expect(moving, greaterThan(from));
expect(moving, lessThan(to), reason: 'it travelled rather than jumped');
});
testWidgets('a finished route parks the scooter', (tester) async {
await pump(tester, route(2, 0, 3, current: false)); // 5 stops, none active
// Nothing is `current`, so the first pending stop holds the scooter.
expect(find.byIcon(Icons.two_wheeler_rounded), findsOneWidget);
await pump(tester, route(5, 0, 0, current: false)); // all done
expect(find.byIcon(Icons.two_wheeler_rounded), findsNothing,
reason: 'nothing left to ride to');
});
// Long routes scroll rather than shrink — the failure that killed the first
// version of this rail was nodes being squeezed to fit a fixed width.
testWidgets('a long route scrolls instead of crushing its nodes',
(tester) async {
await pump(tester, route(4, 0, 35)); // 40 stops
expect(find.byType(SingleChildScrollView), findsWidgets);
expect(tester.takeException(), isNull);
});
testWidgets('the old per-stop hub labels are gone', (tester) async {
await pump(tester, route(2, 0, 4));
expect(find.text('START'), findsNothing);
expect(find.text('RETURN'), findsNothing);
});
}