Files
doormile_milderapp/test/sheet_kit_test.dart
2026-08-28 15:07:30 +05:30

179 lines
6.5 KiB
Dart

import 'package:flutter/material.dart';
import 'package:lucide_icons_flutter/lucide_icons.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:miler/Models/stop_status.dart';
import 'package:miler/data/order_manifest.dart';
import 'package:miler/data/service_profile.dart';
import 'package:miler/views/Dashboard/home/duty_sheet.dart';
import 'package:miler/views/Dashboard/home/stop_action_sheet.dart';
import 'package:miler/views/helpers/constants/Colorconstants.dart';
import 'package:miler/views/helpers/widgets/miler_sheet_kit.dart';
/// ─────────────────────────────────────────────────────────────────────────
/// THE SHEET KIT — the rules, held
///
/// The kit exists because six presentation styles had grown around one kind
/// of surface (see the audit in `miler_sheet_kit.dart`). What stops a seventh
/// growing back is not the kit's existence but these tests: the shared handle
/// on the sheets a rider actually uses, and the one inset rule that prevents
/// the double-SafeArea CTA drift from returning.
/// ─────────────────────────────────────────────────────────────────────────
void main() {
tearDown(() => ServiceProfile.setActive(ServiceProfile.parcel));
Future<void> pump(
WidgetTester tester,
Widget child, {
EdgeInsets viewInsets = EdgeInsets.zero,
EdgeInsets viewPadding = EdgeInsets.zero,
}) async {
tester.view.physicalSize = const Size(1170, 2532);
tester.view.devicePixelRatio = 3.0;
addTearDown(tester.view.reset);
await tester.pumpWidget(
ScreenUtilInit(
designSize: const Size(390, 844),
builder: (_, _) => MaterialApp(
home: MediaQuery(
data: MediaQueryData(
size: const Size(390, 844),
viewInsets: viewInsets,
viewPadding: viewPadding,
),
// NOT a Scaffold: `resizeToAvoidBottomInset` consumes viewInsets
// before the sheet sees them, which is exactly the environment a
// modal route does NOT provide — the sheet must read the keyboard
// itself, which is the behaviour under test.
child: Material(
color: Colors.black,
child: Align(alignment: Alignment.bottomCenter, child: child),
),
),
),
),
);
await tester.pump();
}
group('the scaffold', () {
testWidgets('pays the larger of keyboard and gesture bar, never both', (
tester,
) async {
// A gesture-bar phone with the keyboard up. The old double-SafeArea bug
// paid 300 + 34; the rule is max(300, 34).
await pump(
tester,
const MilerSheetScaffold(child: SizedBox(height: 40)),
viewInsets: const EdgeInsets.only(bottom: 300),
viewPadding: const EdgeInsets.only(bottom: 34),
);
final padding = tester.widget<AnimatedPadding>(
find.byType(AnimatedPadding).first,
);
expect(
padding.padding,
const EdgeInsets.only(bottom: 300),
reason:
'the keyboard replaces the gesture bar — summing them floats '
'the CTA a bar height above the keyboard',
);
});
testWidgets('with no keyboard the gesture bar is still paid', (
tester,
) async {
await pump(
tester,
const MilerSheetScaffold(child: SizedBox(height: 40)),
viewPadding: const EdgeInsets.only(bottom: 34),
);
final padding = tester.widget<AnimatedPadding>(
find.byType(AnimatedPadding).first,
);
expect(padding.padding, const EdgeInsets.only(bottom: 34));
});
testWidgets('draws the shared handle once', (tester) async {
await pump(tester, const MilerSheetScaffold(child: SizedBox(height: 40)));
expect(find.byType(MilerSheetHandle), findsOneWidget);
});
});
group('the migrated sheets carry the kit', () {
testWidgets('duty', (tester) async {
await pump(
tester,
DutySheet(goingOnline: true, onConfirm: () async => false),
);
expect(find.byType(MilerSheetScaffold), findsOneWidget);
expect(find.byType(MilerSheetHandle), findsOneWidget);
});
testWidgets('the rung confirmation', (tester) async {
ServiceProfile.setActive(ServiceProfile.milkMan);
await pump(
tester,
StopActionSheet(
stage: StopStatus.accepted,
placeName: 'Vidhya Kitchen',
count: 2,
lines: const [
ManifestLine(stop: {}, orderId: 'A', customer: 'Joe', label: 'Bag 1'),
ManifestLine(stop: {}, orderId: 'B', customer: 'Arun', label: 'Bag 2'),
],
onConfirm: (_) async => null,
),
);
expect(find.byType(MilerSheetScaffold), findsOneWidget);
expect(find.byType(MilerSheetHandle), findsOneWidget);
// The manifest is still the sheet's content — the kit wraps, it does
// not swallow.
expect(find.text('Joe'), findsOneWidget);
expect(find.text('Bag 2'), findsOneWidget);
});
});
group('the choice row', () {
testWidgets('selection is a soft fill and a tick, not a slab', (
tester,
) async {
await pump(
tester,
Material(
child: Column(
mainAxisSize: MainAxisSize.min,
children: const [
MilerSheetChoiceRow(label: 'Wrong address', selected: true),
MilerSheetChoiceRow(label: 'Out of area', selected: false),
],
),
),
);
// The tick marks the chosen answer…
expect(find.byIcon(LucideIcons.check), findsNWidgets(2));
final ticks = tester
.widgetList<AnimatedOpacity>(find.byType(AnimatedOpacity))
.map((w) => w.opacity)
.toList();
expect(ticks, [
1.0,
0.0,
], reason: 'the tick shows on the selected row and only there');
// …and no row is ever a full-strength primary slab. The fill that broke
// this rule outshouted the destructive CTA under it.
final fills = tester
.widgetList<AnimatedContainer>(find.byType(AnimatedContainer))
.map((c) => (c.decoration as BoxDecoration?)?.color)
.whereType<Color>();
expect(fills, isNot(contains(ColorConstants.primary)));
});
});
}