Files
doormile_milderapp/test/group_accept_count_test.dart
2026-08-28 11:13:15 +05:30

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