// SPDX-FileCopyrightText: 2026 FocusFlow contributors // SPDX-License-Identifier: AGPL-3.0-only /// Tests persistent SQLite composition behavior in the Flutter app. library; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:scheduler_core/scheduler_core.dart'; import 'package:focus_flow_flutter/app/focus_flow_app.dart'; import 'package:focus_flow_flutter/app/persistent_scheduler_composition.dart'; import 'package:focus_flow_flutter/controllers/scheduler_command_controller.dart'; /// Runs persistent composition smoke tests. void main() { testWidgets( 'persistent app starts without seeded demo tasks and reopens capture', (tester) async { final path = await _runAsync(tester, _tempDatabasePath); final composition = await _runAsync( tester, () => PersistentSchedulerComposition.open( sqlitePath: path, selectedDate: _date, clock: FixedClock(_instant(8)), ), ); await tester.pumpWidget(FocusFlowApp(composition: composition)); await tester.pump(); await tester.pump(const Duration(milliseconds: 100)); await tester.pump(const Duration(seconds: 1)); expect(find.text('January 2, 2026'), findsOneWidget); expect(find.text('Clean coffee maker'), findsNothing); expect(find.text('Water plants'), findsNothing); final commandController = composition.createCommandController( refreshReads: () async {}, selectedDate: () => composition.initialDate, ); addTearDown(commandController.dispose); await tester.runAsync( () => commandController.quickCaptureToBacklog( 'Captured from Flutter test', ), ); expect(commandController.state, isA()); await tester.pumpWidget(const SizedBox.shrink()); await tester.pump(); await tester.runAsync(composition.dispose); final reopened = await _runAsync( tester, () => PersistentSchedulerComposition.open( sqlitePath: path, selectedDate: _date, clock: FixedClock(_instant(8, 10)), ), ); addTearDown(() => tester.runAsync(reopened.dispose)); final backlog = await _runAsync( tester, () => reopened.managementUseCases.getBacklog( GetBacklogRequest( context: reopened.context('flutter-read-backlog'), sortKey: BacklogSortKey.age, ), ), ); expect(backlog.requireValue.items, hasLength(1)); expect( backlog.requireValue.items.single.task.title, 'Captured from Flutter test', ); }, ); } /// Fixed date used by persistent Flutter tests. final _date = CivilDate(2026, 1, 2); /// Returns an instant on the fixed lifecycle date. DateTime _instant(int hour, [int minute = 0]) { return DateTime.utc(_date.year, _date.month, _date.day, hour, minute); } /// Returns a fresh temporary SQLite path. Future _tempDatabasePath() async { final directory = await Directory.systemTemp.createTemp( 'focus-flow-flutter-persistence-', ); addTearDown(() => directory.delete(recursive: true)); return '${directory.path}/scheduler.sqlite'; } /// Runs real async work outside the widget-test fake async zone. Future _runAsync( WidgetTester tester, Future Function() callback, ) async { final result = await tester.runAsync(callback); if (result == null) { throw StateError('runAsync returned null.'); } return result; }