215 lines
7.6 KiB
Dart
215 lines
7.6 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:get/get.dart';
|
|
|
|
import 'package:miler/data/service_profile.dart';
|
|
import 'package:miler/views/Dashboard/home/route_timeline.dart';
|
|
import 'package:miler/views/Dashboard/home/trip.dart';
|
|
import 'package:miler/views/Dashboard/home/trip_card.dart';
|
|
|
|
/// ─────────────────────────────────────────────────────────────────────────
|
|
/// `Accepted 1/10`, NOT `Accepted 1/63`
|
|
///
|
|
/// The kitchen card's state chip is a fraction: how many of this counter's
|
|
/// orders the rider has taken, out of how many there are. The numerator was
|
|
/// right and the denominator was the whole day.
|
|
///
|
|
/// ── What it was counting ──
|
|
///
|
|
/// [RouteGroup.allStops] is every order the trip ever bucketed under a source,
|
|
/// and `groupSize` was its raw length. That deliberately includes orders the
|
|
/// rider has **collected** — they left the visible list but not the counter,
|
|
/// and renumbering the place under him is how a rider stops trusting a number.
|
|
///
|
|
/// It also included every order already **delivered** and every one he had
|
|
/// **declined**, which are Activity's business and not this card's. Off a
|
|
/// kitchen that supplies a rider all day that is most of the bucket: ten orders
|
|
/// in the dropdown, one accepted, and a chip reading `Accepted 1/63` — a
|
|
/// fraction whose denominator appears nowhere on the screen.
|
|
///
|
|
/// The load is now stated the way the rider would state it: **what is still in
|
|
/// the dropdown, plus what he has already taken off this counter**.
|
|
/// ─────────────────────────────────────────────────────────────────────────
|
|
void main() {
|
|
tearDown(() {
|
|
Get.reset();
|
|
ServiceProfile.setActive(ServiceProfile.parcel);
|
|
});
|
|
|
|
/// One order off Vidhya Kitchen. [status] is what the backend calls it, which
|
|
/// is what `stopStateOf` reads to place it on the ladder.
|
|
Map<String, dynamic> order(int id, {required String status}) =>
|
|
<String, dynamic>{
|
|
'orderid': 'M-$id',
|
|
'pickupid': id,
|
|
'step': id,
|
|
'kitchenname': 'Vidhya Kitchen',
|
|
'sourceid': 'K1',
|
|
'pickupcustomer': 'Customer $id',
|
|
'pickupaddress': '9 Kitchen Street, Coimbatore',
|
|
'dropaddress': 'Door $id, Peelamedu, Coimbatore',
|
|
'orderstatus': status,
|
|
'type': 'delivery',
|
|
'pickuplat': 11.01,
|
|
'pickuplon': 76.95,
|
|
};
|
|
|
|
/// The card's own grouping, run the way the screen runs it.
|
|
List<RouteGroup> groupsFor(
|
|
List<Map<String, dynamic>> stops, {
|
|
Set<String> accepted = const {},
|
|
Set<String> collected = const {},
|
|
}) {
|
|
ServiceProfile.setActive(ServiceProfile.milkMan);
|
|
return TripCard(
|
|
trip: Trip.fromStops(id: 'T1', stops: stops),
|
|
acceptedIds: accepted,
|
|
collectedIds: collected,
|
|
).routeGroups();
|
|
}
|
|
|
|
group('the denominator is the counter, not the day', () {
|
|
test('ten in the dropdown and one accepted reads 1 of 10', () {
|
|
// The reported case, at the size it was reported: one kitchen, ten
|
|
// orders still to decide, and fifty-three already delivered today.
|
|
final stops = <Map<String, dynamic>>[
|
|
for (var i = 1; i <= 10; i++) order(i, status: 'assigned'),
|
|
for (var i = 11; i <= 63; i++) order(i, status: 'delivered'),
|
|
];
|
|
|
|
final g = groupsFor(stops, accepted: {'M-1'}).single;
|
|
|
|
expect(g.stops.length, 10, reason: 'the dropdown holds ten');
|
|
expect(g.allStops.length, 63, reason: 'the bucket still holds the day');
|
|
expect(g.onRungCount, 1);
|
|
expect(
|
|
g.groupSize,
|
|
10,
|
|
reason: 'fifty-three delivered orders are Activity\'s, not this card\'s',
|
|
);
|
|
});
|
|
|
|
test('a collected order keeps its place in the arithmetic', () {
|
|
// The original rationale, which must survive the fix: three bags are in
|
|
// the rider's box and off Home, but they came off this counter and the
|
|
// place still holds six.
|
|
final stops = <Map<String, dynamic>>[
|
|
for (var i = 1; i <= 6; i++) order(i, status: 'assigned'),
|
|
];
|
|
|
|
final g = groupsFor(
|
|
stops,
|
|
accepted: {'M-1', 'M-2', 'M-3'},
|
|
collected: {'M-1', 'M-2', 'M-3'},
|
|
).single;
|
|
|
|
expect(g.stops.length, 3, reason: 'the collected three left Home');
|
|
expect(
|
|
g.groupSize,
|
|
6,
|
|
reason: 'renumbering the counter under him is the bug this prevents',
|
|
);
|
|
});
|
|
|
|
test('a declined order drops out of the load', () {
|
|
final stops = <Map<String, dynamic>>[
|
|
for (var i = 1; i <= 4; i++) order(i, status: 'assigned'),
|
|
order(5, status: 'rejected'),
|
|
];
|
|
|
|
final g = groupsFor(stops).single;
|
|
|
|
expect(g.groupSize, 4, reason: 'he has finished with the declined one');
|
|
});
|
|
|
|
test('nothing delivered means the two counts agree', () {
|
|
final stops = <Map<String, dynamic>>[
|
|
for (var i = 1; i <= 5; i++) order(i, status: 'assigned'),
|
|
];
|
|
|
|
final g = groupsFor(stops).single;
|
|
|
|
expect(g.groupSize, 5);
|
|
expect(g.groupSize, g.allStops.length);
|
|
});
|
|
|
|
test('a caller that tracks no states gets the old arithmetic', () {
|
|
// Better to over-count than to invent a denominator: a hand-built group
|
|
// with no `allStopStates` must not silently report zero.
|
|
const g = RouteGroup(
|
|
key: 'K1',
|
|
name: 'Vidhya Kitchen',
|
|
stops: [],
|
|
allStops: [{}, {}, {}],
|
|
firstIndex: 0,
|
|
state: StopState.pending,
|
|
stopStates: [],
|
|
);
|
|
|
|
expect(g.groupSize, 3);
|
|
});
|
|
});
|
|
|
|
group('the chip on the card says the same number', () {
|
|
Future<void> pump(
|
|
WidgetTester tester,
|
|
List<Map<String, dynamic>> stops, {
|
|
Set<String> accepted = const {},
|
|
}) async {
|
|
ServiceProfile.setActive(ServiceProfile.milkMan);
|
|
tester.view.physicalSize = const Size(390 * 3, 1600 * 3);
|
|
tester.view.devicePixelRatio = 3;
|
|
addTearDown(tester.view.reset);
|
|
|
|
await tester.pumpWidget(
|
|
ScreenUtilInit(
|
|
designSize: const Size(390, 844),
|
|
builder: (_, _) => MaterialApp(
|
|
home: Scaffold(
|
|
body: SingleChildScrollView(
|
|
child: TripCard(
|
|
trip: Trip.fromStops(id: 'T1', stops: stops),
|
|
acceptedIds: accepted,
|
|
onToggleSelect: (_) {},
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
await tester.pumpAndSettle();
|
|
}
|
|
|
|
testWidgets('the accepted chip is a fraction of the dropdown', (
|
|
tester,
|
|
) async {
|
|
await pump(tester, [
|
|
for (var i = 1; i <= 10; i++) order(i, status: 'assigned'),
|
|
for (var i = 11; i <= 63; i++) order(i, status: 'delivered'),
|
|
], accepted: {'M-1'});
|
|
|
|
expect(find.text('Accepted 1/10'), findsOneWidget);
|
|
expect(
|
|
find.text('Accepted 1/63'),
|
|
findsNothing,
|
|
reason: 'the day is not the counter',
|
|
);
|
|
});
|
|
|
|
testWidgets('a single-order place states no fraction at all', (
|
|
tester,
|
|
) async {
|
|
// `Accepted (1 of 1)` is noise, and the guard for it reads `groupSize`.
|
|
await pump(
|
|
tester,
|
|
[order(1, status: 'assigned')],
|
|
accepted: {'M-1'},
|
|
);
|
|
|
|
expect(find.text('Accepted'), findsOneWidget);
|
|
expect(find.textContaining('Accepted 1/'), findsNothing);
|
|
});
|
|
});
|
|
}
|