import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:miler/data/load_state.dart'; import 'package:miler/views/helpers/widgets/load_state_view.dart'; /// ───────────────────────────────────────────────────────────────────────── /// FOUR SCREENS, ONE ANSWER PER STATE /// /// Every screen decided independently what a failure looked like — usually by /// rendering its empty state, which tells a rider to wait for work that is /// actually a dead request. These pin the mapping, and in particular the two /// cases that were previously indistinguishable. /// ───────────────────────────────────────────────────────────────────────── void main() { Future pump( WidgetTester tester, LoadState> state, { Future Function()? onRetry, }) async { await tester.pumpWidget( ScreenUtilInit( designSize: const Size(390, 844), builder: (_, _) => MaterialApp( home: Scaffold( body: SingleChildScrollView( child: LoadStateView>( state: state, onRetry: onRetry, empty: const Text('NOTHING TODAY'), builder: (v, refreshing) => Text('${v.join(",")} refreshing=$refreshing'), ), ), ), ), ), ); await tester.pump(); } testWidgets('data renders, and says whether a refresh is behind it', ( tester, ) async { await pump(tester, const LoadData(['a', 'b'], refreshing: true)); expect(find.text('a,b refreshing=true'), findsOneWidget); }); testWidgets('an empty day is the screen\'s own empty state', (tester) async { await pump(tester, const LoadEmpty>()); expect(find.text('NOTHING TODAY'), findsOneWidget); }); // ── The distinction the old code could not make ── testWidgets('a failure is never the empty state', (tester) async { await pump( tester, const LoadFailure>( LoadFailureKind.server, message: 'hub is down', ), onRetry: () async {}, ); expect( find.text('NOTHING TODAY'), findsNothing, reason: 'telling a rider to wait for work is wrong when the request died', ); // The server's own sentence, not one this app invented. expect(find.text('hub is down'), findsOneWidget); expect(find.text('Try again'), findsOneWidget); }); testWidgets('offline says so and offers a retry', (tester) async { await pump( tester, const LoadFailure>(LoadFailureKind.offline), onRetry: () async {}, ); expect(find.text('No connection'), findsOneWidget); expect(find.text('Try again'), findsOneWidget); }); testWidgets('an expired session offers no retry', (tester) async { await pump( tester, const LoadFailure>(LoadFailureKind.unauthorized), onRetry: () async {}, ); expect(find.text('Signed out'), findsOneWidget); expect( find.text('Try again'), findsNothing, reason: 'retrying cannot succeed without signing in again', ); }); testWidgets('rate limiting does not invite another tap', (tester) async { await pump( tester, const LoadFailure>(LoadFailureKind.rateLimited), onRetry: () async {}, ); expect(find.text('Too many requests'), findsOneWidget); }); // ── Unavailable is neither an error nor an empty day ── testWidgets('a line with no endpoint gets its own state and no retry', ( tester, ) async { await pump( tester, const LoadUnavailable>('Milk Man work is not served yet.'), onRetry: () async {}, ); expect(find.text('Not available yet'), findsOneWidget); expect(find.text('Milk Man work is not served yet.'), findsOneWidget); expect(find.text('NOTHING TODAY'), findsNothing); expect( find.text('Try again'), findsNothing, reason: 'nothing the rider does changes a missing backend capability', ); }); testWidgets('the retry actually runs', (tester) async { var tries = 0; await pump( tester, const LoadFailure>(LoadFailureKind.offline), onRetry: () async => tries++, ); await tester.tap(find.text('Try again')); await tester.pump(); expect(tries, 1); }); }