import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:krow_worker_app/core/theme.dart'; import 'package:krow_worker_app/screens/clock_screen.dart'; import 'package:krow_worker_app/screens/pay_screens.dart'; import 'package:krow_worker_app/screens/profile_screen.dart'; import 'package:krow_worker_app/screens/shifts_screen.dart'; import 'package:krow_worker_app/screens/today_screen.dart'; import 'package:krow_worker_app/state/session_controller.dart'; import 'package:shared_preferences/shared_preferences.dart'; /// Overflow is reported as a thrown FlutterError, and the stripes only appear /// in a debug build — so a release web check cannot see it and it reaches a /// device first. These pump every tab at real phone sizes to catch it here. void main() { late SharedPreferences prefs; setUp(() async { SharedPreferences.setMockInitialValues({}); prefs = await SharedPreferences.getInstance(); }); Future pumpScreen( WidgetTester tester, Widget screen, { required Size size, double textScale = 1.0, }) async { tester.view.physicalSize = size; tester.view.devicePixelRatio = 1.0; addTearDown(tester.view.reset); // Captured rather than left to the default handler, so a failure names the // widget that overflowed instead of just the pixel count. final errors = []; final previousHandler = FlutterError.onError; FlutterError.onError = errors.add; await tester.pumpWidget( ProviderScope( overrides: [prefsProvider.overrideWithValue(prefs)], child: MaterialApp( theme: buildKrowTheme(), home: MediaQuery( data: MediaQueryData( size: size, textScaler: TextScaler.linear(textScale), // A notch, so the header has to make room for it. padding: const EdgeInsets.only(top: 44, bottom: 34), ), child: screen, ), ), ), ); await tester.pump(); FlutterError.onError = previousHandler; if (errors.isNotEmpty) { fail(errors.map((e) => e.toString()).join('\n\n')); } // Screens with running timers must be torn down, or the test fails on a // pending timer rather than on what it is checking. await tester.pumpWidget(const SizedBox.shrink()); } final screens = { 'Today': const TodayScreen(), 'Shifts': const ShiftsScreen(), 'Clock': const ClockScreen(), 'Pay': const PayScreen(), 'Profile': const ProfileScreen(), }; // A small phone, a tall one, and a short-but-wide one. final sizes = { 'small phone': const Size(360, 640), 'tall phone': const Size(412, 915), 'compact': const Size(320, 568), }; for (final screen in screens.entries) { for (final size in sizes.entries) { testWidgets('${screen.key} lays out on a ${size.key}', (tester) async { await pumpScreen(tester, screen.value, size: size.value); }); } testWidgets('${screen.key} survives large system text', (tester) async { await pumpScreen( tester, screen.value, size: const Size(360, 640), textScale: 1.3, ); }); } }