import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:halkyon_learn_flutter/src/core/device_settings/model/device_settings.dart'; import 'package:halkyon_learn_flutter/src/core/router/route_paths.dart'; import 'package:halkyon_learn_flutter/src/feature/assistant/model/connected_assistant.dart'; import 'package:halkyon_learn_flutter/src/feature/assistant/widget/connect_claude_screen.dart'; import 'package:halkyon_learn_flutter/src/feature/settings/widget/settings_screen.dart'; import 'package:material_ui/material_ui.dart'; import '../../support/fake_assistant_gateway.dart'; import '../../support/pump_app.dart'; import '../../support/test_dependencies.dart'; void main() { late FakeAssistantGateway gateway; setUp(() { gateway = FakeAssistantGateway( initially: [ ConnectedAssistant( id: 1, name: 'Claude', host: 'claude.ai', connectedAt: DateTime.utc(2026, 10, 9), lastUsedAt: DateTime.utc(2026, 10, 10), ), ], ); }); Future showApp( WidgetTester tester, { String location = RoutePaths.connectAssistant, bool on = true, }) async { tester.view.physicalSize = const Size(400, 1000); tester.view.devicePixelRatio = 1; addTearDown(tester.view.reset); await pumpApp( tester, testDependencies( assistantGateway: gateway, deviceSettings: DeviceSettings(assistant: on), ), location: location, ); await tester.pumpAndSettle(); } /// Brings [finder] in Settings into view. Future scrollTo(WidgetTester tester, Finder finder) async { await tester.scrollUntilVisible( finder, 200, scrollable: find .descendant( of: find.byType(SettingsScreen), matching: find.byType(Scrollable), ) .first, ); await tester.ensureVisible(finder); await tester.pumpAndSettle(); } testWidgets('Settings offers to connect Claude only while asking Claude is ' 'on', (tester) async { await showApp(tester, location: RoutePaths.settings, on: false); await scrollTo(tester, find.text('Ask an AI assistant or search the web')); expect(find.text('Connect Claude'), findsNothing); await tester.tap(find.text('Ask an AI assistant or search the web')); await tester.pumpAndSettle(); await scrollTo(tester, find.text('Connect Claude')); await tester.tap(find.text('Connect Claude')); await tester.pumpAndSettle(); expect(find.byType(ConnectClaudeScreen), findsOneWidget); }); testWidgets("shows the connector's address to add in Claude, to copy", ( tester, ) async { final copied = []; tester.binding.defaultBinaryMessenger.setMockMethodCallHandler( SystemChannels.platform, (call) async { if (call.method == 'Clipboard.setData') { copied.add((call.arguments as Map)['text']); } return null; }, ); addTearDown( () => tester.binding.defaultBinaryMessenger.setMockMethodCallHandler( SystemChannels.platform, null, ), ); await showApp(tester); expect(find.text('https://learn.example.org/mcp'), findsOneWidget); expect(find.textContaining('learn.example.org through'), findsOneWidget); await tester.tap(find.byTooltip('Copy')); await tester.pumpAndSettle(); expect(copied, ['https://learn.example.org/mcp']); expect(find.text('Copied'), findsOneWidget); }); testWidgets('shows a code on asking, and a new one replaces it', ( tester, ) async { await showApp(tester); await tester.tap(find.text('Show a code')); await tester.pumpAndSettle(); expect(find.text('K7QX-M2P0'), findsOneWidget); expect(find.textContaining('Valid until'), findsOneWidget); await tester.tap(find.text('New code')); await tester.pumpAndSettle(); expect(find.text('K7QX-M2P0'), findsNothing); expect(find.text('K7QX-M2P1'), findsOneWidget); }); testWidgets('lists what is connected; disconnecting asks first', ( tester, ) async { await showApp(tester); expect(find.text('Claude'), findsOneWidget); expect(find.textContaining('claude.ai'), findsOneWidget); await tester.tap(find.widgetWithText(TextButton, 'Disconnect')); await tester.pumpAndSettle(); expect(find.text('Disconnect Claude?'), findsOneWidget); await tester.tap(find.widgetWithText(FilledButton, 'Disconnect')); await tester.pumpAndSettle(); expect(gateway.connected, isEmpty); expect(find.text('Nothing connected yet.'), findsOneWidget); }); testWidgets('an instance without a connector says so, and shows no steps', ( tester, ) async { gateway = FakeAssistantGateway(url: null); await showApp(tester); expect(find.textContaining("doesn't offer a connector"), findsOneWidget); expect(find.text('Show a code'), findsNothing); }); testWidgets('an instance out of reach says so, and asks again on Try ' 'again', (tester) async { gateway.unreachable = true; await showApp(tester); expect(find.textContaining("can't be reached"), findsOneWidget); gateway.unreachable = false; await tester.tap(find.text('Try again')); await tester.pumpAndSettle(); expect(find.textContaining("can't be reached"), findsNothing); expect(find.text('https://learn.example.org/mcp'), findsOneWidget); }); }