test(auth): add tests for all features

This commit is contained in:
Hugo Pointcheval 2022-11-11 16:54:08 -05:00
parent 03a51b97ad
commit 38480d84f4
Signed by: hugo
GPG Key ID: A9E8E9615379254F
10 changed files with 1463 additions and 321 deletions

View File

@ -17,100 +17,114 @@
import 'package:bloc_test/bloc_test.dart'; import 'package:bloc_test/bloc_test.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart'; import 'package:mocktail/mocktail.dart';
import 'package:wyatt_architecture/wyatt_architecture.dart';
import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart'; import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart';
import 'package:wyatt_type_utils/wyatt_type_utils.dart';
class MockAuthenticationRepository extends Mock class MockAuthenticationRepository extends Mock
implements AuthenticationRepository {} implements AuthenticationRepository<int> {}
class MockUser extends Mock implements User {} class MockAccount extends Mock implements Account {}
void main() { void main() {
group('AuthenticationCubit<T>', () { group('AuthenticationCubit<T>', () {
final MockUser user = MockUser(); final MockAccount account = MockAccount();
late AuthenticationRepository authenticationRepository; final AccountWrapper<int> wrapper = AccountWrapperModel(account, 10);
late AuthenticationRepository<int> authenticationRepository;
setUp(() { setUp(() {
authenticationRepository = MockAuthenticationRepository(); authenticationRepository = MockAuthenticationRepository();
when(() => authenticationRepository.user).thenAnswer( when(() => authenticationRepository.streamAccount()).thenAnswer(
(_) => const Stream.empty(), (_) => const Stream.empty(),
); );
when(() => authenticationRepository.cubitStatus).thenAnswer(
(_) => Stream.fromIterable([AuthCubitStatus.stoped]),
);
when( when(
() => authenticationRepository.currentUser, () => authenticationRepository.getAccount(),
).thenReturn(user); ).thenAnswer((_) async => Ok(account));
}); });
test('initial auth state is `unknown`', () { test('initial auth state is `unknown`', () {
expect( expect(
AuthenticationCubit<void>( AuthenticationCubit<int>(
authenticationRepository: authenticationRepository, authenticationRepository: authenticationRepository,
).state, ).state,
const AuthenticationState<Never>.unknown(), const AuthenticationState<Never>.unknown(),
); );
}); });
test('initial cubit status is `stoped`', () async { group('ListenForAuthenticationChanges', () {
expect( blocTest<AuthenticationCubit<int>, AuthenticationState<int>>(
await AuthenticationCubit<void>( 'emits authenticated when stream contains account',
authenticationRepository: authenticationRepository,
).status,
AuthCubitStatus.stoped,
);
});
group('UserChanged', () {
blocTest<AuthenticationCubit<void>, AuthenticationState<void>>(
'emits authenticated when user is not empty',
setUp: () { setUp: () {
when(() => user.isNotEmpty).thenReturn(true); when(() => authenticationRepository.streamAccount()).thenAnswer(
when(() => authenticationRepository.user).thenAnswer( (_) => Stream.fromIterable([
(_) => Stream.value(user), Future.value(
Ok(wrapper),
)
]),
); );
when(() => authenticationRepository.cubitStatus).thenAnswer(
(_) => Stream.value(AuthCubitStatus.started),
);
},
build: () => AuthenticationCubit(
authenticationRepository: authenticationRepository,
)..init(),
seed: () => const AuthenticationState.unknown(),
expect: () => [AuthenticationState<void>.authenticated(user, null)],
);
blocTest<AuthenticationCubit<void>, AuthenticationState<void>>(
'emits unauthenticated when user is empty',
setUp: () {
when(() => user.isEmpty).thenReturn(true);
when(() => user.isNotEmpty).thenReturn(false);
when(() => authenticationRepository.user).thenAnswer(
(_) => Stream.value(user),
);
when(() => authenticationRepository.cubitStatus).thenAnswer(
(_) => Stream.value(AuthCubitStatus.started),
);
},
build: () => AuthenticationCubit(
authenticationRepository: authenticationRepository,
)..init(),
seed: () => const AuthenticationState.unknown(),
expect: () => [const AuthenticationState<Never>.unauthenticated()],
);
});
group('LogoutRequested', () {
blocTest<AuthenticationCubit<void>, AuthenticationState<void>>(
'invokes signOut',
setUp: () {
when(
() => authenticationRepository.signOut(),
).thenAnswer((_) async {});
}, },
build: () => AuthenticationCubit( build: () => AuthenticationCubit(
authenticationRepository: authenticationRepository, authenticationRepository: authenticationRepository,
), ),
act: (cubit) => cubit.logOut(), seed: () => const AuthenticationState.unknown(),
expect: () => [AuthenticationState<int>.authenticated(wrapper)],
);
blocTest<AuthenticationCubit<int>, AuthenticationState<int>>(
'emits unauthenticated when account stream is empty',
setUp: () {
when(
() => authenticationRepository.destroyCache(),
).thenAnswer((_) async => const Ok(null));
when(() => authenticationRepository.streamAccount()).thenAnswer(
(_) => Stream.fromIterable([
Future.value(
Ok(AccountWrapperModel(null, 1)),
)
]),
);
},
build: () => AuthenticationCubit(
authenticationRepository: authenticationRepository,
),
seed: () => const AuthenticationState.unknown(),
expect: () => [const AuthenticationState<int>.unauthenticated()],
);
blocTest<AuthenticationCubit<int>, AuthenticationState<int>>(
'emits unauthenticated when there is an error in stream',
setUp: () {
when(
() => authenticationRepository.destroyCache(),
).thenAnswer((_) async => const Ok(null));
when(() => authenticationRepository.streamAccount()).thenAnswer(
(_) => Stream.fromIterable([
Future.value(
Err(ServerException()),
)
]),
);
},
build: () => AuthenticationCubit(
authenticationRepository: authenticationRepository,
),
seed: () => const AuthenticationState.unknown(),
expect: () => [const AuthenticationState<int>.unauthenticated()],
);
});
group('SignOut', () {
blocTest<AuthenticationCubit<int>, AuthenticationState<int>>(
'invokes signOut',
setUp: () {
when(
() => authenticationRepository.signOut(),
).thenAnswer((_) async => const Ok(null));
},
build: () => AuthenticationCubit(
authenticationRepository: authenticationRepository,
),
act: (cubit) => cubit.signOut(),
verify: (_) { verify: (_) {
verify(() => authenticationRepository.signOut()).called(1); verify(() => authenticationRepository.signOut()).called(1);
}, },

View File

@ -18,7 +18,7 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart'; import 'package:mocktail/mocktail.dart';
import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart'; import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart';
class MockUser extends Mock implements User {} class MockAccount extends Mock implements Account {}
void main() { void main() {
group('AuthenticationState', () { group('AuthenticationState', () {
@ -27,29 +27,33 @@ void main() {
const AuthenticationState<void> state = const AuthenticationState<void> state =
AuthenticationState.unauthenticated(); AuthenticationState.unauthenticated();
expect(state.status, AuthenticationStatus.unauthenticated); expect(state.status, AuthenticationStatus.unauthenticated);
expect(state.user, null); expect(state.accountWrapper, null);
}); });
}); });
group('authenticated', () { group('authenticated', () {
test('has correct status', () { test('has correct status', () {
final MockUser user = MockUser(); final MockAccount account = MockAccount();
final AuthenticationState<void> state = final AuthenticationState<void> state =
AuthenticationState.authenticated(user, null); AuthenticationState.authenticated(
AccountWrapperModel<void>(account, null),
);
expect(state.status, AuthenticationStatus.authenticated); expect(state.status, AuthenticationStatus.authenticated);
expect(state.user, user); expect(state.accountWrapper?.account, account);
}); });
}); });
group('authenticated with extra data', () { group('authenticated with extra data', () {
test('has correct status', () { test('has correct status', () {
final MockUser user = MockUser(); final MockAccount account = MockAccount();
const String extra = 'AwesomeExtraData'; const String extra = 'AwesomeExtraData';
final AuthenticationState<String> state = final AuthenticationState<String> state =
AuthenticationState.authenticated(user, extra); AuthenticationState.authenticated(
AccountWrapperModel(account, extra),
);
expect(state.status, AuthenticationStatus.authenticated); expect(state.status, AuthenticationStatus.authenticated);
expect(state.user, user); expect(state.accountWrapper?.account, account);
expect(state.extra, extra); expect(state.accountWrapper?.data, extra);
}); });
}); });
}); });

View File

@ -0,0 +1,249 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:bloc_test/bloc_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:wyatt_architecture/wyatt_architecture.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';
class MockAuthenticationRepository extends Mock
implements AuthenticationRepository<int> {}
class MockAuthenticationCubit extends Mock implements AuthenticationCubit<int> {
}
class MockAccount extends Mock implements Account {}
class MockFormRepository extends Mock implements FormRepository {}
void main() {
group('EmailVerificationCubit<T>', () {
late MockAccount account;
late AuthenticationRepository<int> authenticationRepository;
setUp(() {
authenticationRepository = MockAuthenticationRepository();
when(
() => authenticationRepository.getAccount(),
).thenAnswer((_) async => Ok(account));
when(
() => authenticationRepository.refresh(),
).thenAnswer((_) async => const Ok(null));
account = MockAccount();
when(
() => account.emailVerified,
).thenAnswer((_) => true);
});
test('initial state is `false`', () {
expect(
EmailVerificationCubit<int>(
authenticationRepository: authenticationRepository,
).state,
const EmailVerificationState(),
);
});
group('SendVerificationEmail', () {
blocTest<EmailVerificationCubit<int>, EmailVerificationState>(
'invokes sendEmailVerification,',
setUp: () {
when(() => authenticationRepository.sendEmailVerification())
.thenAnswer((_) async => const Ok(null));
},
build: () => EmailVerificationCubit(
authenticationRepository: authenticationRepository,
),
act: (cubit) => cubit.sendEmailVerification(),
verify: (_) {
verify(() => authenticationRepository.sendEmailVerification())
.called(1);
},
);
blocTest<EmailVerificationCubit<int>, EmailVerificationState>(
'emits success',
setUp: () {
when(() => authenticationRepository.sendEmailVerification())
.thenAnswer((_) async => const Ok(null));
},
build: () => EmailVerificationCubit(
authenticationRepository: authenticationRepository,
),
seed: () => const EmailVerificationState(),
act: (cubit) => cubit.sendEmailVerification(),
expect: () => [
const EmailVerificationState(
status: FormStatus.submissionInProgress,
),
const EmailVerificationState(
status: FormStatus.submissionSuccess,
)
],
);
blocTest<EmailVerificationCubit<int>, EmailVerificationState>(
'emits failure',
setUp: () {
when(() => authenticationRepository.sendEmailVerification())
.thenAnswer((_) async => Err(ServerException('erreur')));
},
build: () => EmailVerificationCubit(
authenticationRepository: authenticationRepository,
),
seed: () => const EmailVerificationState(),
act: (cubit) => cubit.sendEmailVerification(),
expect: () => [
const EmailVerificationState(
status: FormStatus.submissionInProgress,
),
const EmailVerificationState(
errorMessage: 'erreur',
status: FormStatus.submissionFailure,
)
],
);
});
group('CheckEmailVerification', () {
blocTest<EmailVerificationCubit<int>, EmailVerificationState>(
'invokes refresh,',
setUp: () {
when(
() => authenticationRepository.refresh(),
).thenAnswer((_) async => const Ok(null));
},
build: () => EmailVerificationCubit(
authenticationRepository: authenticationRepository,
),
act: (cubit) => cubit.checkEmailVerification(),
verify: (_) {
verify(() => authenticationRepository.refresh()).called(1);
},
);
blocTest<EmailVerificationCubit<int>, EmailVerificationState>(
'invokes emailVerified,',
setUp: () {
when(
() => authenticationRepository.refresh(),
).thenAnswer((_) async => const Ok(null));
when(() => account.emailVerified).thenAnswer((_) => false);
},
build: () => EmailVerificationCubit(
authenticationRepository: authenticationRepository,
),
act: (cubit) => cubit.checkEmailVerification(),
verify: (_) {
verify(() => account.emailVerified).called(1);
},
);
blocTest<EmailVerificationCubit<int>, EmailVerificationState>(
'emits success with true if verified',
setUp: () {
when(() => authenticationRepository.refresh())
.thenAnswer((_) async => const Ok(null));
},
build: () => EmailVerificationCubit(
authenticationRepository: authenticationRepository,
),
seed: () => const EmailVerificationState(),
act: (cubit) => cubit.checkEmailVerification(),
expect: () => [
const EmailVerificationState(
status: FormStatus.submissionInProgress,
),
const EmailVerificationState(
isVerified: true,
status: FormStatus.submissionSuccess,
)
],
);
blocTest<EmailVerificationCubit<int>, EmailVerificationState>(
'emits success with false if not verified',
setUp: () {
when(() => authenticationRepository.refresh())
.thenAnswer((_) async => const Ok(null));
when(() => account.emailVerified).thenAnswer((_) => false);
},
build: () => EmailVerificationCubit(
authenticationRepository: authenticationRepository,
),
seed: () => const EmailVerificationState(),
act: (cubit) => cubit.checkEmailVerification(),
expect: () => [
const EmailVerificationState(
status: FormStatus.submissionInProgress,
),
const EmailVerificationState(
status: FormStatus.submissionSuccess,
)
],
);
blocTest<EmailVerificationCubit<int>, EmailVerificationState>(
'emits failure on refresh error',
setUp: () {
when(() => authenticationRepository.refresh())
.thenAnswer((_) async => Err(ServerException('erreur')));
},
build: () => EmailVerificationCubit(
authenticationRepository: authenticationRepository,
),
seed: () => const EmailVerificationState(),
act: (cubit) => cubit.checkEmailVerification(),
expect: () => [
const EmailVerificationState(
status: FormStatus.submissionInProgress,
),
const EmailVerificationState(
errorMessage: 'erreur',
status: FormStatus.submissionFailure,
)
],
);
blocTest<EmailVerificationCubit<int>, EmailVerificationState>(
'emits failure on get account error',
setUp: () {
when(() => authenticationRepository.getAccount())
.thenAnswer((_) async => Err(ServerException('erreur')));
},
build: () => EmailVerificationCubit(
authenticationRepository: authenticationRepository,
),
seed: () => const EmailVerificationState(),
act: (cubit) => cubit.checkEmailVerification(),
expect: () => [
const EmailVerificationState(
status: FormStatus.submissionInProgress,
),
const EmailVerificationState(
errorMessage: 'erreur',
status: FormStatus.submissionFailure,
)
],
);
});
});
}

View File

@ -0,0 +1,48 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:flutter_test/flutter_test.dart';
import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart';
import 'package:wyatt_form_bloc/wyatt_form_bloc.dart';
void main() {
group('EmailVerificationState', () {
test('supports value comparisons', () {
expect(
const EmailVerificationState(isVerified: true),
const EmailVerificationState(isVerified: true),
);
});
test('returns same object when no properties are passed', () {
expect(
const EmailVerificationState(isVerified: true).copyWith(),
const EmailVerificationState(isVerified: true),
);
});
test('returns object with updated status when status is passed', () {
expect(
const EmailVerificationState(isVerified: true)
.copyWith(status: FormStatus.invalid),
const EmailVerificationState(
isVerified: true,
status: FormStatus.invalid,
),
);
});
});
}

View File

@ -0,0 +1,335 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:bloc_test/bloc_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:wyatt_architecture/wyatt_architecture.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';
class MockAuthenticationRepository extends Mock
implements AuthenticationRepository<int> {}
class MockAuthenticationCubit extends Mock implements AuthenticationCubit<int> {
}
class MockAccount extends Mock implements Account {}
class MockFormRepository extends Mock implements FormRepository {}
void main() {
const String invalidEmailString = 'invalid';
const String validEmailString = 'test@gmail.com';
group('PasswordResetCubit', () {
final WyattForm form = WyattFormImpl(
[
FormInput(AuthFormField.email, const Email.pure()),
FormInput(AuthFormField.password, const Password.pure())
],
name: AuthFormName.passwordResetForm,
);
late MockFormRepository formRepository;
late AuthenticationRepository<int> authenticationRepository;
setUp(() {
authenticationRepository = MockAuthenticationRepository();
formRepository = MockFormRepository();
when(
() => authenticationRepository.sendPasswordResetEmail(
email: any(named: 'email'),
),
).thenAnswer((_) async => const Ok(null));
when(
() => authenticationRepository.formRepository,
).thenAnswer((_) => formRepository);
when(
() => formRepository.accessForm(AuthFormName.passwordResetForm),
).thenAnswer((_) => form);
});
test('initial state is pure', () {
expect(
PasswordResetCubit(
authenticationRepository: authenticationRepository,
).state,
PasswordResetState(form: form),
);
});
group('emailChanged', () {
blocTest<PasswordResetCubit<int>, PasswordResetState>(
'emits [invalid] when email is invalid',
build: () => PasswordResetCubit(
authenticationRepository: authenticationRepository,
),
act: (cubit) => cubit.emailChanged(invalidEmailString),
expect: () => <PasswordResetState>[
PasswordResetState(
form: WyattFormImpl(
[
FormInput(
AuthFormField.email,
const Email.dirty(invalidEmailString),
),
],
name: AuthFormName.passwordResetForm,
),
status: FormStatus.invalid,
),
],
);
blocTest<PasswordResetCubit<int>, PasswordResetState>(
'emits [valid] when email is valid',
setUp: () {
when(
() => formRepository.accessForm(AuthFormName.passwordResetForm),
).thenAnswer(
(_) => WyattFormImpl(
[
FormInput(
AuthFormField.email,
const Email.pure(),
),
],
name: AuthFormName.passwordResetForm,
),
);
},
build: () => PasswordResetCubit(
authenticationRepository: authenticationRepository,
),
seed: () => PasswordResetState(
form: WyattFormImpl(
[
FormInput(
AuthFormField.email,
const Email.pure(),
),
],
name: AuthFormName.passwordResetForm,
),
status: FormStatus.invalid,
),
act: (cubit) => cubit.emailChanged(validEmailString),
expect: () => <PasswordResetState>[
PasswordResetState(
form: WyattFormImpl(
[
FormInput(
AuthFormField.email,
const Email.dirty(validEmailString),
),
],
name: AuthFormName.passwordResetForm,
),
status: FormStatus.valid,
),
],
);
});
group('submit', () {
blocTest<PasswordResetCubit<int>, PasswordResetState>(
'does nothing when status is not validated',
build: () => PasswordResetCubit(
authenticationRepository: authenticationRepository,
),
act: (cubit) => cubit.submit(),
expect: () => const <PasswordResetState>[],
);
blocTest<PasswordResetCubit<int>, PasswordResetState>(
'calls sendPasswordResetEmail with correct email',
setUp: () {
when(
() => formRepository.accessForm(AuthFormName.passwordResetForm),
).thenAnswer(
(_) => WyattFormImpl(
[
FormInput(
AuthFormField.email,
const Email.dirty(validEmailString),
),
],
name: AuthFormName.passwordResetForm,
),
);
},
build: () => PasswordResetCubit(
authenticationRepository: authenticationRepository,
),
seed: () => PasswordResetState(
form: WyattFormImpl(
[
FormInput(
AuthFormField.email,
const Email.dirty(validEmailString),
),
],
name: AuthFormName.passwordResetForm,
),
status: FormStatus.valid,
),
act: (cubit) => cubit.submit(),
verify: (_) {
verify(
() => authenticationRepository.sendPasswordResetEmail(
email: validEmailString,
),
).called(1);
},
);
blocTest<PasswordResetCubit<int>, PasswordResetState>(
'emits [submissionInProgress, submissionSuccess] '
'when sendPasswordResetEmail succeeds',
setUp: () {
when(
() => formRepository.accessForm(AuthFormName.passwordResetForm),
).thenAnswer(
(_) => WyattFormImpl(
[
FormInput(
AuthFormField.email,
const Email.dirty(validEmailString),
),
],
name: AuthFormName.passwordResetForm,
),
);
},
build: () => PasswordResetCubit(
authenticationRepository: authenticationRepository,
),
seed: () => PasswordResetState(
form: WyattFormImpl(
[
FormInput(
AuthFormField.email,
const Email.dirty(validEmailString),
),
],
name: AuthFormName.passwordResetForm,
),
status: FormStatus.valid,
),
act: (cubit) => cubit.submit(),
expect: () => <PasswordResetState>[
PasswordResetState(
form: WyattFormImpl(
[
FormInput(
AuthFormField.email,
const Email.dirty(validEmailString),
),
],
name: AuthFormName.passwordResetForm,
),
status: FormStatus.submissionInProgress,
),
PasswordResetState(
form: WyattFormImpl(
[
FormInput(
AuthFormField.email,
const Email.dirty(validEmailString),
),
],
name: AuthFormName.passwordResetForm,
),
status: FormStatus.submissionSuccess,
)
],
);
blocTest<PasswordResetCubit<int>, PasswordResetState>(
'emits [submissionInProgress, submissionFailure] '
'when sendPasswordResetEmail fails',
setUp: () {
when(
() => authenticationRepository.sendPasswordResetEmail(
email: any(named: 'email'),
),
).thenAnswer((_) async => Err(ServerException()));
when(
() => formRepository.accessForm(AuthFormName.passwordResetForm),
).thenAnswer(
(_) => WyattFormImpl(
[
FormInput(
AuthFormField.email,
const Email.dirty(validEmailString),
),
],
name: AuthFormName.passwordResetForm,
),
);
},
build: () => PasswordResetCubit(
authenticationRepository: authenticationRepository,
),
seed: () => PasswordResetState(
form: WyattFormImpl(
[
FormInput(
AuthFormField.email,
const Email.dirty(validEmailString),
),
],
name: AuthFormName.passwordResetForm,
),
status: FormStatus.valid,
),
act: (cubit) => cubit.submit(),
expect: () => <PasswordResetState>[
PasswordResetState(
form: WyattFormImpl(
[
FormInput(
AuthFormField.email,
const Email.dirty(validEmailString),
),
],
name: AuthFormName.passwordResetForm,
),
status: FormStatus.submissionInProgress,
),
PasswordResetState(
form: WyattFormImpl(
[
FormInput(
AuthFormField.email,
const Email.dirty(validEmailString),
),
],
name: AuthFormName.passwordResetForm,
),
status: FormStatus.submissionFailure,
)
],
);
});
});
}

View File

@ -0,0 +1,56 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:flutter_test/flutter_test.dart';
import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart';
import 'package:wyatt_form_bloc/wyatt_form_bloc.dart';
void main() {
final WyattForm form = WyattFormImpl(
[
FormInput(AuthFormField.email, const Email.pure()),
],
name: AuthFormName.passwordResetForm,
);
group('PasswordResetState', () {
test('supports value comparisons', () {
expect(
PasswordResetState(
form: form,
),
PasswordResetState(form: form),
);
});
test('returns same object when no properties are passed', () {
expect(
PasswordResetState(form: form).copyWith(),
PasswordResetState(form: form),
);
});
test('returns object with updated status when status is passed', () {
expect(
PasswordResetState(form: form).copyWith(status: FormStatus.invalid),
PasswordResetState(
form: form,
status: FormStatus.invalid,
),
);
});
});
}

View File

@ -17,46 +17,61 @@
import 'package:bloc_test/bloc_test.dart'; import 'package:bloc_test/bloc_test.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart'; import 'package:mocktail/mocktail.dart';
import 'package:wyatt_architecture/wyatt_architecture.dart';
import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart'; import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.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';
class MockAuthenticationRepository extends Mock class MockAuthenticationRepository extends Mock
implements AuthenticationRepository {} implements AuthenticationRepository<int> {}
class MockAuthenticationCubit extends Mock class MockAuthenticationCubit extends Mock implements AuthenticationCubit<int> {
implements AuthenticationCubit<void> {} }
class MockAccount extends Mock implements Account {}
class MockFormRepository extends Mock implements FormRepository {}
void main() { void main() {
const String invalidEmailString = 'invalid'; const String invalidEmailString = 'invalid';
const Email invalidEmail = Email.dirty(invalidEmailString);
const String validEmailString = 'test@gmail.com'; const String validEmailString = 'test@gmail.com';
const Email validEmail = Email.dirty(validEmailString);
const String invalidPasswordString = 'invalid'; const String invalidPasswordString = 'invalid';
const Password invalidPassword = Password.dirty(invalidPasswordString);
const String validPasswordString = 't0pS3cret1234'; const String validPasswordString = 't0pS3cret1234';
const Password validPassword = Password.dirty(validPasswordString);
group('SignInCubit', () { group('SignInCubit', () {
late AuthenticationRepository authenticationRepository; final MockAccount account = MockAccount();
late AuthenticationCubit<void> authenticationCubit; final WyattForm form = WyattFormImpl(
[
FormInput(AuthFormField.email, const Email.pure()),
FormInput(AuthFormField.password, const Password.pure())
],
name: AuthFormName.signInForm,
);
late MockFormRepository formRepository;
late AuthenticationRepository<int> authenticationRepository;
setUp(() { setUp(() {
authenticationRepository = MockAuthenticationRepository(); authenticationRepository = MockAuthenticationRepository();
authenticationCubit = MockAuthenticationCubit(); formRepository = MockFormRepository();
when( when(
() => authenticationRepository.signInWithEmailAndPassword( () => authenticationRepository.signInWithEmailAndPassword(
email: any(named: 'email'), email: any(named: 'email'),
password: any(named: 'password'), password: any(named: 'password'),
), ),
).thenAnswer((_) async {}); ).thenAnswer((_) async => Ok(account));
when( when(
() => authenticationCubit.start(), () => authenticationRepository.formRepository,
).thenReturn(true); ).thenAnswer((_) => formRepository);
when(
() => formRepository.accessForm(AuthFormName.signInForm),
).thenAnswer((_) => form);
}); });
test('initial state is SignInState', () { test('initial state is SignInState', () {
@ -64,33 +79,90 @@ void main() {
SignInCubit( SignInCubit(
authenticationRepository: authenticationRepository, authenticationRepository: authenticationRepository,
).state, ).state,
const SignInState(), SignInState(form: form),
); );
}); });
group('emailChanged', () { group('emailChanged', () {
blocTest<SignInCubit, SignInState>( blocTest<SignInCubit<int>, SignInState>(
'emits [invalid] when email/password are invalid', 'emits [invalid] when email/password are invalid',
build: () => SignInCubit( build: () => SignInCubit(
authenticationRepository: authenticationRepository, authenticationRepository: authenticationRepository,
), ),
act: (cubit) => cubit.emailChanged(invalidEmailString), act: (cubit) => cubit.emailChanged(invalidEmailString),
expect: () => const <SignInState>[ expect: () => <SignInState>[
SignInState(email: invalidEmail, status: FormStatus.invalid), SignInState(
form: WyattFormImpl(
[
FormInput(
AuthFormField.email,
const Email.dirty(invalidEmailString),
),
FormInput(AuthFormField.password, const Password.pure())
],
name: AuthFormName.signInForm,
),
status: FormStatus.invalid,
),
], ],
); );
blocTest<SignInCubit, SignInState>( blocTest<SignInCubit<int>, SignInState>(
'emits [valid] when email/password are valid', 'emits [valid] when email/password are valid',
setUp: () {
when(
() => formRepository.accessForm(AuthFormName.signInForm),
).thenAnswer(
(_) => WyattFormImpl(
[
FormInput(
AuthFormField.email,
const Email.pure(),
),
FormInput(
AuthFormField.password,
const Password.dirty(validPasswordString),
)
],
name: AuthFormName.signInForm,
),
);
},
build: () => SignInCubit( build: () => SignInCubit(
authenticationRepository: authenticationRepository, authenticationRepository: authenticationRepository,
), ),
seed: () => const SignInState(password: validPassword), seed: () => SignInState(
form: WyattFormImpl(
[
FormInput(
AuthFormField.email,
const Email.pure(),
),
FormInput(
AuthFormField.password,
const Password.dirty(validPasswordString),
)
],
name: AuthFormName.signInForm,
),
status: FormStatus.invalid,
),
act: (cubit) => cubit.emailChanged(validEmailString), act: (cubit) => cubit.emailChanged(validEmailString),
expect: () => const <SignInState>[ expect: () => <SignInState>[
SignInState( SignInState(
email: validEmail, form: WyattFormImpl(
password: validPassword, [
FormInput(
AuthFormField.email,
const Email.dirty(validEmailString),
),
FormInput(
AuthFormField.password,
const Password.dirty(validPasswordString),
)
],
name: AuthFormName.signInForm,
),
status: FormStatus.valid, status: FormStatus.valid,
), ),
], ],
@ -98,58 +170,145 @@ void main() {
}); });
group('passwordChanged', () { group('passwordChanged', () {
blocTest<SignInCubit, SignInState>( blocTest<SignInCubit<int>, SignInState>(
'emits [invalid] when email/password are invalid', 'emits [invalid] when email/password are invalid',
build: () => SignInCubit( build: () => SignInCubit(
authenticationRepository: authenticationRepository, authenticationRepository: authenticationRepository,
), ),
act: (cubit) => cubit.passwordChanged(invalidPasswordString), act: (cubit) => cubit.passwordChanged(invalidPasswordString),
expect: () => const <SignInState>[ expect: () => <SignInState>[
SignInState( SignInState(
password: invalidPassword, form: WyattFormImpl(
[
FormInput(
AuthFormField.email,
const Email.pure(),
),
FormInput(
AuthFormField.password,
const Password.dirty(invalidPasswordString),
)
],
name: AuthFormName.signInForm,
),
status: FormStatus.invalid, status: FormStatus.invalid,
), ),
], ],
); );
blocTest<SignInCubit, SignInState>( blocTest<SignInCubit<int>, SignInState>(
'emits [valid] when email/password are valid', 'emits [valid] when email/password are valid',
setUp: () {
when(
() => formRepository.accessForm(AuthFormName.signInForm),
).thenAnswer(
(_) => WyattFormImpl(
[
FormInput(
AuthFormField.email,
const Email.dirty(validEmailString),
),
FormInput(
AuthFormField.password,
const Password.pure(),
)
],
name: AuthFormName.signInForm,
),
);
},
build: () => SignInCubit( build: () => SignInCubit(
authenticationRepository: authenticationRepository, authenticationRepository: authenticationRepository,
), ),
seed: () => const SignInState(email: validEmail), seed: () => SignInState(
form: WyattFormImpl(
[
FormInput(
AuthFormField.email,
const Email.dirty(validEmailString),
),
FormInput(
AuthFormField.password,
const Password.pure(),
)
],
name: AuthFormName.signInForm,
),
status: FormStatus.invalid,
),
act: (cubit) => cubit.passwordChanged(validPasswordString), act: (cubit) => cubit.passwordChanged(validPasswordString),
expect: () => const <SignInState>[ expect: () => <SignInState>[
SignInState( SignInState(
email: validEmail, form: WyattFormImpl(
password: validPassword, [
FormInput(
AuthFormField.email,
const Email.dirty(validEmailString),
),
FormInput(
AuthFormField.password,
const Password.dirty(validPasswordString),
)
],
name: AuthFormName.signInForm,
),
status: FormStatus.valid, status: FormStatus.valid,
), ),
], ],
); );
}); });
group('logInWithCredentials', () { group('submit', () {
blocTest<SignInCubit, 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.signInWithEmailAndPassword(), act: (cubit) => cubit.submit(),
expect: () => const <SignInState>[], expect: () => const <SignInState>[],
); );
blocTest<SignInCubit, SignInState>( blocTest<SignInCubit<int>, SignInState>(
'calls signInWithEmailAndPassword with correct email/password', 'calls signInWithEmailAndPassword with correct email/password',
setUp: () {
when(
() => formRepository.accessForm(AuthFormName.signInForm),
).thenAnswer(
(_) => WyattFormImpl(
[
FormInput(
AuthFormField.email,
const Email.dirty(validEmailString),
),
FormInput(
AuthFormField.password,
const Password.dirty(validPasswordString),
)
],
name: AuthFormName.signInForm,
),
);
},
build: () => SignInCubit( build: () => SignInCubit(
authenticationRepository: authenticationRepository, authenticationRepository: authenticationRepository,
), ),
seed: () => const SignInState( seed: () => SignInState(
status: FormStatus.valid, form: WyattFormImpl(
email: validEmail, [
password: validPassword, FormInput(
AuthFormField.email,
const Email.dirty(validEmailString),
), ),
act: (cubit) => cubit.signInWithEmailAndPassword(), FormInput(
AuthFormField.password,
const Password.dirty(validPasswordString),
)
],
name: AuthFormName.signInForm,
),
status: FormStatus.valid,
),
act: (cubit) => cubit.submit(),
verify: (_) { verify: (_) {
verify( verify(
() => authenticationRepository.signInWithEmailAndPassword( () => authenticationRepository.signInWithEmailAndPassword(
@ -160,33 +319,85 @@ void main() {
}, },
); );
blocTest<SignInCubit, SignInState>( blocTest<SignInCubit<int>, SignInState>(
'emits [submissionInProgress, submissionSuccess] ' 'emits [submissionInProgress, submissionSuccess] '
'when signInWithEmailAndPassword succeeds', 'when signInWithEmailAndPassword succeeds',
setUp: () {
when(
() => formRepository.accessForm(AuthFormName.signInForm),
).thenAnswer(
(_) => WyattFormImpl(
[
FormInput(
AuthFormField.email,
const Email.dirty(validEmailString),
),
FormInput(
AuthFormField.password,
const Password.dirty(validPasswordString),
)
],
name: AuthFormName.signInForm,
),
);
},
build: () => SignInCubit( build: () => SignInCubit(
authenticationRepository: authenticationRepository, authenticationRepository: authenticationRepository,
), ),
seed: () => const SignInState( seed: () => SignInState(
form: WyattFormImpl(
[
FormInput(
AuthFormField.email,
const Email.dirty(validEmailString),
),
FormInput(
AuthFormField.password,
const Password.dirty(validPasswordString),
)
],
name: AuthFormName.signInForm,
),
status: FormStatus.valid, status: FormStatus.valid,
email: validEmail,
password: validPassword,
), ),
act: (cubit) => cubit.signInWithEmailAndPassword(), act: (cubit) => cubit.submit(),
expect: () => const <SignInState>[ expect: () => <SignInState>[
SignInState( SignInState(
form: WyattFormImpl(
[
FormInput(
AuthFormField.email,
const Email.dirty(validEmailString),
),
FormInput(
AuthFormField.password,
const Password.dirty(validPasswordString),
)
],
name: AuthFormName.signInForm,
),
status: FormStatus.submissionInProgress, status: FormStatus.submissionInProgress,
email: validEmail,
password: validPassword,
), ),
SignInState( SignInState(
form: WyattFormImpl(
[
FormInput(
AuthFormField.email,
const Email.dirty(validEmailString),
),
FormInput(
AuthFormField.password,
const Password.dirty(validPasswordString),
)
],
name: AuthFormName.signInForm,
),
status: FormStatus.submissionSuccess, status: FormStatus.submissionSuccess,
email: validEmail,
password: validPassword,
) )
], ],
); );
blocTest<SignInCubit, SignInState>( blocTest<SignInCubit<int>, SignInState>(
'emits [submissionInProgress, submissionFailure] ' 'emits [submissionInProgress, submissionFailure] '
'when signInWithEmailAndPassword fails', 'when signInWithEmailAndPassword fails',
setUp: () { setUp: () {
@ -195,27 +406,77 @@ void main() {
email: any(named: 'email'), email: any(named: 'email'),
password: any(named: 'password'), password: any(named: 'password'),
), ),
).thenThrow(Exception('oops')); ).thenAnswer((_) async => Err(ServerException()));
when(
() => formRepository.accessForm(AuthFormName.signInForm),
).thenAnswer(
(_) => WyattFormImpl(
[
FormInput(
AuthFormField.email,
const Email.dirty(validEmailString),
),
FormInput(
AuthFormField.password,
const Password.dirty(validPasswordString),
)
],
name: AuthFormName.signInForm,
),
);
}, },
build: () => SignInCubit( build: () => SignInCubit(
authenticationRepository: authenticationRepository, authenticationRepository: authenticationRepository,
), ),
seed: () => const SignInState( seed: () => SignInState(
form: WyattFormImpl(
[
FormInput(
AuthFormField.email,
const Email.dirty(validEmailString),
),
FormInput(
AuthFormField.password,
const Password.dirty(validPasswordString),
)
],
name: AuthFormName.signInForm,
),
status: FormStatus.valid, status: FormStatus.valid,
email: validEmail,
password: validPassword,
), ),
act: (cubit) => cubit.signInWithEmailAndPassword(), act: (cubit) => cubit.submit(),
expect: () => const <SignInState>[ expect: () => <SignInState>[
SignInState( SignInState(
form: WyattFormImpl(
[
FormInput(
AuthFormField.email,
const Email.dirty(validEmailString),
),
FormInput(
AuthFormField.password,
const Password.dirty(validPasswordString),
)
],
name: AuthFormName.signInForm,
),
status: FormStatus.submissionInProgress, status: FormStatus.submissionInProgress,
email: validEmail,
password: validPassword,
), ),
SignInState( SignInState(
form: WyattFormImpl(
[
FormInput(
AuthFormField.email,
const Email.dirty(validEmailString),
),
FormInput(
AuthFormField.password,
const Password.dirty(validPasswordString),
)
],
name: AuthFormName.signInForm,
),
status: FormStatus.submissionFailure, status: FormStatus.submissionFailure,
email: validEmail,
password: validPassword,
) )
], ],
); );

View File

@ -19,36 +19,38 @@ import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart';
import 'package:wyatt_form_bloc/wyatt_form_bloc.dart'; import 'package:wyatt_form_bloc/wyatt_form_bloc.dart';
void main() { void main() {
const Email email = Email.dirty('email'); final WyattForm form = WyattFormImpl(
const Password password = Password.dirty('password'); [
FormInput(AuthFormField.email, const Email.pure()),
FormInput(AuthFormField.password, const Password.pure())
],
name: AuthFormName.signInForm,
);
group('SignInState', () { group('SignInState', () {
test('supports value comparisons', () { test('supports value comparisons', () {
expect(const SignInState(), const SignInState()); expect(
SignInState(
form: form,
),
SignInState(form: form),
);
}); });
test('returns same object when no properties are passed', () { test('returns same object when no properties are passed', () {
expect(const SignInState().copyWith(), const SignInState()); expect(
SignInState(form: form).copyWith(),
SignInState(form: form),
);
}); });
test('returns object with updated status when status is passed', () { test('returns object with updated status when status is passed', () {
expect( expect(
const SignInState().copyWith(status: FormStatus.pure), SignInState(form: form).copyWith(status: FormStatus.invalid),
const SignInState(), SignInState(
); form: form,
}); status: FormStatus.invalid,
),
test('returns object with updated email when email is passed', () {
expect(
const SignInState().copyWith(email: email),
const SignInState(email: email),
);
});
test('returns object with updated password when password is passed', () {
expect(
const SignInState().copyWith(password: password),
const SignInState(password: password),
); );
}); });
}); });

View File

@ -17,163 +17,298 @@
import 'package:bloc_test/bloc_test.dart'; import 'package:bloc_test/bloc_test.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart'; import 'package:mocktail/mocktail.dart';
import 'package:wyatt_architecture/wyatt_architecture.dart';
import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart'; import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.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';
class MockAuthenticationRepository extends Mock class MockAuthenticationRepository extends Mock
implements AuthenticationRepository {} implements AuthenticationRepository<int> {}
class MockAuthenticationCubit extends Mock class MockAuthenticationCubit extends Mock implements AuthenticationCubit<int> {
implements AuthenticationCubit<void> {} }
class MockAccount extends Mock implements Account {}
class MockFormRepository extends Mock implements FormRepository {}
void main() { void main() {
const String invalidEmailString = 'invalid'; const String invalidEmailString = 'invalid';
const Email invalidEmail = Email.dirty(invalidEmailString);
const String validEmailString = 'test@gmail.com'; const String validEmailString = 'test@gmail.com';
const Email validEmail = Email.dirty(validEmailString);
const String invalidPasswordString = 'invalid'; const String invalidPasswordString = 'invalid';
const Password invalidPassword = Password.dirty(invalidPasswordString);
const String validPasswordString = 't0pS3cret1234'; const String validPasswordString = 't0pS3cret1234';
const Password validPassword = Password.dirty(validPasswordString);
group('SignUpCubit', () { group('SignUpCubit', () {
late AuthenticationRepository authenticationRepository; final MockAccount account = MockAccount();
late AuthenticationCubit<void> authenticationCubit; final WyattForm form = WyattFormImpl(
[
FormInput(AuthFormField.email, const Email.pure()),
FormInput(AuthFormField.password, const Password.pure())
],
name: AuthFormName.signUpForm,
);
late MockFormRepository formRepository;
late AuthenticationRepository<int> authenticationRepository;
setUp(() { setUp(() {
authenticationRepository = MockAuthenticationRepository(); authenticationRepository = MockAuthenticationRepository();
authenticationCubit = MockAuthenticationCubit(); formRepository = MockFormRepository();
when( when(
() => authenticationRepository.signUp( () => authenticationRepository.signUp(
email: any(named: 'email'), email: any(named: 'email'),
password: any(named: 'password'), password: any(named: 'password'),
), ),
).thenAnswer((_) async => 'uid'); ).thenAnswer((_) async => Ok(account));
when( when(
() => authenticationCubit.start(), () => authenticationRepository.formRepository,
).thenReturn(true); ).thenAnswer((_) => formRepository);
when( when(
() => authenticationCubit.stop(), () => formRepository.accessForm(AuthFormName.signUpForm),
).thenReturn(true); ).thenAnswer((_) => form);
}); });
test('initial state is SignUpState', () { test('initial state is SignUpState', () {
expect( expect(
SignUpCubit( SignUpCubit(
authenticationRepository: authenticationRepository, authenticationRepository: authenticationRepository,
formData: const FormData.empty(),
).state, ).state,
const SignUpState(data: FormData.empty()), SignUpState(form: form),
); );
}); });
group('emailChanged', () { group('emailChanged', () {
blocTest<SignUpCubit, SignUpState>( blocTest<SignUpCubit<int>, SignUpState>(
'emits [invalid] when email/password are invalid', 'emits [invalid] when email/password are invalid',
build: () => SignUpCubit( build: () => SignUpCubit(
authenticationRepository: authenticationRepository, authenticationRepository: authenticationRepository,
formData: const FormData.empty(),
), ),
act: (cubit) => cubit.emailChanged(invalidEmailString), act: (cubit) => cubit.emailChanged(invalidEmailString),
expect: () => <SignUpState>[ expect: () => <SignUpState>[
const SignUpState( SignUpState(
email: invalidEmail, form: WyattFormImpl(
[
FormInput(
AuthFormField.email,
const Email.dirty(invalidEmailString),
),
FormInput(AuthFormField.password, const Password.pure())
],
name: AuthFormName.signUpForm,
),
status: FormStatus.invalid, status: FormStatus.invalid,
data: FormData.empty(),
), ),
], ],
); );
blocTest<SignUpCubit, SignUpState>( blocTest<SignUpCubit<int>, SignUpState>(
'emits [valid] when email/password are valid', 'emits [valid] when email/password are valid',
setUp: () {
when(
() => formRepository.accessForm(AuthFormName.signUpForm),
).thenAnswer(
(_) => WyattFormImpl(
[
FormInput(
AuthFormField.email,
const Email.pure(),
),
FormInput(
AuthFormField.password,
const Password.dirty(validPasswordString),
)
],
name: AuthFormName.signUpForm,
),
);
},
build: () => SignUpCubit( build: () => SignUpCubit(
authenticationRepository: authenticationRepository, authenticationRepository: authenticationRepository,
formData: const FormData.empty(),
), ),
seed: () => const SignUpState( seed: () => SignUpState(
password: validPassword, form: WyattFormImpl(
data: FormData.empty(), [
FormInput(
AuthFormField.email,
const Email.pure(),
),
FormInput(
AuthFormField.password,
const Password.dirty(validPasswordString),
)
],
name: AuthFormName.signUpForm,
),
status: FormStatus.invalid,
), ),
act: (cubit) => cubit.emailChanged(validEmailString), act: (cubit) => cubit.emailChanged(validEmailString),
expect: () => <SignUpState>[ expect: () => <SignUpState>[
const SignUpState( SignUpState(
email: validEmail, form: WyattFormImpl(
password: validPassword, [
FormInput(
AuthFormField.email,
const Email.dirty(validEmailString),
),
FormInput(
AuthFormField.password,
const Password.dirty(validPasswordString),
)
],
name: AuthFormName.signUpForm,
),
status: FormStatus.valid, status: FormStatus.valid,
data: FormData.empty(),
), ),
], ],
); );
}); });
group('passwordChanged', () { group('passwordChanged', () {
blocTest<SignUpCubit, SignUpState>( blocTest<SignUpCubit<int>, SignUpState>(
'emits [invalid] when email/password are invalid', 'emits [invalid] when email/password are invalid',
build: () => SignUpCubit( build: () => SignUpCubit(
authenticationRepository: authenticationRepository, authenticationRepository: authenticationRepository,
formData: const FormData.empty(),
), ),
act: (cubit) => cubit.passwordChanged(invalidPasswordString), act: (cubit) => cubit.passwordChanged(invalidPasswordString),
expect: () => <SignUpState>[ expect: () => <SignUpState>[
const SignUpState( SignUpState(
password: invalidPassword, form: WyattFormImpl(
[
FormInput(
AuthFormField.email,
const Email.pure(),
),
FormInput(
AuthFormField.password,
const Password.dirty(invalidPasswordString),
)
],
name: AuthFormName.signUpForm,
),
status: FormStatus.invalid, status: FormStatus.invalid,
data: FormData.empty(),
), ),
], ],
); );
blocTest<SignUpCubit, SignUpState>( blocTest<SignUpCubit<int>, SignUpState>(
'emits [valid] when email/password are valid', 'emits [valid] when email/password are valid',
setUp: () {
when(
() => formRepository.accessForm(AuthFormName.signUpForm),
).thenAnswer(
(_) => WyattFormImpl(
[
FormInput(
AuthFormField.email,
const Email.dirty(validEmailString),
),
FormInput(
AuthFormField.password,
const Password.pure(),
)
],
name: AuthFormName.signUpForm,
),
);
},
build: () => SignUpCubit( build: () => SignUpCubit(
authenticationRepository: authenticationRepository, authenticationRepository: authenticationRepository,
formData: const FormData.empty(),
), ),
seed: () => const SignUpState( seed: () => SignUpState(
email: validEmail, form: WyattFormImpl(
data: FormData.empty(), [
FormInput(
AuthFormField.email,
const Email.dirty(validEmailString),
),
FormInput(
AuthFormField.password,
const Password.pure(),
)
],
name: AuthFormName.signUpForm,
),
status: FormStatus.invalid,
), ),
act: (cubit) => cubit.passwordChanged(validPasswordString), act: (cubit) => cubit.passwordChanged(validPasswordString),
expect: () => <SignUpState>[ expect: () => <SignUpState>[
const SignUpState( SignUpState(
email: validEmail, form: WyattFormImpl(
password: validPassword, [
FormInput(
AuthFormField.email,
const Email.dirty(validEmailString),
),
FormInput(
AuthFormField.password,
const Password.dirty(validPasswordString),
)
],
name: AuthFormName.signUpForm,
),
status: FormStatus.valid, status: FormStatus.valid,
data: FormData.empty(),
), ),
], ],
); );
}); });
group('signUpFormSubmitted', () { group('submit', () {
blocTest<SignUpCubit, SignUpState>( blocTest<SignUpCubit<int>, SignUpState>(
'does nothing when status is not validated', 'does nothing when status is not validated',
build: () => SignUpCubit( build: () => SignUpCubit(
authenticationRepository: authenticationRepository, authenticationRepository: authenticationRepository,
formData: const FormData.empty(),
), ),
act: (cubit) => cubit.signUpFormSubmitted(), act: (cubit) => cubit.submit(),
expect: () => const <SignUpState>[], expect: () => const <SignUpState>[],
); );
blocTest<SignUpCubit, SignUpState>( blocTest<SignUpCubit<int>, SignUpState>(
'calls signUp with correct email/password/confirmedPassword', 'calls signUp with correct email/password',
setUp: () {
when(
() => formRepository.accessForm(AuthFormName.signUpForm),
).thenAnswer(
(_) => WyattFormImpl(
[
FormInput(
AuthFormField.email,
const Email.dirty(validEmailString),
),
FormInput(
AuthFormField.password,
const Password.dirty(validPasswordString),
)
],
name: AuthFormName.signUpForm,
),
);
},
build: () => SignUpCubit( build: () => SignUpCubit(
authenticationRepository: authenticationRepository, authenticationRepository: authenticationRepository,
formData: const FormData.empty(),
), ),
seed: () => const SignUpState( seed: () => SignUpState(
form: WyattFormImpl(
[
FormInput(
AuthFormField.email,
const Email.dirty(validEmailString),
),
FormInput(
AuthFormField.password,
const Password.dirty(validPasswordString),
)
],
name: AuthFormName.signUpForm,
),
status: FormStatus.valid, status: FormStatus.valid,
email: validEmail,
password: validPassword,
data: FormData.empty(),
), ),
act: (cubit) => cubit.signUpFormSubmitted(), act: (cubit) => cubit.submit(),
verify: (_) { verify: (_) {
verify( verify(
() => authenticationRepository.signUp( () => authenticationRepository.signUp(
@ -184,37 +319,85 @@ void main() {
}, },
); );
blocTest<SignUpCubit, SignUpState>( blocTest<SignUpCubit<int>, SignUpState>(
'emits [submissionInProgress, submissionSuccess] ' 'emits [submissionInProgress, submissionSuccess] '
'when signUp succeeds', 'when signUp succeeds',
setUp: () {
when(
() => formRepository.accessForm(AuthFormName.signUpForm),
).thenAnswer(
(_) => WyattFormImpl(
[
FormInput(
AuthFormField.email,
const Email.dirty(validEmailString),
),
FormInput(
AuthFormField.password,
const Password.dirty(validPasswordString),
)
],
name: AuthFormName.signUpForm,
),
);
},
build: () => SignUpCubit( build: () => SignUpCubit(
authenticationRepository: authenticationRepository, authenticationRepository: authenticationRepository,
formData: const FormData.empty(),
), ),
seed: () => const SignUpState( seed: () => SignUpState(
form: WyattFormImpl(
[
FormInput(
AuthFormField.email,
const Email.dirty(validEmailString),
),
FormInput(
AuthFormField.password,
const Password.dirty(validPasswordString),
)
],
name: AuthFormName.signUpForm,
),
status: FormStatus.valid, status: FormStatus.valid,
email: validEmail,
password: validPassword,
data: FormData.empty(),
), ),
act: (cubit) => cubit.signUpFormSubmitted(), act: (cubit) => cubit.submit(),
expect: () => <SignUpState>[ expect: () => <SignUpState>[
const SignUpState( SignUpState(
status: FormStatus.submissionInProgress, form: WyattFormImpl(
email: validEmail, [
password: validPassword, FormInput(
data: FormData.empty(), AuthFormField.email,
const Email.dirty(validEmailString),
),
FormInput(
AuthFormField.password,
const Password.dirty(validPasswordString),
)
],
name: AuthFormName.signUpForm,
),
status: FormStatus.submissionInProgress,
),
SignUpState(
form: WyattFormImpl(
[
FormInput(
AuthFormField.email,
const Email.dirty(validEmailString),
),
FormInput(
AuthFormField.password,
const Password.dirty(validPasswordString),
)
],
name: AuthFormName.signUpForm,
), ),
const SignUpState(
status: FormStatus.submissionSuccess, status: FormStatus.submissionSuccess,
email: validEmail,
password: validPassword,
data: FormData.empty(),
) )
], ],
); );
blocTest<SignUpCubit, SignUpState>( blocTest<SignUpCubit<int>, SignUpState>(
'emits [submissionInProgress, submissionFailure] ' 'emits [submissionInProgress, submissionFailure] '
'when signUp fails', 'when signUp fails',
setUp: () { setUp: () {
@ -223,31 +406,77 @@ void main() {
email: any(named: 'email'), email: any(named: 'email'),
password: any(named: 'password'), password: any(named: 'password'),
), ),
).thenThrow(Exception('oops')); ).thenAnswer((_) async => Err(ServerException()));
when(
() => formRepository.accessForm(AuthFormName.signUpForm),
).thenAnswer(
(_) => WyattFormImpl(
[
FormInput(
AuthFormField.email,
const Email.dirty(validEmailString),
),
FormInput(
AuthFormField.password,
const Password.dirty(validPasswordString),
)
],
name: AuthFormName.signUpForm,
),
);
}, },
build: () => SignUpCubit( build: () => SignUpCubit(
authenticationRepository: authenticationRepository, authenticationRepository: authenticationRepository,
formData: const FormData.empty(),
), ),
seed: () => const SignUpState( seed: () => SignUpState(
form: WyattFormImpl(
[
FormInput(
AuthFormField.email,
const Email.dirty(validEmailString),
),
FormInput(
AuthFormField.password,
const Password.dirty(validPasswordString),
)
],
name: AuthFormName.signUpForm,
),
status: FormStatus.valid, status: FormStatus.valid,
email: validEmail,
password: validPassword,
data: FormData.empty(),
), ),
act: (cubit) => cubit.signUpFormSubmitted(), act: (cubit) => cubit.submit(),
expect: () => <SignUpState>[ expect: () => <SignUpState>[
const SignUpState( SignUpState(
status: FormStatus.submissionInProgress, form: WyattFormImpl(
email: validEmail, [
password: validPassword, FormInput(
data: FormData.empty(), AuthFormField.email,
const Email.dirty(validEmailString),
),
FormInput(
AuthFormField.password,
const Password.dirty(validPasswordString),
)
],
name: AuthFormName.signUpForm,
),
status: FormStatus.submissionInProgress,
),
SignUpState(
form: WyattFormImpl(
[
FormInput(
AuthFormField.email,
const Email.dirty(validEmailString),
),
FormInput(
AuthFormField.password,
const Password.dirty(validPasswordString),
)
],
name: AuthFormName.signUpForm,
), ),
const SignUpState(
status: FormStatus.submissionFailure, status: FormStatus.submissionFailure,
email: validEmail,
password: validPassword,
data: FormData.empty(),
) )
], ],
); );

View File

@ -19,93 +19,37 @@ import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart';
import 'package:wyatt_form_bloc/wyatt_form_bloc.dart'; import 'package:wyatt_form_bloc/wyatt_form_bloc.dart';
void main() { void main() {
const Email email = Email.dirty('email'); final WyattForm form = WyattFormImpl(
const String passwordString = 'password'; [
const Password password = Password.dirty(passwordString); FormInput(AuthFormField.email, const Email.pure()),
FormInput(AuthFormField.password, const Password.pure())
],
name: AuthFormName.signInForm,
);
group('SignUpState', () { group('SignUpState', () {
test('supports value comparisons', () { test('supports value comparisons', () {
expect( expect(
const SignUpState( SignUpState(
data: FormData.empty(), form: form,
),
const SignUpState(
data: FormData.empty(),
), ),
SignUpState(form: form),
); );
}); });
test('returns same object when no properties are passed', () { test('returns same object when no properties are passed', () {
expect( expect(
const SignUpState( SignUpState(form: form).copyWith(),
data: FormData.empty(), SignUpState(form: form),
).copyWith(),
const SignUpState(
data: FormData.empty(),
),
); );
}); });
test('returns object with updated status when status is passed', () { test('returns object with updated status when status is passed', () {
expect( expect(
const SignUpState( SignUpState(form: form).copyWith(status: FormStatus.invalid),
data: FormData.empty(), SignUpState(
).copyWith(status: FormStatus.pure), form: form,
const SignUpState( status: FormStatus.invalid,
data: FormData.empty(),
),
);
});
test('returns object with updated email when email is passed', () {
expect(
const SignUpState(
data: FormData.empty(),
).copyWith(email: email),
const SignUpState(
email: email,
data: FormData.empty(),
),
);
});
test('returns object with updated password when password is passed', () {
expect(
const SignUpState(
data: FormData.empty(),
).copyWith(password: password),
const SignUpState(
password: password,
data: FormData.empty(),
),
);
});
test(
'returns object with updated data'
' when data is passed', () {
expect(
const SignUpState(
data: FormData.empty(),
).copyWith(
data: const FormData(
[
FormInput(
'field',
Name.pure(),
),
],
),
),
const SignUpState(
data: FormData(
[
FormInput(
'field',
Name.pure(),
),
],
),
), ),
); );
}); });