miler map
This commit is contained in:
269
test/hub_pickup_group_test.dart
Normal file
269
test/hub_pickup_group_test.dart
Normal file
@@ -0,0 +1,269 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import 'package:miler/data/pickup_locations.dart';
|
||||
import 'package:miler/data/service_profile.dart';
|
||||
import 'package:miler/views/Dashboard/home/trip.dart';
|
||||
import 'package:miler/views/Dashboard/home/route_timeline.dart';
|
||||
import 'package:miler/views/Dashboard/home/trip_card.dart';
|
||||
|
||||
/// ─────────────────────────────────────────────────────────────────────────
|
||||
/// THE HUB IS A PICKUP LOCATION, THE SAME WAY A KITCHEN IS
|
||||
///
|
||||
/// A milk round draws its orders under the counter they are loaded at: one node
|
||||
/// naming the kitchen, its address under it, and the drops in a list beneath.
|
||||
/// A logistics route drew nothing of the kind — every stop was its own node —
|
||||
/// because grouping was one flag for the whole line
|
||||
/// ([ServiceProfile.sourceIsKitchen]).
|
||||
///
|
||||
/// That is right for a *collection*. The parcel line's pickups are one
|
||||
/// consignment per door and each genuinely is its own place. It is wrong for a
|
||||
/// **delivery**: a booking the rider hands over was loaded at the depot, which
|
||||
/// is the same relationship a kitchen has to a milk round. Six drops drew six
|
||||
/// nodes repeating one base address, and the fact that mattered — these come
|
||||
/// off one shelf, and here is that shelf — was the one thing missing.
|
||||
///
|
||||
/// So the question is asked per stop. A route that mixes gets both shapes.
|
||||
/// ─────────────────────────────────────────────────────────────────────────
|
||||
void main() {
|
||||
Map<String, dynamic> drop(String id, String who) => {
|
||||
'orderid': id,
|
||||
'pickupid': 'HUB',
|
||||
'pickuplocationid': 'HUB',
|
||||
'sourcename': 'Doormile Hub — Peelamedu',
|
||||
'pickupaddress': '4, Avinashi Road, Peelamedu, Coimbatore 641004',
|
||||
'pickupcustomer': who,
|
||||
'orderstatus': 'pending',
|
||||
'type': 'delivery',
|
||||
'Quantity': '1',
|
||||
};
|
||||
|
||||
Map<String, dynamic> collect(String id, String who) => {
|
||||
'orderid': id,
|
||||
'pickupid': id,
|
||||
'pickupcustomer': who,
|
||||
'pickupaddress': 'RS Puram, Coimbatore',
|
||||
'orderstatus': 'pending',
|
||||
'type': 'pickup',
|
||||
'Quantity': '1',
|
||||
};
|
||||
|
||||
setUp(() {
|
||||
PickupLocations.seedForTest(const {'7': 'H1 — Peelamedu Hub'});
|
||||
RiderHub.resetForTest(7);
|
||||
});
|
||||
|
||||
tearDown(() {
|
||||
ServiceProfile.setActive(ServiceProfile.parcel);
|
||||
RiderHub.resetForTest();
|
||||
PickupLocations.seedForTest(const {});
|
||||
});
|
||||
|
||||
test('the whole logistics run is one node, named by the rider\'s hub', () {
|
||||
ServiceProfile.setActive(ServiceProfile.parcel);
|
||||
final groups = TripCard(
|
||||
trip: Trip.fromStops(
|
||||
id: 't1',
|
||||
stops: [drop('1', 'Joe'), drop('2', 'Arun'), drop('3', 'Ravi')],
|
||||
),
|
||||
).routeGroups();
|
||||
|
||||
expect(groups, hasLength(1), reason: 'one hub, one node');
|
||||
|
||||
// ── The hub, not a booking's address ──
|
||||
//
|
||||
// A logistics day starts and ends at one building: it assigns the day's
|
||||
// bookings, hands over the route, and the rider accepts the lot before he
|
||||
// leaves. Reading the head off the stops titled it with whichever address
|
||||
// sorted first — right for a drop, which is loaded at the depot, and wrong
|
||||
// for a collection, which is not. See [RiderHub].
|
||||
expect(groups.single.name, 'H1 — Peelamedu Hub');
|
||||
expect(groups.single.stops, hasLength(3));
|
||||
expect(
|
||||
groups.single.flat,
|
||||
isFalse,
|
||||
reason: 'a place holding orders opens a list; it is not the stop itself',
|
||||
);
|
||||
|
||||
// No street under it. The locations table names the hub and gives no
|
||||
// address, and a booking's road printed there reads as the hub's own.
|
||||
expect(groups.single.location, isEmpty);
|
||||
});
|
||||
|
||||
test('a collection belongs to the hub run too', () {
|
||||
ServiceProfile.setActive(ServiceProfile.parcel);
|
||||
final groups = TripCard(
|
||||
trip: Trip.fromStops(
|
||||
id: 't1',
|
||||
stops: [collect('9', 'Sri Balaji Stores')],
|
||||
),
|
||||
).routeGroups();
|
||||
|
||||
expect(groups, hasLength(1));
|
||||
|
||||
// A collection is picked up at the customer's door, but it is still a
|
||||
// booking the hub handed this rider and it is accepted at the hub with the
|
||||
// rest. So it sits under the same head, with the customer on its own rung.
|
||||
expect(groups.single.flat, isFalse);
|
||||
expect(groups.single.name, 'H1 — Peelamedu Hub');
|
||||
expect(groups.single.manifest.single.customer, 'Sri Balaji Stores');
|
||||
});
|
||||
|
||||
test('a mixed route is still one hub run', () {
|
||||
ServiceProfile.setActive(ServiceProfile.parcel);
|
||||
final groups = TripCard(
|
||||
trip: Trip.fromStops(
|
||||
id: 't1',
|
||||
stops: [
|
||||
drop('1', 'Joe'),
|
||||
drop('2', 'Arun'),
|
||||
collect('9', 'Sri Balaji Stores'),
|
||||
],
|
||||
),
|
||||
).routeGroups();
|
||||
|
||||
// Drops and collections alike: one building assigned them, so one node
|
||||
// holds them and each rung says which job it is — see the `D`/`P`/`P&D`
|
||||
// tags in `route_timeline.dart`.
|
||||
expect(groups, hasLength(1));
|
||||
expect(groups.single.name, 'H1 — Peelamedu Hub');
|
||||
expect(groups.single.stops, hasLength(3));
|
||||
});
|
||||
|
||||
test('a milk round is unchanged — it grouped on the line, and still does', () {
|
||||
ServiceProfile.setActive(ServiceProfile.milkMan);
|
||||
final groups = TripCard(
|
||||
trip: Trip.fromStops(
|
||||
id: 't1',
|
||||
stops: [
|
||||
{
|
||||
'orderid': '1',
|
||||
'pickupid': 'K1',
|
||||
'kitchenid': 'K1',
|
||||
'kitchenname': 'Vidhya Kitchen',
|
||||
'pickupaddress': '12, SNS Colony, Peelamedu',
|
||||
'pickupcustomer': 'Joe Mathew',
|
||||
'orderstatus': 'pending',
|
||||
},
|
||||
{
|
||||
'orderid': '2',
|
||||
'pickupid': 'K1',
|
||||
'kitchenid': 'K1',
|
||||
'kitchenname': 'Vidhya Kitchen',
|
||||
'pickupaddress': '12, SNS Colony, Peelamedu',
|
||||
'pickupcustomer': 'Arun Prakash',
|
||||
'orderstatus': 'pending',
|
||||
},
|
||||
],
|
||||
),
|
||||
).routeGroups();
|
||||
|
||||
expect(groups, hasLength(1));
|
||||
expect(groups.single.name, 'Vidhya Kitchen');
|
||||
expect(groups.single.stops, hasLength(2));
|
||||
});
|
||||
|
||||
// ── The run-level select-all survives a mixed route ──
|
||||
//
|
||||
// `_flatSelectIds` bailed on `groups.every((g) => g.flat)`, which was right
|
||||
// while a route was one shape or the other. Grouping the drops made a
|
||||
// logistics route *both*, so the guard removed the only way to take the
|
||||
// collections in one go — on exactly the routes that have the most of them.
|
||||
group('taking the collections in one go', () {
|
||||
Future<void> pump(
|
||||
WidgetTester tester,
|
||||
List<Map<String, dynamic>> stops, {
|
||||
Set<String> selected = const {},
|
||||
}) async {
|
||||
tester.view.physicalSize = const Size(390 * 3, 900 * 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: SingleChildScrollView(
|
||||
child: TripCard(
|
||||
trip: Trip.fromStops(id: 't1', stops: stops),
|
||||
selectedIds: selected,
|
||||
onToggleSelect: (_) {},
|
||||
onSelectAll: (_) {},
|
||||
).buildStops(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
|
||||
testWidgets('a logistics run has one, and it is the hub\'s', (
|
||||
tester,
|
||||
) async {
|
||||
ServiceProfile.setActive(ServiceProfile.parcel);
|
||||
await pump(
|
||||
tester,
|
||||
[
|
||||
drop('1', 'Joe'),
|
||||
drop('2', 'Arun'),
|
||||
collect('8', 'Sri Balaji Stores'),
|
||||
collect('9', 'Kumar Traders'),
|
||||
],
|
||||
// The head control only appears once something is ticked — it is a
|
||||
// selection-mode helper, not a permanent row.
|
||||
selected: const {'8'},
|
||||
);
|
||||
|
||||
// ── One control, over the whole run ──
|
||||
//
|
||||
// The rider accepts the day's bookings at the hub before he leaves it,
|
||||
// and every booking is under the hub's node — so there is one select-all
|
||||
// and it covers the lot. The run-level control that sits outside the
|
||||
// groups is for stops with no place above them to hold one, and a
|
||||
// logistics run has none.
|
||||
expect(find.byType(SelectAllAction), findsOneWidget);
|
||||
expect(find.text('1 of 4 selected'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('a milk round is unchanged — it has no flat groups', (
|
||||
tester,
|
||||
) async {
|
||||
ServiceProfile.setActive(ServiceProfile.milkMan);
|
||||
await pump(
|
||||
tester,
|
||||
[
|
||||
{
|
||||
'orderid': '1',
|
||||
'pickupid': 'K1',
|
||||
'kitchenid': 'K1',
|
||||
'kitchenname': 'Vidhya Kitchen',
|
||||
'pickupaddress': '12, SNS Colony, Peelamedu',
|
||||
'pickupcustomer': 'Joe Mathew',
|
||||
'orderstatus': 'pending',
|
||||
},
|
||||
{
|
||||
'orderid': '2',
|
||||
'pickupid': 'K1',
|
||||
'kitchenid': 'K1',
|
||||
'kitchenname': 'Vidhya Kitchen',
|
||||
'pickupaddress': '12, SNS Colony, Peelamedu',
|
||||
'pickupcustomer': 'Arun Prakash',
|
||||
'orderstatus': 'pending',
|
||||
},
|
||||
],
|
||||
selected: const {'1'},
|
||||
);
|
||||
|
||||
// Every group is a counter, so there is nothing for a run-level control
|
||||
// to be over: the select-all belongs to the kitchen and there is exactly
|
||||
// **one** of it. Before the fix this was also one, and it must stay one —
|
||||
// the guard that was removed never applied to a route with no flat groups.
|
||||
expect(find.byType(SelectAllAction), findsOneWidget);
|
||||
expect(find.text('1 of 2 selected'), findsOneWidget);
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user