import 'dart:typed_data'; import 'package:flutter_test/flutter_test.dart'; import 'package:halkyon_learn_flutter/src/core/router/route_paths.dart'; import 'package:halkyon_learn_flutter/src/feature/auth/model/auth_status.dart'; import 'package:halkyon_learn_flutter/src/feature/profile/data/profile_repository.dart'; import 'package:halkyon_learn_flutter/src/feature/profile/model/public_profile.dart'; import 'package:material_ui/material_ui.dart'; import '../../support/fake_auth_gateway.dart'; import '../../support/fake_file_picker.dart'; import '../../support/fake_profile_gateway.dart'; import '../../support/pump_app.dart'; import '../../support/test_dependencies.dart'; void main() { /// Opens the Profile tab of a person with an identity, whose profile the /// instance answers through [profiles]. Future showProfile( WidgetTester tester, FakeProfileGateway profiles, { bool identified = true, FakeFilePicker? files, }) async { tester.view.physicalSize = const Size(1000, 1800); tester.view.devicePixelRatio = 1; addTearDown(tester.view.reset); await pumpApp( tester, testDependencies( authGateway: FakeAuthGateway( status: SignedIn( authUserId: 'github-person', isIdentified: identified, ), ), profileGateway: profiles, files: files, ), location: RoutePaths.profile, ); await tester.pumpAndSettle(); } Finder field(String label) => find.widgetWithText(TextField, label); testWidgets('a person with an identity writes a public profile and saves ' 'it, then sees the address others find them by', (tester) async { final profiles = FakeProfileGateway(); await showProfile(tester, profiles); expect(find.text('Public profile'), findsOneWidget); await tester.enterText(field('Display name'), 'Tizian'); await tester.enterText(field('Handle'), 'raisondetredev'); await tester.enterText(field('Bio'), 'Learning Japanese.'); await tester.tap(find.text('Add link')); await tester.pumpAndSettle(); await tester.enterText( find.widgetWithText(TextField, 'https://…'), 'https://halkyon.app', ); await tester.tap(find.text('Save')); await tester.pumpAndSettle(); expect(find.text('Profile saved.'), findsOneWidget); expect( find.text('Others find you as @raisondetredev@halkyon.eu'), findsOneWidget, ); expect(profiles.profile!.links, ['https://halkyon.app']); expect(profiles.profile!.displayName, 'Tizian'); }); testWidgets('a handle someone else has is refused, said under it', ( tester, ) async { await showProfile(tester, FakeProfileGateway(taken: {'Tizian'})); await tester.enterText(field('Handle'), 'tizian'); await tester.tap(find.text('Save')); await tester.pumpAndSettle(); expect(find.text('Someone else has this handle.'), findsOneWidget); }); testWidgets('once its person has published, the handle is fixed and the ' 'profile stays', (tester) async { await showProfile( tester, FakeProfileGateway( profile: const PublicProfile( handle: 'raisondetredev', address: 'raisondetredev@halkyon.eu', handleFixed: true, ), ), ); expect( find.text('Fixed since you published: others refer to it.'), findsOneWidget, ); expect( tester.widget(field('Handle')).readOnly, isTrue, ); expect(find.text('Remove profile'), findsNothing); }); testWidgets('removing a profile asks first, then frees its handle', ( tester, ) async { final profiles = FakeProfileGateway( profile: const PublicProfile( handle: 'raisondetredev', address: 'raisondetredev@halkyon.eu', ), ); await showProfile(tester, profiles); await tester.tap(find.text('Remove profile')); await tester.pumpAndSettle(); expect(find.text('Remove your public profile?'), findsOneWidget); await tester.tap(find.text('Remove profile').last); await tester.pumpAndSettle(); expect(find.text('Profile removed.'), findsOneWidget); expect(profiles.profile, isNull); }); testWidgets('a saved profile takes a picture as its avatar, and gives it ' 'up', (tester) async { final profiles = FakeProfileGateway( profile: const PublicProfile( handle: 'raisondetredev', address: 'raisondetredev@halkyon.eu', ), ); final files = FakeFilePicker() ..next = (name: 'me.jpg', bytes: Uint8List.fromList([1, 2, 3])); await showProfile(tester, profiles, files: files); await tester.tap(find.text('Choose picture')); await tester.pumpAndSettle(); expect(profiles.avatars.single, [1, 2, 3]); expect(files.asked.single, contains('jpg')); expect(find.text('Profile saved.'), findsOneWidget); await tester.tap(find.text('Remove picture')); await tester.pumpAndSettle(); expect(profiles.profile!.avatarUrl, isNull); expect(find.text('Remove picture'), findsNothing); }); testWidgets('a profile not yet saved has no picture to choose', ( tester, ) async { await showProfile(tester, FakeProfileGateway()); expect(find.text('Save your profile to add a picture.'), findsOneWidget); expect(find.text('Choose picture'), findsNothing); }); testWidgets('an anonymous person is told how to have one', (tester) async { await showProfile(tester, FakeProfileGateway(), identified: false); expect( find.text('Sign in with GitHub to have a public profile and publish.'), findsOneWidget, ); expect(find.text('Public profile'), findsNothing); }); testWidgets('without the instance, it says so and tries again', ( tester, ) async { final profiles = FakeProfileGateway()..fail = Exception('offline'); await showProfile(tester, profiles); expect( find.text("Your public profile can't be reached right now."), findsOneWidget, ); profiles.fail = null; await tester.tap(find.text('Retry')); await tester.pumpAndSettle(); expect(field('Handle'), findsOneWidget); }); test( 'the repository leaves blank links out and trims what it saves', () async { final profiles = FakeProfileGateway(); await ProfileRepository(profiles).save( handle: ' tizian ', displayName: ' Tizian ', bio: '', links: ['', ' https://halkyon.app '], ); expect(profiles.profile!.handle, 'tizian'); expect(profiles.profile!.links, ['https://halkyon.app']); }, ); }