From 150d72c1dc7a6a50c5fb68906e7cb59e1e0c406d Mon Sep 17 00:00:00 2001 From: boxdot Date: Thu, 13 Feb 2025 18:06:23 +0100 Subject: [PATCH] fix: padding and fonts in registration screens Also add snapshot tests for the registration screens. --- .../registration/display_name_picture.dart | 44 +++++----- app/lib/registration/server_choice.dart | 78 ++++++++--------- app/lib/registration/username_password.dart | 85 ++++++++++--------- app/test/mocks.dart | 4 + .../display_name_picture_test.dart | 67 +++++++++++++++ .../goldens/display_name_avatar_choice.png | 3 + .../display_name_avatar_choice_empty.png | 3 + .../registration/goldens/server_choice.png | 3 + .../goldens/server_choice_empty.png | 3 + .../goldens/username_password_choice.png | 3 + .../username_password_choice_empty.png | 3 + app/test/registration/server_choice_test.dart | 64 ++++++++++++++ .../registration/username_password_test.dart | 70 +++++++++++++++ 13 files changed, 331 insertions(+), 99 deletions(-) create mode 100644 app/test/registration/display_name_picture_test.dart create mode 100644 app/test/registration/goldens/display_name_avatar_choice.png create mode 100644 app/test/registration/goldens/display_name_avatar_choice_empty.png create mode 100644 app/test/registration/goldens/server_choice.png create mode 100644 app/test/registration/goldens/server_choice_empty.png create mode 100644 app/test/registration/goldens/username_password_choice.png create mode 100644 app/test/registration/goldens/username_password_choice_empty.png create mode 100644 app/test/registration/server_choice_test.dart create mode 100644 app/test/registration/username_password_test.dart diff --git a/app/lib/registration/display_name_picture.dart b/app/lib/registration/display_name_picture.dart index cbed5adb..28db778e 100644 --- a/app/lib/registration/display_name_picture.dart +++ b/app/lib/registration/display_name_picture.dart @@ -25,28 +25,30 @@ class DisplayNameAvatarChoice extends StatelessWidget { toolbarHeight: isPointer() ? 100 : null, leading: const AppBarBackButton(), ), - body: Padding( - padding: const EdgeInsets.all(20.0), - child: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - const _UserAvatarPicker(), - Column( - children: [ - const Text('Choose a picture and a display name'), - const SizedBox(height: 20), - Form( - autovalidateMode: AutovalidateMode.always, - child: ConstrainedBox( - constraints: BoxConstraints.tight(const Size(300, 80)), - child: const _DisplayNameTextField(), + body: SafeArea( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: Spacings.s), + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + const _UserAvatarPicker(), + Column( + children: [ + const Text('Choose a picture and a display name'), + const SizedBox(height: 20), + Form( + autovalidateMode: AutovalidateMode.always, + child: ConstrainedBox( + constraints: BoxConstraints.tight(const Size(300, 80)), + child: const _DisplayNameTextField(), + ), ), - ), - ], - ), - const _SignUpFooter() - ], + ], + ), + const _SignUpFooter() + ], + ), ), ), ), diff --git a/app/lib/registration/server_choice.dart b/app/lib/registration/server_choice.dart index 3ec85aa9..bcd9306f 100644 --- a/app/lib/registration/server_choice.dart +++ b/app/lib/registration/server_choice.dart @@ -25,46 +25,48 @@ class ServerChoice extends StatelessWidget { leading: const AppBarBackButton(), ), body: SafeArea( - child: Center( - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - Text( - 'Choose a server where you want to create your account', - style: Theme.of(context).textTheme.labelMedium, - ), - Form( - autovalidateMode: AutovalidateMode.always, - child: Column( - children: [ - const SizedBox(height: 10), - ConstrainedBox( - constraints: BoxConstraints.tight(const Size(300, 80)), - child: TextFormField( - autofocus: (Platform.isIOS || Platform.isAndroid) - ? false - : true, - decoration: - const InputDecoration(hintText: 'DOMAIN NAME'), - initialValue: - context.read().state.domain, - style: inputTextStyle, - onChanged: (String value) { - context.read().setDomain(value); - }, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: Spacings.s), + child: Center( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + const Text( + 'Choose a server where you want to create your account', + ), + Form( + autovalidateMode: AutovalidateMode.always, + child: Column( + children: [ + const SizedBox(height: 10), + ConstrainedBox( + constraints: BoxConstraints.tight(const Size(300, 80)), + child: TextFormField( + autofocus: (Platform.isIOS || Platform.isAndroid) + ? false + : true, + decoration: + const InputDecoration(hintText: 'DOMAIN NAME'), + initialValue: + context.read().state.domain, + style: inputTextStyle, + onChanged: (String value) { + context.read().setDomain(value); + }, + ), ), - ), - ], + ], + ), ), - ), - Column( - crossAxisAlignment: isSmallScreen(context) - ? CrossAxisAlignment.stretch - : CrossAxisAlignment.center, - children: const [_NextButton()], - ) - ], + Column( + crossAxisAlignment: isSmallScreen(context) + ? CrossAxisAlignment.stretch + : CrossAxisAlignment.center, + children: const [_NextButton()], + ) + ], + ), ), ), ), diff --git a/app/lib/registration/username_password.dart b/app/lib/registration/username_password.dart index 56fd9ba0..59802953 100644 --- a/app/lib/registration/username_password.dart +++ b/app/lib/registration/username_password.dart @@ -24,48 +24,52 @@ class UsernamePasswordChoice extends StatelessWidget { toolbarHeight: isPointer() ? 100 : null, leading: const AppBarBackButton(), ), - body: Padding( - padding: const EdgeInsets.all(20.0), - child: Center( - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - Text( - 'Choose a username and password', - style: Theme.of(context).textTheme.labelMedium, - ), - Form( - autovalidateMode: AutovalidateMode.always, - child: Column( - children: [ - const SizedBox(height: 5), - ConstrainedBox( - constraints: BoxConstraints.tight(const Size(300, 80)), - child: const _UsernameTextField(), - ), - const SizedBox(height: 5), - ConstrainedBox( - constraints: BoxConstraints.tight(const Size(300, 80)), - child: TextFormField( - decoration: const InputDecoration(hintText: 'PASSWORD'), - style: inputTextStyle, - obscureText: true, - onChanged: (String value) { - context.read().setPassword(value); - }, + body: SafeArea( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: Spacings.s), + child: Center( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + const Text('Choose a username and password'), + Form( + autovalidateMode: AutovalidateMode.always, + child: Column( + children: [ + const SizedBox(height: 5), + ConstrainedBox( + constraints: BoxConstraints.tight(const Size(300, 80)), + child: const _UsernameTextField(), ), - ) - ], + const SizedBox(height: 5), + ConstrainedBox( + constraints: BoxConstraints.tight(const Size(300, 80)), + child: TextFormField( + initialValue: + context.read().state.password, + decoration: + const InputDecoration(hintText: 'PASSWORD'), + style: inputTextStyle, + obscureText: true, + onChanged: (String value) { + context + .read() + .setPassword(value); + }, + ), + ) + ], + ), ), - ), - Column( - crossAxisAlignment: isSmallScreen(context) - ? CrossAxisAlignment.stretch - : CrossAxisAlignment.center, - children: const [_NextButton()], - ) - ], + Column( + crossAxisAlignment: isSmallScreen(context) + ? CrossAxisAlignment.stretch + : CrossAxisAlignment.center, + children: const [_NextButton()], + ) + ], + ), ), ), ), @@ -80,6 +84,7 @@ class _UsernameTextField extends StatelessWidget { Widget build(BuildContext context) { return TextFormField( autofocus: (Platform.isIOS || Platform.isAndroid) ? false : true, + initialValue: context.read().state.username, decoration: const InputDecoration(hintText: 'USERNAME'), style: inputTextStyle, validator: _usernameValidator, diff --git a/app/test/mocks.dart b/app/test/mocks.dart index 939daa5f..5ebec70d 100644 --- a/app/test/mocks.dart +++ b/app/test/mocks.dart @@ -12,6 +12,7 @@ import 'package:prototype/core/core.dart'; import 'package:prototype/message_list/message_cubit.dart'; import 'package:prototype/message_list/message_list_cubit.dart'; import 'package:prototype/navigation/navigation.dart'; +import 'package:prototype/registration/registration.dart'; import 'package:prototype/user/user.dart'; class MockNavigationCubit extends MockCubit @@ -94,3 +95,6 @@ class MockLoadableUserCubit extends MockCubit implements LoadableUserCubit {} class MockUser extends Mock implements User {} + +class MockRegistrationCubit extends MockCubit + implements RegistrationCubit {} diff --git a/app/test/registration/display_name_picture_test.dart b/app/test/registration/display_name_picture_test.dart new file mode 100644 index 00000000..9464c494 --- /dev/null +++ b/app/test/registration/display_name_picture_test.dart @@ -0,0 +1,67 @@ +// SPDX-FileCopyrightText: 2025 Phoenix R&D GmbH +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:prototype/registration/registration.dart'; +import 'package:prototype/theme/theme.dart'; + +import '../mocks.dart'; + +void main() { + group('DisplayNameAvatarChoice', () { + late MockRegistrationCubit registrationCubit; + + setUp(() async { + registrationCubit = MockRegistrationCubit(); + }); + + Widget buildSubject() => MultiBlocProvider( + providers: [ + BlocProvider.value( + value: registrationCubit, + ), + ], + child: Builder( + builder: (context) { + return MaterialApp( + debugShowCheckedModeBanner: false, + theme: themeData(context), + home: const Scaffold( + body: DisplayNameAvatarChoice(), + ), + ); + }, + ), + ); + + testWidgets('renders correctly when empty', (tester) async { + when(() => registrationCubit.state).thenReturn(const RegistrationState()); + + await tester.pumpWidget(buildSubject()); + + await expectLater( + find.byType(MaterialApp), + matchesGoldenFile('goldens/display_name_avatar_choice_empty.png'), + ); + }); + + testWidgets('renders correctly', (tester) async { + when(() => registrationCubit.state).thenReturn( + const RegistrationState( + displayName: "Alice", + ), + ); + + await tester.pumpWidget(buildSubject()); + + await expectLater( + find.byType(MaterialApp), + matchesGoldenFile('goldens/display_name_avatar_choice.png'), + ); + }); + }); +} diff --git a/app/test/registration/goldens/display_name_avatar_choice.png b/app/test/registration/goldens/display_name_avatar_choice.png new file mode 100644 index 00000000..e4468dfc --- /dev/null +++ b/app/test/registration/goldens/display_name_avatar_choice.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5cc06b206aa25727cf56f1cc4803af4ec619f50716b9139885070c3d9d528815 +size 45736 diff --git a/app/test/registration/goldens/display_name_avatar_choice_empty.png b/app/test/registration/goldens/display_name_avatar_choice_empty.png new file mode 100644 index 00000000..2a7237a3 --- /dev/null +++ b/app/test/registration/goldens/display_name_avatar_choice_empty.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2acf4cee69aba9ccc5732509b06f03694649469ee68db1ee061a80a6b0dd55f2 +size 47020 diff --git a/app/test/registration/goldens/server_choice.png b/app/test/registration/goldens/server_choice.png new file mode 100644 index 00000000..29fe7e8d --- /dev/null +++ b/app/test/registration/goldens/server_choice.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ade8a5cf26dfe91cc64967ee22e0d650ed44952f7bee134576917de72682d832 +size 44891 diff --git a/app/test/registration/goldens/server_choice_empty.png b/app/test/registration/goldens/server_choice_empty.png new file mode 100644 index 00000000..ae135ad8 --- /dev/null +++ b/app/test/registration/goldens/server_choice_empty.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:80916375ed712b2b48a1060c60e599e5b4ee3e4978b727af3b371e89691514d0 +size 41623 diff --git a/app/test/registration/goldens/username_password_choice.png b/app/test/registration/goldens/username_password_choice.png new file mode 100644 index 00000000..b370afb2 --- /dev/null +++ b/app/test/registration/goldens/username_password_choice.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:630b9dd7797ede5a756aaa0dadd94af80cc6cf85c3db4e308e1cab39ddbb8cd3 +size 40128 diff --git a/app/test/registration/goldens/username_password_choice_empty.png b/app/test/registration/goldens/username_password_choice_empty.png new file mode 100644 index 00000000..fcaa5369 --- /dev/null +++ b/app/test/registration/goldens/username_password_choice_empty.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a54c9fe8866d403036abd68265112cfb0ad1a4f88b9bae58d04e0ace976d620d +size 43501 diff --git a/app/test/registration/server_choice_test.dart b/app/test/registration/server_choice_test.dart new file mode 100644 index 00000000..79626a48 --- /dev/null +++ b/app/test/registration/server_choice_test.dart @@ -0,0 +1,64 @@ +// SPDX-FileCopyrightText: 2025 Phoenix R&D GmbH +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:prototype/registration/registration.dart'; +import 'package:prototype/theme/theme.dart'; + +import '../mocks.dart'; + +void main() { + group('ServerChoice', () { + late MockRegistrationCubit registrationCubit; + + setUp(() async { + registrationCubit = MockRegistrationCubit(); + }); + + Widget buildSubject() => MultiBlocProvider( + providers: [ + BlocProvider.value( + value: registrationCubit, + ), + ], + child: Builder( + builder: (context) { + return MaterialApp( + debugShowCheckedModeBanner: false, + theme: themeData(context), + home: const Scaffold( + body: ServerChoice(), + ), + ); + }, + ), + ); + + testWidgets('renders correctly when empty', (tester) async { + when(() => registrationCubit.state).thenReturn(const RegistrationState()); + + await tester.pumpWidget(buildSubject()); + + await expectLater( + find.byType(MaterialApp), + matchesGoldenFile('goldens/server_choice_empty.png'), + ); + }); + + testWidgets('renders correctly', (tester) async { + when(() => registrationCubit.state) + .thenReturn(const RegistrationState(domain: 'example.com')); + + await tester.pumpWidget(buildSubject()); + + await expectLater( + find.byType(MaterialApp), + matchesGoldenFile('goldens/server_choice.png'), + ); + }); + }); +} diff --git a/app/test/registration/username_password_test.dart b/app/test/registration/username_password_test.dart new file mode 100644 index 00000000..68a6e98f --- /dev/null +++ b/app/test/registration/username_password_test.dart @@ -0,0 +1,70 @@ +// SPDX-FileCopyrightText: 2025 Phoenix R&D GmbH +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:prototype/registration/registration.dart'; +import 'package:prototype/theme/theme.dart'; + +import '../mocks.dart'; + +void main() { + group('UsernamePasswordChoice', () { + late MockRegistrationCubit registrationCubit; + + setUp(() async { + registrationCubit = MockRegistrationCubit(); + }); + + Widget buildSubject() => MultiBlocProvider( + providers: [ + BlocProvider.value( + value: registrationCubit, + ), + ], + child: Builder( + builder: (context) { + return MaterialApp( + debugShowCheckedModeBanner: false, + theme: themeData(context), + home: const Scaffold( + body: UsernamePasswordChoice(), + ), + ); + }, + ), + ); + + testWidgets('renders correctly when empty', (tester) async { + when(() => registrationCubit.state).thenReturn(const RegistrationState()); + + await tester.pumpWidget(buildSubject()); + + await expectLater( + find.byType(MaterialApp), + matchesGoldenFile('goldens/username_password_choice_empty.png'), + ); + }); + + testWidgets('renders correctly', (tester) async { + when(() => registrationCubit.state).thenReturn( + const RegistrationState( + username: "alice", + password: "test", + isUsernameValid: true, + isPasswordValid: true, + ), + ); + + await tester.pumpWidget(buildSubject()); + + await expectLater( + find.byType(MaterialApp), + matchesGoldenFile('goldens/username_password_choice.png'), + ); + }); + }); +}