import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:miler/data/service_profile.dart'; import 'package:miler/helpers/rider_shell.dart'; import 'package:miler/widget/Bottom_page.dart' as parcel; /// ───────────────────────────────────────────────────────────────────────── /// THE XPRESS SUBTREE IS A REFERENCE, NOT A DESTINATION /// /// `lib/xpress` is a verbatim port of the Xpress-rider app, kept beside this /// one so its proven delivery UX can be read while the Miler-native milk run is /// built. It is **not** one of the two operations the hub runs, and the Milk Man /// flow is implemented natively in Miler — Miler screens, Miler controllers, /// Miler API layer — rather than by routing riders into it. /// /// ── What this file used to assert, and why it changed ── /// /// It used to prove the opposite: that a tenant *named* "delivery"/"xpress", or /// an account in `configid: 6`, would land the rider in that other application. /// That was reasonable while the tenant was unknowable — the build flag and a /// name were the only signals there were. /// /// It is not reasonable now. `verify-pin` returns a real `tenantname`, which /// means an arbitrary string chosen by whoever set up a client in the admin /// console reaches this resolver. A client called "Delivery" is an entirely /// plausible thing to create, and under the old rule that alone would have /// dropped a live rider into a different app with a different bottom bar, a /// different set of endpoints and no shipment desk. The failure would look, to /// him, like the app breaking. /// /// So the wire cannot reach it any more. Only `--dart-define=TENANT=delivery` /// can, and that is a compile-time constant no test can set — which is the /// point. What is tested here is the closed door. /// ───────────────────────────────────────────────────────────────────────── void main() { TestWidgetsFlutterBinding.ensureInitialized(); Future resolveWith(Map prefs) async { SharedPreferences.setMockInitialValues(prefs); final profile = await resolveServiceProfile(); ServiceProfile.setActive(profile); return profile; } tearDown(() => ServiceProfile.setActive(ServiceProfile.parcel)); group('the wire cannot reach the ported line', () { test('no tenant name routes a rider out of Miler', () async { // Every name that used to open it. Each one must now resolve to one of // the two real operations — in practice logistics, the safe fallback. for (final name in [ 'delivery', 'xpress', 'express', 'doormile xpress', 'doormilexpress', ' XPress ', ]) { final p = await resolveWith({'tenantname': name}); expect( p.isDelivery, isFalse, reason: 'tenantname=$name must not open the ported app', ); expect(p.line, ServiceLine.parcel, reason: 'tenantname=$name'); } }); test('no tenant id routes a rider out of Miler', () async { expect(TenantController.deliveryTenantIds, isEmpty); for (final id in [1, 6, 99, 1001]) { final p = await resolveWith({'tenantid': id}); expect(p.isDelivery, isFalse, reason: 'tenantid=$id'); } }); test('configid 6 no longer decides anything', () async { // Xpress-rider signed its riders into partition 6, and this resolver used // to read that as "give him the other app". A partition is an account // location, not a statement about which business a rider works for, and // it was the weakest of the three signals — so it is no longer consulted. final p = await resolveWith({'configid': 6}); expect(p.line, ServiceLine.parcel); }); testWidgets('and no resolvable tenant produces the Xpress shell', ( tester, ) async { for (final prefs in >[ {'tenantname': 'xpress'}, {'tenantname': 'delivery'}, {'configid': 6}, {'tenantid': 6}, {}, ]) { await resolveWith(prefs); expect(riderShell(), isA(), reason: '$prefs'); } }); }); group('the two operations the hub actually runs', () { test('a milk-man tenant resolves to the milk run', () async { final p = await resolveWith({'tenantname': 'milkman'}); expect(p.line, ServiceLine.milkMan); expect(p.isMilkMan, isTrue); }); test('a logistics tenant resolves to logistics', () async { final p = await resolveWith({'tenantname': 'logistics'}); expect(p.line, ServiceLine.parcel); expect(p.isLogistics, isTrue); }); test('an unknown tenant falls back to logistics', () async { final p = await resolveWith({'tenantname': 'someclient', 'tenantid': 99}); expect(p.line, ServiceLine.parcel); }); }); group('both operations share one Miler dashboard', () { testWidgets('logistics gets it', (tester) async { await resolveWith({'tenantname': 'logistics'}); expect(riderShell(), isA()); }); testWidgets('the milk run gets it too', (tester) async { // Milk Man is a capability variant of the Miler screens, not a separate // app — which is the whole point of §2 of the brief. await resolveWith({'tenantname': 'milkman'}); expect(riderShell(), isA()); }); testWidgets('the requested tab is carried through', (tester) async { await resolveWith({'tenantname': 'milkman'}); expect( (riderShell(initialIndex: 2) as parcel.BottomPage).initialIndex, 2, ); await resolveWith({'tenantname': 'logistics'}); expect( (riderShell(initialIndex: 2) as parcel.BottomPage).initialIndex, 2, ); }); testWidgets('an override page is honoured', (tester) async { const marker = SizedBox.shrink(); await resolveWith({'tenantname': 'milkman'}); expect( (riderShell(overridePage: marker) as parcel.BottomPage).overridePage, same(marker), ); await resolveWith({'tenantname': 'logistics'}); expect( (riderShell(overridePage: marker) as parcel.BottomPage).overridePage, same(marker), ); }); }); group('the ported profile still describes itself honestly', () { // The profile object survives even though nothing off the wire selects it: // the build flag still reaches it, and a profile that lies about the work // would be worse than one nobody uses. test('names the work Doormile, never the app it was ported from', () { const p = ServiceProfile.delivery; expect(p.label, 'Doormile'); expect(p.label.toLowerCase(), isNot(contains('nearle'))); expect(p.label.toLowerCase(), isNot(contains('xpress'))); }); test('speaks in deliveries, not bookings', () { const p = ServiceProfile.delivery; expect(p.jobNoun, 'delivery'); expect(p.workTabLabel, 'Deliveries'); expect(p.completionVerb, 'Delivered'); }); test('is not the milk run in disguise', () { const p = ServiceProfile.delivery; expect(p.collectsCash, isTrue); expect(p.acceptsPerStop, isTrue); expect(p.sourceIsKitchen, isFalse); expect(p.isMilkMan, isFalse); // And it does not carry the milk run's hinge: START DELIVERY belongs to // the line that collects a whole load before it drives. expect(p.startsDeliveryAfterFullLoad, isFalse); }); }); }