first commit

This commit is contained in:
2026-08-11 13:16:33 +05:30
parent 18531bf46b
commit c8350563d9
388 changed files with 69656 additions and 0 deletions

View File

@@ -0,0 +1,243 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:image_picker_platform_interface/image_picker_platform_interface.dart';
import 'package:miler/views/Dashboard/pickups/stop_verify.dart';
/// The review step used to be untestable — "it sits behind a camera gate no
/// widget test can pass" — so the only thing held about it was the pure packing
/// rule in [packReviewFacts]. That left the screen itself unwatched, and it
/// broke: the fact rows were `Row(crossAxisAlignment: stretch)` inside a
/// `SingleChildScrollView`, which asks the row to stretch to an infinite height.
/// Layout threw, the exception propagated up through the leg panel's Column, and
/// the last screen of the flow — the one the rider signs — showed no figures at
/// all.
///
/// The gate is passable: [ImagePickerPlatform.instance] is the seam. Swapping in
/// a fake that returns a real file on disk lets the whole flow run to the review
/// and assert what the rider is actually shown.
class _FakeCamera extends ImagePickerPlatform {
final String path;
_FakeCamera(this.path);
@override
Future<XFile?> getImageFromSource({
required ImageSource source,
ImagePickerOptions options = const ImagePickerOptions(),
}) async => XFile(path);
}
void main() {
setUpAll(() async {
final dir = await Directory.systemTemp.createTemp('miler_review');
final png = File('${dir.path}/proof.png')
// A 1×1 PNG: Image.file needs bytes it can decode, not a real photo.
..writeAsBytesSync(const <int>[
137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, //
0, 0, 0, 1, 0, 0, 0, 1, 8, 6, 0, 0, 0, 31, 21, 196, 137,
0, 0, 0, 13, 73, 68, 65, 84, 120, 156, 99, 250, 207, 192, 240,
31, 0, 5, 5, 2, 0, 141, 88, 175, 91, 0, 0, 0, 0, 73, 69,
78, 68, 174, 66, 96, 130,
]);
ImagePickerPlatform.instance = _FakeCamera(png.path);
});
Future<void> pump(
WidgetTester tester,
Map<String, dynamic> stop, {
double width = 390,
double textScale = 1.0,
}) async {
tester.view.physicalSize = Size(width * 3, 844 * 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(
builder: (context, child) => MediaQuery(
data: MediaQuery.of(
context,
).copyWith(textScaler: TextScaler.linear(textScale)),
child: child!,
),
home: StopVerificationPage(pickup: stop),
),
),
);
await tester.pumpAndSettle();
}
/// Taps by visible label, scrolling it into view first — every one of these
/// controls sits below the fold on a phone.
Future<void> tap(WidgetTester tester, String label) async {
final target = find.text(label).first;
await tester.ensureVisible(target);
await tester.pumpAndSettle();
await tester.tap(target);
await tester.pumpAndSettle();
}
Future<void> type(WidgetTester tester, String hint, String value) async {
await tester.enterText(
find.byWidgetPredicate(
(w) => w is TextField && w.decoration?.hintText == hint,
),
value,
);
await tester.pumpAndSettle();
}
Map<String, dynamic> stopOf({
required String type,
int deliveryQty = 0,
int pickupQty = 0,
double cash = 0,
}) => <String, dynamic>{
'orderid': 'T-1',
'type': type,
'pickupcustomer': 'Priya Sharma',
'pickupaddress': '45, Lake View Apartments, Coimbatore',
if (deliveryQty > 0) 'deliveryqty': '$deliveryQty',
if (pickupQty > 0) 'pickupqty': '$pickupQty',
'Quantity': '${pickupQty > 0 ? pickupQty : deliveryQty}',
if (cash > 0) 'collectionamt': '$cash',
};
testWidgets('a pickup stop shows every collected figure on the review', (
tester,
) async {
await pump(tester, stopOf(type: 'pickup', pickupQty: 3, cash: 2450));
await type(tester, 'Total weight in kg', '4.5');
await tap(tester, 'Take photo');
// The pickup code is six boxes over one hidden field, not a hinted one.
await tester.enterText(find.byType(TextField).last, '482156');
await tester.pumpAndSettle();
await tap(tester, 'Review');
// The heading is the screen's own voice, not another section title.
expect(find.text('Check before you confirm'), findsOneWidget);
// Every figure the rider is signing for. Before the layout fix this whole
// block rendered as nothing.
expect(find.text('COLLECTED'), findsOneWidget);
expect(find.text('3 of 3'), findsOneWidget);
expect(find.text('4.5 kg'), findsOneWidget);
expect(find.text('Sealed & intact'), findsOneWidget);
expect(find.text('482156'), findsOneWidget);
// The photo is shown, not asserted as a tick the rider cannot check.
expect(find.byType(Image), findsWidgets);
expect(find.text('Tap to enlarge'), findsOneWidget);
// Money is flagged before the commit, not after it.
expect(
find.text('Collect ₹2450 from the customer on the next screen'),
findsOneWidget,
);
expect(find.text('Confirm pickup'), findsOneWidget);
});
testWidgets('a combined stop reviews both legs separately', (tester) async {
await pump(tester, stopOf(type: 'combined', deliveryQty: 2, pickupQty: 3));
// Hand over.
await tap(tester, 'Parcel 1 of 2');
await tap(tester, 'Parcel 2 of 2');
await tap(tester, 'Security');
await type(tester, "Person's name", 'Ramesh Kumar');
await tester.enterText(find.byType(TextField).last, '482156');
await tester.pumpAndSettle();
await tap(tester, 'Take photo');
await tap(tester, 'Continue to collect');
// Collect, with a damaged seal so the note takes its own wide row.
await tap(tester, 'Damaged');
await type(tester, 'Total weight in kg', '12.75');
await type(
tester,
'e.g. corner of the box is crushed',
'Corner of the second box is crushed',
);
await tap(tester, 'Take photo');
await tap(tester, 'Review');
// Two panels, each with its own facts and its own photo — the pairing is
// the whole reason both shots are taken.
expect(find.text('HANDED OVER'), findsOneWidget);
expect(find.text('2 of 2'), findsOneWidget);
expect(find.text('Ramesh Kumar · Security'), findsOneWidget);
expect(find.text('COLLECTED'), findsOneWidget);
expect(find.text('3 of 3'), findsOneWidget);
expect(find.text('12.75 kg'), findsOneWidget);
expect(find.text('Damaged'), findsOneWidget);
expect(find.text('Corner of the second box is crushed'), findsOneWidget);
expect(find.text('Tap to enlarge'), findsNWidgets(2));
// Both legs are complete, and each says so on itself — on a combined stop
// the page-level verdict does not say which job is short.
expect(find.text('Complete'), findsNWidgets(2));
// No pickup code on a combined stop: the delivery OTP already proved the
// door, so it is never collected and must not be reviewed.
expect(find.text('Pickup code'), findsNothing);
});
testWidgets('tapping a proof photo opens it full frame', (tester) async {
await pump(tester, stopOf(type: 'pickup', pickupQty: 1));
await type(tester, 'Total weight in kg', '4.5');
await tap(tester, 'Take photo');
await tester.enterText(find.byType(TextField).last, '482156');
await tester.pumpAndSettle();
await tap(tester, 'Review');
await tap(tester, 'Tap to enlarge');
expect(find.byType(InteractiveViewer), findsOneWidget);
});
// The review is a grid of fixed-width halves, which is the shape that
// overflows when the system font grows. Measured rather than eyeballed.
for (final scale in const [1.0, 1.3, 1.5, 2.0]) {
testWidgets('the review lays out at ${scale}x on a 360pt phone', (
tester,
) async {
await pump(
tester,
stopOf(type: 'combined', deliveryQty: 2, pickupQty: 3, cash: 2450),
width: 360,
textScale: scale,
);
await tap(tester, 'Parcel 1 of 2');
await tap(tester, 'Parcel 2 of 2');
await tap(tester, 'Security');
await type(tester, "Person's name", 'Ramesh Kumar Subramaniam');
await tester.enterText(find.byType(TextField).last, '482156');
await tester.pumpAndSettle();
await tap(tester, 'Take photo');
await tap(tester, 'Continue to collect');
await tap(tester, 'Damaged');
await type(tester, 'Total weight in kg', '12.75');
await type(
tester,
'e.g. corner of the box is crushed',
'Corner of the second box is crushed on the underside',
);
await tap(tester, 'Take photo');
await tap(tester, 'Review');
expect(find.text('HANDED OVER'), findsOneWidget);
expect(find.text('COLLECTED'), findsOneWidget);
expect(tester.takeException(), isNull);
});
}
}