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,228 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:miler/views/Dashboard/profile/informations/faq.dart';
import 'package:miler/views/helpers/constants/Colorconstants.dart';
import 'package:miler/views/helpers/constants/miler_type.dart';
import 'package:miler/views/helpers/widgets/app_widgets.dart';
/// The pages you can navigate *into* from Account.
///
/// ── Why these needed a test ──
///
/// Eight pushed pages had six different app bars between them: four solid brand
/// red with white text, two white with black text, one grey, one white with a
/// red title. The back arrow came in four different icons at four sizes in three
/// colours. Two shared helpers already existed and, between them, were used by
/// three pages.
///
/// And the FAQ page **overflowed at any text scale at or above 1.5** — three
/// separate places, on every screen width — which is a black-and-yellow band
/// across a help page for the riders most likely to need help reading it.
///
/// Both classes of bug are invisible in a screenshot taken at default settings
/// on the developer's own phone, so both are measured here instead.
void main() {
Future<void> pump(
WidgetTester tester,
Widget page, {
double width = 390,
double textScale = 1.0,
}) async {
tester.view.physicalSize = Size(width * 3, 800 * 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: page,
),
),
);
await tester.pumpAndSettle();
}
group('the shared pushed-page bar', () {
testWidgets('is a glass surface with a slate title', (tester) async {
await pump(
tester,
Scaffold(appBar: milerPageBar('Saved Addresses', onBack: () {})),
);
// The bar is transparent and wears `milerGlassSurface` — a frosted 7%
// brand wash — rather than a solid slab of either white or red. The rule
// it is holding to is unchanged: the surface stays light enough for
// near-black type, so the loudest colour in the palette is not spent on
// the one element that never changes.
final bar = tester.widget<AppBar>(find.byType(AppBar));
expect(bar.backgroundColor, Colors.transparent);
expect(bar.flexibleSpace, isNotNull,
reason: 'the glass wash is what colours the bar now');
// ── Slate, not maroon ──
//
// This was the only heading in the app in the brand colour, so an Account
// page's title read as a link while the same heading on a tab was
// near-black. One title colour across every bar; the brand still owns the
// glass wash behind it.
final title = tester.widget<Text>(find.text('Saved Addresses'));
expect(title.style!.color, ColorConstants.slateText);
expect(title.style!.fontSize, MilerType.pageTitleFixed.fontSize,
reason: 'one page-title size across tabs and pushed screens');
});
testWidgets('carries a back affordance when given one', (tester) async {
var popped = false;
await pump(
tester,
Scaffold(appBar: milerPageBar('Notifications', onBack: () => popped = true)),
);
// ── An arrow, because these pages are pushed ──
//
// The cross promised dismissal on nine screens you actually go *back*
// from, and it was one of three back glyphs in the app. `close_rounded`
// survives behind `isModal: true`, for a sheet that genuinely dismisses.
expect(find.byIcon(Icons.arrow_back_rounded), findsOneWidget);
expect(find.byIcon(Icons.close_rounded), findsNothing);
await tester.tap(find.byIcon(Icons.arrow_back_rounded));
await tester.pumpAndSettle();
expect(popped, isTrue);
});
testWidgets('and none when it is not', (tester) async {
await pump(tester, Scaffold(appBar: milerPageBar('Terms')));
expect(find.byIcon(Icons.arrow_back_rounded), findsNothing);
expect(find.byIcon(Icons.close_rounded), findsNothing);
});
testWidgets('a modal presentation still gets a cross', (tester) async {
await pump(
tester,
Scaffold(
appBar: milerPageBar('Skip stop', onBack: () {}, isModal: true),
),
);
expect(find.byIcon(Icons.close_rounded), findsOneWidget);
expect(find.byIcon(Icons.arrow_back_rounded), findsNothing);
});
testWidgets('survives a long title and a large font', (tester) async {
await pump(
tester,
Scaffold(
appBar: milerPageBar(
'A Very Long Page Title That Will Not Fit',
onBack: () {},
),
),
width: 320,
textScale: 2.0,
);
expect(tester.takeException(), isNull);
});
// ── One line, not two ──
//
// The bar used to carry a caption under the title. It was the smallest type
// on the screen in the one place a rider reads exactly once, and every
// caption duplicated something the page said louder further down — two
// sources for one number, which is how they drift apart.
testWidgets('carries the page name and nothing under it', (tester) async {
await pump(tester, Scaffold(appBar: milerPageBar('Notifications')));
final texts = find.byType(Text);
expect(tester.widgetList<Text>(texts), hasLength(1));
expect(find.text('Notifications'), findsOneWidget);
});
// ── The × is stacked ABOVE the heading, on the same margin ──
//
// It used to share the row: `[×] Edit Profile`. That pushed every title on
// every pushed page 52pt in from the margin, out of line with the content
// beneath it, and a control sitting at a title's left at the same optical
// weight reads as part of the title rather than as something you press.
testWidgets('stacks the close above the title', (tester) async {
await pump(
tester,
Scaffold(appBar: milerPageBar('Edit Profile', onBack: () {})),
);
final close = tester.getRect(find.byIcon(Icons.arrow_back_rounded));
final title = tester.getRect(find.text('Edit Profile'));
expect(
close.bottom,
lessThanOrEqualTo(title.top),
reason: 'the back control must sit on its own row above the heading',
);
expect(
close.left,
closeTo(title.left, 0.5),
reason:
'they share the page margin — an IconButton could not do this, '
'which is why the close is hand-rolled. See _TopBarClose.',
);
});
testWidgets('gives the back control a full 48pt target', (tester) async {
await pump(
tester,
Scaffold(appBar: milerPageBar('Edit Profile', onBack: () {})),
);
final box = tester.getSize(
find.ancestor(
of: find.byIcon(Icons.arrow_back_rounded),
matching: find.byType(SizedBox),
).first,
);
// 48, not the 44pt accessibility floor: this is the control a rider hits
// one-handed, and the glyph inside stays 24 so only the target grew.
expect(box.width, 48);
expect(box.height, 48);
});
});
group('FAQ lays out at every font scale', () {
// It did not. `Container(height: 160.h)` around the hero overflowed by 89px
// at 1.5x; the category grid's `childAspectRatio` derived cell height from
// the column *width*, so all four tiles overflowed by ~10px; and the SOS
// row's two unflexed children ran 30px past the card.
for (final width in [320.0, 360.0, 390.0, 430.0]) {
for (final scale in [1.0, 1.3, 1.5, 2.0]) {
testWidgets('at ${width.toInt()}pt / ${scale}x', (tester) async {
await pump(tester, const FaqPage(),
width: width, textScale: scale);
expect(tester.takeException(), isNull,
reason: 'FAQ overflowed at ${width.toInt()}pt / ${scale}x');
// Opening the accordions is where the page grows the most.
final tiles = find.byType(ExpansionTile);
for (var i = 0; i < tiles.evaluate().length; i++) {
await tester.tap(tiles.at(i), warnIfMissed: false);
await tester.pumpAndSettle();
expect(tester.takeException(), isNull,
reason: 'FAQ overflowed opening accordion $i at '
'${width.toInt()}pt / ${scale}x');
}
});
}
}
});
// Terms & Conditions is deliberately not laid out here: it hosts a
// `WebView`, and `WebViewPlatform.instance` has no implementation in a headless
// unit test, so the page cannot be built at all. Its app bar is the shared one
// — that much is covered by the group above — but the body needs a device or
// an integration test.
}