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
// Created Date: 19/08/2022 15:24:37
// Last Modified: Fri Nov 11 2022
// Last Modified: Tue Nov 15 2022
// -----
// Copyright (c) 2022
@ -59,8 +59,24 @@ class _SignInButton extends StatelessWidget {
return status.isSubmissionInProgress
? const CircularProgressIndicator()
: ElevatedButton(
onPressed: status.isValidated ? () => cubit.submit() : null,
child: const Text('Sign in'),
onPressed: status.isValidated ? () => cubit.signInWithEmailAndPassword() : null,
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: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
_EmailInput(),
const SizedBox(height: 8),
_PasswordInput(),
const SizedBox(height: 16),
_SignInButton(),
const SizedBox(height: 16),
_SignInAnonymouslyButton(),
],
),
),

View File

@ -16,9 +16,7 @@
import 'dart:async';
import 'package:wyatt_authentication_bloc/src/core/constants/form_field.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_authentication_bloc/wyatt_authentication_bloc.dart';
import 'package:wyatt_form_bloc/wyatt_form_bloc.dart';
import 'package:wyatt_type_utils/wyatt_type_utils.dart';
@ -82,6 +80,41 @@ class SignInCubit<Extra> extends FormDataCubit<SignInState> {
@override
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) {
return;
}
@ -117,28 +150,22 @@ class SignInCubit<Extra> extends FormDataCubit<SignInState> {
);
}
@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);
FutureOr<void> signInAnonymously() async {
if (state.status.isSubmissionInProgress) {
return;
}
emit(state.copyWith(status: FormStatus.submissionInProgress));
final uid = await _authenticationRepository.signInAnonymously();
emit(
state.copyWith(
form: newForm,
status: newForm.validate(),
),
);
}
@override
FutureOr<void> validate() {
emit(
state.copyWith(
status: _formRepository.accessForm(formName).validate(),
uid.fold(
(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));
when(
() => authenticationRepository.signInAnonymously(),
).thenAnswer((_) async => Ok(account));
when(
() => authenticationRepository.formRepository,
).thenAnswer((_) => formRepository);
@ -258,13 +262,38 @@ void main() {
);
});
group('submit', () {
group('signInWithEmailAndPassword()', () {
blocTest<SignInCubit<int>, SignInState>(
'does nothing when status is not validated',
build: () => SignInCubit(
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>[],
);
@ -308,7 +337,7 @@ void main() {
),
status: FormStatus.valid,
),
act: (cubit) => cubit.submit(),
act: (cubit) => cubit.signInWithEmailAndPassword(),
verify: (_) {
verify(
() => authenticationRepository.signInWithEmailAndPassword(
@ -360,7 +389,7 @@ void main() {
),
status: FormStatus.valid,
),
act: (cubit) => cubit.submit(),
act: (cubit) => cubit.signInWithEmailAndPassword(),
expect: () => <SignInState>[
SignInState(
form: WyattFormImpl(
@ -444,7 +473,170 @@ void main() {
),
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>[
SignInState(
form: WyattFormImpl(