fix(auth)!: add signInWithEmailAndPassword and signInAnonymously methods in SignInCubit #26

This commit is contained in:
Hugo Pointcheval 2022-11-15 19:25:20 -05:00
parent 4fd2a2a16c
commit 31ad460757
Signed by: hugo
GPG Key ID: A9E8E9615379254F
4 changed files with 273 additions and 32 deletions

View File

@ -1 +1,4 @@
include: package:wyatt_analysis/analysis_options.flutter.yaml include: package:wyatt_analysis/analysis_options.flutter.yaml
analyzer:
exclude: "!example/**"

View File

@ -3,7 +3,7 @@
// ----- // -----
// File: sign_in_form.dart // File: sign_in_form.dart
// Created Date: 19/08/2022 15:24:37 // Created Date: 19/08/2022 15:24:37
// Last Modified: Fri Nov 11 2022 // Last Modified: Tue Nov 15 2022
// ----- // -----
// Copyright (c) 2022 // Copyright (c) 2022
@ -59,8 +59,24 @@ class _SignInButton extends StatelessWidget {
return status.isSubmissionInProgress return status.isSubmissionInProgress
? const CircularProgressIndicator() ? const CircularProgressIndicator()
: ElevatedButton( : ElevatedButton(
onPressed: status.isValidated ? () => cubit.submit() : null, onPressed: status.isValidated ? () => cubit.signInWithEmailAndPassword() : null,
child: const Text('Sign in'), child: const Text('Sign in with credentials'),
);
}),
);
}
}
class _SignInAnonymouslyButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return SubmitBuilder<SignInCubit<int>>(
builder: ((context, cubit, status) {
return status.isSubmissionInProgress
? const CircularProgressIndicator()
: ElevatedButton(
onPressed: () => cubit.signInAnonymously(),
child: const Text('Sign in anonymously'),
); );
}), }),
); );
@ -80,12 +96,15 @@ class SignInForm extends StatelessWidget {
), ),
child: SingleChildScrollView( child: SingleChildScrollView(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
_EmailInput(), _EmailInput(),
const SizedBox(height: 8), const SizedBox(height: 8),
_PasswordInput(), _PasswordInput(),
const SizedBox(height: 16), const SizedBox(height: 16),
_SignInButton(), _SignInButton(),
const SizedBox(height: 16),
_SignInAnonymouslyButton(),
], ],
), ),
), ),

View File

@ -16,9 +16,7 @@
import 'dart:async'; import 'dart:async';
import 'package:wyatt_authentication_bloc/src/core/constants/form_field.dart'; import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart';
import 'package:wyatt_authentication_bloc/src/core/constants/form_name.dart';
import 'package:wyatt_authentication_bloc/src/domain/repositories/authentication_repository.dart';
import 'package:wyatt_form_bloc/wyatt_form_bloc.dart'; import 'package:wyatt_form_bloc/wyatt_form_bloc.dart';
import 'package:wyatt_type_utils/wyatt_type_utils.dart'; import 'package:wyatt_type_utils/wyatt_type_utils.dart';
@ -82,6 +80,41 @@ class SignInCubit<Extra> extends FormDataCubit<SignInState> {
@override @override
FutureOr<void> submit() async { FutureOr<void> submit() async {
throw UnimplementedError('`submit()` is not implemented for SignInCubit, '
'please use `signInWithEmailAndPassword()` or `signInAnonymously()`');
}
@override
FutureOr<void> update(
WyattForm form, {
SetOperation operation = SetOperation.replace,
}) {
final WyattForm current = _formRepository.accessForm(formName).clone();
final WyattForm newForm = operation.operation.call(current, form);
_formRepository.updateForm(newForm);
emit(
state.copyWith(
form: newForm,
status: newForm.validate(),
),
);
}
@override
FutureOr<void> validate() {
emit(
state.copyWith(
status: _formRepository.accessForm(formName).validate(),
),
);
}
FutureOr<void> signInWithEmailAndPassword() async {
if (state.status.isSubmissionInProgress) {
return;
}
if (!state.status.isValidated) { if (!state.status.isValidated) {
return; return;
} }
@ -117,28 +150,22 @@ class SignInCubit<Extra> extends FormDataCubit<SignInState> {
); );
} }
@override FutureOr<void> signInAnonymously() async {
FutureOr<void> update( if (state.status.isSubmissionInProgress) {
WyattForm form, { return;
SetOperation operation = SetOperation.replace,
}) {
final WyattForm current = _formRepository.accessForm(formName).clone();
final WyattForm newForm = operation.operation.call(current, form);
_formRepository.updateForm(newForm);
emit(
state.copyWith(
form: newForm,
status: newForm.validate(),
),
);
} }
@override emit(state.copyWith(status: FormStatus.submissionInProgress));
FutureOr<void> validate() {
final uid = await _authenticationRepository.signInAnonymously();
emit( emit(
state.copyWith( uid.fold(
status: _formRepository.accessForm(formName).validate(), (value) => state.copyWith(status: FormStatus.submissionSuccess),
(error) => state.copyWith(
errorMessage: error.message,
status: FormStatus.submissionFailure,
),
), ),
); );
} }

