import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:halkyon_app/halkyon_app.dart'; import 'package:halkyon_app/testing.dart'; import 'package:material_ui/material_ui.dart'; /// A signed-in person's app as a test makes one: whose it is, and a way to /// sign out. class const _PersonApp(final SignedIn person) extends StatelessWidget { @override Widget build(BuildContext context) => MaterialApp( localizationsDelegates: testLocalizationsDelegates, supportedLocales: HalkyonAppLocalizations.supportedLocales, home: Scaffold( body: Builder( builder: (context) => Column( children: [ Text('App of ${person.authUserId}'), TextButton( onPressed: () => context.read().add( const SessionEventSignOutRequested(), ), child: const Text('Sign out'), ), ], ), ), ), ); } void main() { late FakeAuthGateway gateway; late TestAppDependencies dependencies; setUp(() { gateway = FakeAuthGateway(); dependencies = TestAppDependencies(gateway: gateway); }); Future start(WidgetTester tester) async { await tester.pumpWidget( AppFrameScope( frame: testFrame(), child: AppScope( dependencies: dependencies, child: SessionGate( look: (context) => (themeMode: ThemeMode.light, locale: null), app: _PersonApp.new, ), ), ), ); await tester.pumpAndSettle(); } testWidgets('shows the app once signed in anonymously at a first start', ( tester, ) async { await start(tester); expect(find.text('App of anonymous-1'), findsOneWidget); }); testWidgets('offers to try again when signing in fails', (tester) async { gateway.signInError = Exception('No connection'); await start(tester); expect(find.text("Couldn't reach the server."), findsOneWidget); gateway.signInError = null; await tester.tap(find.text('Try again')); await tester.pumpAndSettle(); expect(find.text('App of anonymous-2'), findsOneWidget); }); testWidgets('signed out, it asks how to start on the instance, making no ' 'account; starting without one makes one', (tester) async { await dependencies.signedOut.write(signedOut: true); await start(tester); expect(find.text('Signed out'), findsOneWidget); expect(find.text('Start on test.example.org:'), findsOneWidget); expect(find.text('Sign in with GitHub'), findsOneWidget); expect(find.text('Create an account'), findsOneWidget); expect( find.text('Use another instance'), findsNothing, reason: 'no instances to choose among here', ); expect(gateway.signIns, 0); await tester.tap(find.text('Start without an account')); await tester.pumpAndSettle(); expect(find.text('App of anonymous-1'), findsOneWidget); expect(dependencies.signedOut.signedOut, isFalse); }); testWidgets('from there, GitHub signs in its person; a GitHub account ' 'nobody here has yet is said so', (tester) async { await dependencies.signedOut.write(signedOut: true); await start(tester); gateway.gitHubError = const GitHubSignInFailed( GitHubSignInProblem.unknownAccount, ); await tester.tap(find.text('Sign in with GitHub')); await tester.pumpAndSettle(); expect( find.text( // The package's sentence, then the app's on where GitHub is added. 'No account here signs in with this GitHub account yet. Start ' 'without an account, then add GitHub in Settings.', ), findsOneWidget, ); gateway.gitHubError = null; await tester.tap(find.text('Sign in with GitHub')); await tester.pumpAndSettle(); // A password is offered first (ADR 0017, D2), while the sign-in is // recent enough to set one; put off, the person's app. expect(find.text('Set up a password'), findsWidgets); await tester.tap(find.text('Later')); await tester.pumpAndSettle(); expect(find.text('App of github-person'), findsOneWidget); expect(gateway.signIns, 0); }); testWidgets('after GitHub, offers no password to someone who has one, or ' 'where the instance offers none', (tester) async { dependencies.passwordGateway.offered = false; await dependencies.signedOut.write(signedOut: true); await start(tester); await tester.tap(find.text('Sign in with GitHub')); await tester.pumpAndSettle(); expect(find.text('Later'), findsNothing); expect(find.text('App of github-person'), findsOneWidget); }); testWidgets('offers only the ways the instance has', (tester) async { gateway.offersGitHub = false; dependencies.passwordGateway.offered = false; await dependencies.signedOut.write(signedOut: true); await start(tester); expect(find.text('Sign in with GitHub'), findsNothing); expect(find.text('Create an account'), findsNothing); expect(find.text('Start without an account'), findsOneWidget); }); testWidgets('signing out, or the instance ending the session, asks how to ' 'start', (tester) async { gateway = FakeAuthGateway( status: const SignedIn(authUserId: 'ada', isIdentified: true), ); dependencies = TestAppDependencies(gateway: gateway); await start(tester); expect(find.text('App of ada'), findsOneWidget); await tester.tap(find.text('Sign out')); await tester.pumpAndSettle(); expect(find.text('Signed out'), findsOneWidget); expect(dependencies.signedOut.signedOut, isTrue); await tester.tap(find.text('Start without an account')); await tester.pumpAndSettle(); gateway.revokeSession(); await tester.pumpAndSettle(); expect(find.text('Signed out'), findsOneWidget); }); }