import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:scheduler_core/scheduler_core.dart'; import 'package:focus_flow_flutter/controllers/scheduler_read_controller.dart'; import 'package:focus_flow_flutter/main.dart'; import 'package:focus_flow_flutter/widgets/backlog_pane.dart'; void main() { testWidgets('renders Today and Backlog read models', (tester) async { await tester.pumpWidget( FocusFlowApp(composition: DemoSchedulerComposition.seeded()), ); await _pumpAndSettle(tester); expect(find.text('FocusFlow'), findsOneWidget); expect(find.text('Today'), findsWidgets); expect(find.text('Plan the day'), findsWidgets); expect(find.text('Appointment'), findsWidgets); await tester.tap(find.byIcon(Icons.inbox_outlined)); await _pumpAndSettle(tester); expect(find.text('Backlog'), findsWidgets); expect(find.text('Send project update'), findsOneWidget); expect(find.text('Sort receipts'), findsOneWidget); }); testWidgets('renders compact panel from owner settings', (tester) async { await tester.pumpWidget( FocusFlowApp(composition: DemoSchedulerComposition.seeded()), ); await _pumpAndSettle(tester); expect(find.text('Compact'), findsOneWidget); expect(find.text('Plan the day'), findsNWidgets(2)); }); testWidgets('renders Backlog empty state', (tester) async { await tester.pumpWidget( _testShell( BacklogPane( controller: _FakeReadController( SchedulerReadEmpty(_emptyBacklog()), ), ), ), ); expect(find.text('Backlog is clear'), findsOneWidget); expect( find.text('Captured tasks will appear here when they need planning.'), findsOneWidget, ); }); testWidgets('typed failure state can retry', (tester) async { final controller = _FakeReadController( SchedulerReadFailure( failure: const ApplicationFailure( code: ApplicationFailureCode.conflict, ), ), retryState: SchedulerReadEmpty(_emptyBacklog()), ); await tester.pumpWidget(_testShell(BacklogPane(controller: controller))); expect(find.text('Could not load Backlog'), findsOneWidget); expect(find.text('Status: conflict'), findsOneWidget); await tester.tap(find.text('Retry')); await _pumpAndSettle(tester); expect(controller.retryCount, 1); expect(find.text('Backlog is clear'), findsOneWidget); }); testWidgets('quick capture schedules into Today and completes', ( tester, ) async { final composition = DemoSchedulerComposition.seeded( includeTodayItems: false, includeBacklogItems: false, ); await tester.pumpWidget(FocusFlowApp(composition: composition)); await _pumpAndSettle(tester); await tester.tap(find.byIcon(Icons.inbox_outlined)); await _pumpAndSettle(tester); await tester.enterText( find.byKey(const ValueKey('quick-capture-title')), 'Water plants', ); await tester.tap(find.text('Capture')); await _pumpAndSettle(tester); expect(find.text('Water plants'), findsOneWidget); expect(composition.store.currentTasks.single.status, TaskStatus.backlog); await tester.enterText(find.byType(TextField).last, '20'); await tester.tap(find.byTooltip('Schedule')); await _pumpAndSettle(tester); await tester.tap(find.byIcon(Icons.today_outlined)); await _pumpAndSettle(tester); expect(find.text('Water plants'), findsWidgets); expect(composition.store.currentTasks.single.status, TaskStatus.planned); await tester.tap(find.byTooltip('Done').first); await _pumpAndSettle(tester); expect(composition.store.currentTasks.single.status, TaskStatus.completed); }); } Future _pumpAndSettle(WidgetTester tester) { return tester.pumpAndSettle( const Duration(milliseconds: 50), EnginePhase.sendSemanticsUpdate, const Duration(seconds: 2), ); } Widget _testShell(Widget child) { return MaterialApp( theme: ThemeData( colorScheme: ColorScheme.fromSeed( seedColor: const Color(0xFF2F6F73), brightness: Brightness.dark, ), useMaterial3: true, ), home: Scaffold(body: child), ); } BacklogQueryResult _emptyBacklog() { return BacklogQueryResult( ownerId: 'owner-1', settings: OwnerSettings(ownerId: 'owner-1', timeZoneId: 'UTC'), items: const [], ); } class _FakeReadController extends UiReadController { _FakeReadController(SchedulerReadState state, {this.retryState}) : _state = state; SchedulerReadState _state; final SchedulerReadState? retryState; int retryCount = 0; @override SchedulerReadState get state => _state; @override Future load() async {} @override Future retry() async { retryCount += 1; final next = retryState; if (next != null) { _state = next; notifyListeners(); } } }