import 'package:flutter_test/flutter_test.dart'; import 'package:halkyon_learn_flutter/src/app/instance_gate.dart'; import 'package:halkyon_learn_flutter/src/app/person_app.dart'; import 'package:halkyon_learn_flutter/src/core/instance/instance.dart'; import 'package:halkyon_learn_flutter/src/core/instance/instance_choice.dart'; import 'package:halkyon_learn_flutter/src/feature/auth/widget/session_gate.dart'; import 'package:halkyon_learn_flutter/src/feature/settings/widget/settings_screen.dart'; import 'package:material_ui/material_ui.dart'; import '../support/fake_instance_checker.dart'; import '../support/memory_device_settings_store.dart'; import '../support/memory_instance_store.dart'; import '../support/pump_app.dart'; import '../support/test_dependencies.dart'; void main() { final builtIn = Instance.parse('learn.halkyon.eu')!; final other = Instance.parse('learn.example.org')!; late MemoryInstanceStore store; late List opened; late List forgotten; setUp(() { store = MemoryInstanceStore(); opened = []; forgotten = []; }); /// Starts the app on a device that [usedBefore] the choice existed, with /// [answering] instances answering a check. Future start( WidgetTester tester, { bool usedBefore = false, Set answering = const {}, }) async { tester.view.physicalSize = const Size(412, 900); tester.view.devicePixelRatio = 1; addTearDown(tester.view.reset); final choice = InstanceChoice( store, builtIn: builtIn, forget: (instance) async => forgotten.add(instance), ); await choice.load(usedBefore: usedBefore); await tester.pumpWidget( InstanceGate( choice: choice, checker: FakeInstanceChecker(answering), deviceSettings: MemoryDeviceSettingsStore(), open: (instance) async { opened.add(instance); return testDependencies(); }, app: SessionGate(app: (person) => PersonApp(person: person)), ), ); await tester.pumpAndSettle(); return choice; } /// Opens Settings › Switch account, further down Settings. Future openSwitcher(WidgetTester tester) async { await openTab(tester, 'Profile'); await tester.tap(find.byTooltip('Settings')); await tester.pumpAndSettle(); await tester.scrollUntilVisible( find.text('Switch account'), 200, // Settings' list; its citation preview is a scroll view of its own. scrollable: find .descendant( of: find.byType(SettingsScreen), matching: find.byType(Scrollable), ) .first, ); // Built, but perhaps still below the window's edge. await tester.ensureVisible(find.text('Switch account')); await tester.pumpAndSettle(); await tester.tap(find.text('Switch account')); await tester.pumpAndSettle(); } testWidgets('a new install asks for the instance first, and opens the ' "build's in one tap", (tester) async { await start(tester); // The wordmark says it to screen readers. expect(find.bySemanticsLabel('Welcome to Halkyon Learn'), findsOneWidget); expect(opened, isEmpty, reason: 'nothing made anywhere yet'); expect(find.byType(TextField), findsNothing, reason: 'no address asked'); await tester.tap(find.text('learn.halkyon.eu')); await tester.pumpAndSettle(); expect(opened, [builtIn]); expect(tab('Home'), findsOneWidget); }); testWidgets('another instance is checked before the app moves there', ( tester, ) async { await start(tester, answering: {other}); await tester.tap(find.text('Another instance')); await tester.pumpAndSettle(); expect( FocusManager.instance.primaryFocus?.context ?.findAncestorWidgetOfExactType(), isNotNull, reason: 'the address is typed at once', ); await tester.enterText(find.byType(TextField), 'nowhere.example.org'); await tester.tap(find.text('Continue')); await tester.pumpAndSettle(); expect( find.text( 'No instance of Halkyon Learn answers at nowhere.example.org.', ), findsOneWidget, ); expect(opened, isEmpty); await tester.enterText(find.byType(TextField), 'learn.example.org'); await tester.tap(find.text('Continue')); await tester.pumpAndSettle(); expect(opened, [other]); }); testWidgets('an install from before goes on without asking', (tester) async { await start(tester, usedBefore: true); expect(opened, [builtIn]); expect(tab('Home'), findsOneWidget); }); testWidgets('Settings switches between the accounts on the device, and ' 'removes one not in use', (tester) async { store = MemoryInstanceStore((current: builtIn, known: [builtIn, other])); await start(tester); await openSwitcher(tester); expect(find.text('learn.halkyon.eu'), findsWidgets); await tester.tap(find.text('learn.example.org')); await tester.pumpAndSettle(); expect(opened, [builtIn, other]); expect(tab('Home'), findsOneWidget, reason: 'a new app, on Home'); await openSwitcher(tester); await tester.tap(find.byTooltip('Remove account')); await tester.pumpAndSettle(); await tester.tap(find.widgetWithText(FilledButton, 'Remove')); await tester.pumpAndSettle(); expect(forgotten, [builtIn]); expect(store.kept?.known, [other]); }); }