import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:miler/views/Dashboard/activity/activity_page.dart'; import 'package:miler/views/Dashboard/summary/summary.dart'; import 'package:miler/views/offline/offline_banner.dart'; /// ── Riders turn the system font up ── /// /// An audit found the screens with layout tests (verify, collect payment, /// profile, trip tabs) solid at 2×, and everything else unguarded: 443 `Text` /// widgets against 109 `maxLines`, and exactly one file in 102 reading the text /// scaler. /// /// The failure mode is not cosmetic. A `Row` with a fixed-width child overflows /// into the black-and-yellow stripe bar, which on a customer's doorstep looks /// like the app has crashed. Earnings was the worst of them — a 46sp hero /// figure sharing a row with labels — which is why that figure is now on the /// type scale. /// /// This sweeps the screens that had no coverage. It asserts no exception is /// thrown, which is exactly what a `RenderFlex` overflow does. void main() { Future pump( WidgetTester tester, Widget page, { double width = 360, double scale = 1.0, }) async { tester.view.physicalSize = Size(width * 3, 780 * 3); tester.view.devicePixelRatio = 3.0; addTearDown(tester.view.reset); await tester.pumpWidget( ScreenUtilInit( designSize: const Size(390, 844), minTextAdapt: true, builder: (_, _) => MaterialApp( builder: (context, child) => MediaQuery( data: MediaQuery.of( context, ).copyWith(textScaler: TextScaler.linear(scale)), child: child!, ), home: page, ), ), ); await tester.pump(const Duration(milliseconds: 400)); } // The four scales Android offers, on the narrowest phone the app supports. const scales = [1.0, 1.3, 1.5, 2.0]; group('Earnings', () { for (final s in scales) { testWidgets('lays out at ${s}x', (tester) async { SharedPreferences.setMockInitialValues({'userId': 1}); await pump(tester, const Summary(), scale: s); expect(tester.takeException(), isNull); }); } }); group('Activity', () { for (final s in scales) { testWidgets('lays out at ${s}x', (tester) async { SharedPreferences.setMockInitialValues({'userId': 1}); await pump(tester, const ActivityPage(), scale: s); expect(tester.takeException(), isNull); }); } }); // ── Sign In is covered by `auth_design_test.dart`, not here ── // // The screen calls `NotificationServce.initialize` in `initState`, which // needs a platform channel a widget test has no way to answer. That is a test // harness limitation rather than a layout finding: the auth *components* — // the code row, the scaffold, the primary button, the inline error — are // pumped and asserted directly in `auth_design_test.dart`, which is where the // design surface actually lives. group('the offline strip', () { for (final s in scales) { testWidgets('lays out at ${s}x without covering the job', (tester) async { await pump( tester, const OfflineScope( offline: true, child: Scaffold( body: Center(child: Text('45, Lake View Apartments, Coimbatore')), ), ), scale: s, ); expect(tester.takeException(), isNull); // Still readable, still not on top of anything. expect(find.text('45, Lake View Apartments, Coimbatore'), findsOneWidget); }); } }); }