View File

@ -65,6 +65,10 @@ void main() {
), ),
).thenAnswer((_) async => Ok(account)); ).thenAnswer((_) async => Ok(account));
when(
() => authenticationRepository.signInAnonymously(),
).thenAnswer((_) async => Ok(account));
when( when(
() => authenticationRepository.formRepository, () => authenticationRepository.formRepository,
).thenAnswer((_) => formRepository); ).thenAnswer((_) => formRepository);
@ -258,13 +262,38 @@ void main() {
); );
}); });
group('submit', () { group('signInWithEmailAndPassword()', () {
blocTest<SignInCubit<int>, SignInState>( blocTest<SignInCubit<int>, SignInState>(
'does nothing when status is not validated', 'does nothing when status is not validated',
build: () => SignInCubit( build: () => SignInCubit(
authenticationRepository: authenticationRepository, authenticationRepository: authenticationRepository,
), ),
act: (cubit) => cubit.submit(), act: (cubit) => cubit.signInWithEmailAndPassword(),
expect: () => const <SignInState>[],
);
blocTest<SignInCubit<int>, SignInState>(
'does nothing when status is submitInProgress',
build: () => SignInCubit(
authenticationRepository: authenticationRepository,
),
seed: () => SignInState(
form: WyattFormImpl(
[
FormInput(
AuthFormField.email,
const Email.dirty(validEmailString),
),
FormInput(
AuthFormField.password,
const Password.dirty(validPasswordString),
)
],
name: AuthFormName.signInForm,
),
status: FormStatus.submissionInProgress,
),
act: (cubit) => cubit.signInWithEmailAndPassword(),
expect: () => const <SignInState>[], expect: () => const <SignInState>[],
); );
@ -308,7 +337,7 @@ void main() {
), ),
status: FormStatus.valid, status: FormStatus.valid,
), ),
act: (cubit) => cubit.submit(), act: (cubit) => cubit.signInWithEmailAndPassword(),
verify: (_) { verify: (_) {
verify( verify(
() => authenticationRepository.signInWithEmailAndPassword( () => authenticationRepository.signInWithEmailAndPassword(
@ -360,7 +389,7 @@ void main() {
), ),
status: FormStatus.valid, status: FormStatus.valid,
), ),
act: (cubit) => cubit.submit(), act: (cubit) => cubit.signInWithEmailAndPassword(),
expect: () => <SignInState>[ expect: () => <SignInState>[
SignInState( SignInState(
form: WyattFormImpl( form: WyattFormImpl(
@ -444,7 +473,170 @@ void main() {
), ),
status: FormStatus.valid, status: FormStatus.valid,
), ),
act: (cubit) => cubit.submit(), act: (cubit) => cubit.signInWithEmailAndPassword(),
expect: () => <SignInState>[
SignInState(
form: WyattFormImpl(
[
FormInput(
AuthFormField.email,
const Email.dirty(validEmailString),
),
FormInput(
AuthFormField.password,
const Password.dirty(validPasswordString),
)
],
name: AuthFormName.signInForm,
),
status: FormStatus.submissionInProgress,
),
SignInState(
form: WyattFormImpl(
[
FormInput(
AuthFormField.email,
const Email.dirty(validEmailString),
),
FormInput(
AuthFormField.password,
const Password.dirty(validPasswordString),
)
],
name: AuthFormName.signInForm,
),
status: FormStatus.submissionFailure,
)
],
);
});
group('signInAnonymously()', () {
blocTest<SignInCubit<int>, SignInState>(
'does nothing when status is submitInProgress',
build: () => SignInCubit(
authenticationRepository: authenticationRepository,
),
seed: () => SignInState(
form: WyattFormImpl(
[
FormInput(
AuthFormField.email,
const Email.dirty(validEmailString),
),
FormInput(
AuthFormField.password,
const Password.dirty(validPasswordString),
)
],
name: AuthFormName.signInForm,
),
status: FormStatus.submissionInProgress,
),
act: (cubit) => cubit.signInAnonymously(),
expect: () => const <SignInState>[],
);
blocTest<SignInCubit<int>, SignInState>(
'calls signInAnonymously',
build: () => SignInCubit(
authenticationRepository: authenticationRepository,
),
act: (cubit) => cubit.signInAnonymously(),
verify: (_) {
verify(
() => authenticationRepository.signInAnonymously(),
).called(1);
},
);
blocTest<SignInCubit<int>, SignInState>(
'emits [submissionInProgress, submissionSuccess] '
'when signInAnonymously succeeds',
build: () => SignInCubit(
authenticationRepository: authenticationRepository,
),
seed: () => SignInState(
form: WyattFormImpl(
[
FormInput(
AuthFormField.email,
const Email.dirty(validEmailString),
),
FormInput(
AuthFormField.password,
const Password.dirty(validPasswordString),
)
],
name: AuthFormName.signInForm,
),
status: FormStatus.valid,
),
act: (cubit) => cubit.signInAnonymously(),
expect: () => <SignInState>[
SignInState(
form: WyattFormImpl(
[
FormInput(
AuthFormField.email,
const Email.dirty(validEmailString),
),
FormInput(
AuthFormField.password,
const Password.dirty(validPasswordString),
)
],
name: AuthFormName.signInForm,
),
status: FormStatus.submissionInProgress,
),
SignInState(
form: WyattFormImpl(
[
FormInput(
AuthFormField.email,
const Email.dirty(validEmailString),
),
FormInput(
AuthFormField.password,
const Password.dirty(validPasswordString),
)
],
name: AuthFormName.signInForm,
),
status: FormStatus.submissionSuccess,
)
],
);
blocTest<SignInCubit<int>, SignInState>(
'emits [submissionInProgress, submissionFailure] '
'when signInAnonymously fails',
setUp: () {
when(
() => authenticationRepository.signInAnonymously(),
).thenAnswer((_) async => Err(ServerException()));
},
build: () => SignInCubit(
authenticationRepository: authenticationRepository,
),
seed: () => SignInState(
form: WyattFormImpl(
[
FormInput(
AuthFormField.email,
const Email.dirty(validEmailString),
),
FormInput(
AuthFormField.password,
const Password.dirty(validPasswordString),
)
],
name: AuthFormName.signInForm,
),
status: FormStatus.valid,
),
act: (cubit) => cubit.signInAnonymously(),
expect: () => <SignInState>[ expect: () => <SignInState>[
SignInState( SignInState(
form: WyattFormImpl( form: WyattFormImpl(