milestone/stable-1-reconcile-auth-3 #176
@ -1,5 +1,5 @@
 | 
			
		||||
<!--
 | 
			
		||||
 * Copyright (C) 2022 WYATT GROUP
 | 
			
		||||
 * Copyright (C) 2023 WYATT GROUP
 | 
			
		||||
 * Please see the AUTHORS file for details.
 | 
			
		||||
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify
 | 
			
		||||
@ -7,7 +7,7 @@
 | 
			
		||||
 * 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,
 | 
			
		||||
 * 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.
 | 
			
		||||
@ -19,9 +19,7 @@
 | 
			
		||||
# Flutter - Authentication BLoC
 | 
			
		||||
 | 
			
		||||
<p align="left">
 | 
			
		||||
  <a href="https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages/src/branch/master/packages/wyatt_analysis">
 | 
			
		||||
    <img src="https://img.shields.io/badge/Style-Wyatt%20Analysis-blue.svg?style=flat-square" alt="Style: Wyatt Analysis" />
 | 
			
		||||
  </a>
 | 
			
		||||
  <a href="https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages/src/branch/master/packages/wyatt_analysis"><img src="https://img.shields.io/badge/Style-Wyatt%20Analysis-blue.svg?style=flat-square" alt="Style: Wyatt Analysis" /></a>
 | 
			
		||||
  <img src="https://img.shields.io/badge/SDK-Flutter-blue?style=flat-square" alt="SDK: Flutter" />
 | 
			
		||||
</p>
 | 
			
		||||
 | 
			
		||||
@ -29,37 +27,195 @@ Authentication Bloc for Flutter.
 | 
			
		||||
 | 
			
		||||
## Features
 | 
			
		||||
 | 
			
		||||
- Wyatt Architecture
 | 
			
		||||
- Entities:
 | 
			
		||||
    - Account : AccountModel -> Contains account information from provider
 | 
			
		||||
    - AccountWrapper : AccountWrapperModel -> Contains account and extra data.
 | 
			
		||||
- Data Sources:
 | 
			
		||||
    - Local:
 | 
			
		||||
        - Cached Authentication Data : AuthenticationCacheDataSourceImpl -> Provides a cache implementation
 | 
			
		||||
    - Remote:
 | 
			
		||||
        - Remote Authentication Data : AuthenticationFirebaseDataSourceImpl -> Provides a proxy to FirebaseAuth
 | 
			
		||||
- Repositories:
 | 
			
		||||
    - AuthenticationRepository : AuthenticationRepositoryImpl -> Provides all authentication methods
 | 
			
		||||
- Features:
 | 
			
		||||
    - Authentication:
 | 
			
		||||
        - AuthenticationBuilder : widget to build reactive view from authentication state
 | 
			
		||||
        - AuthenticationCubit : tracks every auth changes, have sign out capability.
 | 
			
		||||
    - SignUp:
 | 
			
		||||
        - SignUpCubit: implementation of a FormDataCubit from `wyatt_form_bloc` for the sign up
 | 
			
		||||
    - SignIn:
 | 
			
		||||
        - SignUpCubit: implementation of a FormDataCubit from `wyatt_form_bloc` for the sign in
 | 
			
		||||
- Consistent
 | 
			
		||||
    * Every class have same naming convention
 | 
			
		||||
- Tested
 | 
			
		||||
* 🧐 Wyatt Architecture
 | 
			
		||||
* 🧱 Entities
 | 
			
		||||
    - Account -> Contains account information from provider.
 | 
			
		||||
    - Session -> Contains account and associated data retrieved from an external source.
 | 
			
		||||
    - AuthenticationChangeEvent -> Describes an event in authentication change (sign in, sign up, sign out, etc...)
 | 
			
		||||
    - SessionWrapper -> Contains latest authentication change event and session.
 | 
			
		||||
* 🔑 Powerful and secured authentication repository
 | 
			
		||||
* 🔥 Multiple data sources
 | 
			
		||||
    - Mock
 | 
			
		||||
    - Firebase
 | 
			
		||||
* 🧊 Cubits, why make it complicated when you can make it simple?
 | 
			
		||||
    - Goes to the essential.
 | 
			
		||||
* 📐 Consistent
 | 
			
		||||
    - Every class have same naming convention
 | 
			
		||||
* 🧪 Tested
 | 
			
		||||
* 📚 Documented: [available here](./doc/api/index.md)
 | 
			
		||||
 | 
			
		||||
## Getting started
 | 
			
		||||
 | 
			
		||||
Simply add `wyatt_authentication_bloc` in `pubspec.yaml`, then
 | 
			
		||||
Simply add `wyatt_authentication_bloc` in `pubspec.yaml` , then
 | 
			
		||||
 | 
			
		||||
```dart
 | 
			
		||||
import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart';
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Usage
 | 
			
		||||
### Data source
 | 
			
		||||
 | 
			
		||||
// TODO
 | 
			
		||||
The first step is to provide a data source.
 | 
			
		||||
 | 
			
		||||
```dart
 | 
			
		||||
getIt.registerLazySingleton<AuthenticationRemoteDataSource<int>>(
 | 
			
		||||
    () => AuthenticationFirebaseDataSourceImpl<int>(
 | 
			
		||||
        firebaseAuth: FirebaseAuth.instance,
 | 
			
		||||
        googleSignIn:
 | 
			
		||||
            GoogleSignIn(clientId: DefaultFirebaseOptions.ios.iosClientId)),
 | 
			
		||||
);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
> Here we use GetIt (see example project)
 | 
			
		||||
 | 
			
		||||
### Repository
 | 
			
		||||
 | 
			
		||||
Then you can configure your repository.
 | 
			
		||||
 | 
			
		||||
```dart
 | 
			
		||||
final AuthenticationRepository<int> authenticationRepository = AuthenticationRepositoryImpl(
 | 
			
		||||
    authenticationRemoteDataSource:
 | 
			
		||||
        getIt<AuthenticationRemoteDataSource<int>>(),
 | 
			
		||||
    customPasswordValidator: const CustomPassword.pure(),
 | 
			
		||||
    extraSignUpInputs: [
 | 
			
		||||
        FormInput(
 | 
			
		||||
        AuthFormField.confirmPassword,
 | 
			
		||||
        const ConfirmedPassword.pure(),
 | 
			
		||||
        metadata: const FormInputMetadata<void>(export: false),
 | 
			
		||||
        ),
 | 
			
		||||
    ],
 | 
			
		||||
);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
> Here we pass some extra inputs for the sign up, and a custom password validator.
 | 
			
		||||
 | 
			
		||||
### Cubits
 | 
			
		||||
 | 
			
		||||
It is necessary to implement each cubit. Don't panic, most of the work is already done 😊 you just have to customize the logic of these.
 | 
			
		||||
 | 
			
		||||
In each of these cubits it is necessary to overload the various callbacks.
 | 
			
		||||
 | 
			
		||||
> Here the associated data `Data` is a `int`
 | 
			
		||||
 | 
			
		||||
#### Authentication
 | 
			
		||||
 | 
			
		||||
In the authentication are managed, the refresh, the deletion of account or the disconnection.
 | 
			
		||||
 | 
			
		||||
```dart
 | 
			
		||||
class ExampleAuthenticationCubit extends AuthenticationCubit<int> {
 | 
			
		||||
  ExampleAuthenticationCubit({required super.authenticationRepository});
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  FutureOrResult<int?> onReauthenticate(Result<Account, AppException> result) async {
 | 
			
		||||
    // TODO
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  FutureOrResult<int?> onRefresh(Result<Account, AppException> result) {
 | 
			
		||||
    // TODO
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  FutureOrResult<int?> onSignInFromCache(SessionWrapper<int> wrapper) {
 | 
			
		||||
    // TODO
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  FutureOrResult<void> onSignOut() {
 | 
			
		||||
    // TODO
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  FutureOrResult<void> onDelete() {
 | 
			
		||||
    // TODO
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
#### Sign Up
 | 
			
		||||
 | 
			
		||||
```dart
 | 
			
		||||
class ExampleSignUpCubit extends SignUpCubit<int> {
 | 
			
		||||
  ExampleSignUpCubit({
 | 
			
		||||
    required super.authenticationRepository,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  FutureOrResult<int?> onSignUpWithEmailAndPassword(Result<Account, AppException> result, WyattForm form) async {
 | 
			
		||||
    // TODO
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
#### Sign In
 | 
			
		||||
 | 
			
		||||
```dart
 | 
			
		||||
class ExampleSignInCubit extends SignInCubit<int> {
 | 
			
		||||
  ExampleSignInCubit({
 | 
			
		||||
    required super.authenticationRepository,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  FutureOrResult<int?> onSignInWithEmailAndPassword(Result<Account, AppException> result, WyattForm form) {
 | 
			
		||||
    // TODO
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  FutureOrResult<int?> onSignInAnonymously(Result<Account, AppException> result, WyattForm form) {
 | 
			
		||||
    // TODO
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  FutureOrResult<int?> onSignInWithGoogle(Result<Account, AppException> result, WyattForm form) {
 | 
			
		||||
    // TODO
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
After setting up all these cubits you can provide them in the application. And that's it!
 | 
			
		||||
 | 
			
		||||
```dart
 | 
			
		||||
BlocProvider<SignUpCubit<int>>(
 | 
			
		||||
    create: (_) => ExampleSignUpCubit(
 | 
			
		||||
        authenticationRepository: authenticationRepository,
 | 
			
		||||
    ),
 | 
			
		||||
),
 | 
			
		||||
BlocProvider<SignInCubit<int>>(
 | 
			
		||||
    create: (_) => ExampleSignInCubit(
 | 
			
		||||
        authenticationRepository: authenticationRepository,
 | 
			
		||||
    ),
 | 
			
		||||
),
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Widgets
 | 
			
		||||
 | 
			
		||||
Widgets are provided to make your life easier. Starting with the `AuthenticationBuilder` which allows you to build according to the authentication state.
 | 
			
		||||
 | 
			
		||||
```dart
 | 
			
		||||
AuthenticationBuilder<int>(
 | 
			
		||||
    authenticated: (context, sessionWrapper) => Text(
 | 
			
		||||
        'Logged as ${sessionWrapper.session?.account.email} | GeneratedId is ${sessionWrapper.session?.data}'),
 | 
			
		||||
    unauthenticated: (context) =>
 | 
			
		||||
        const Text('Not logged (unauthenticated)'),
 | 
			
		||||
    unknown: (context) => const Text('Not logged (unknown)'),
 | 
			
		||||
),
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
A `BuildContext` extension is also available to access certain attributes more quickly.
 | 
			
		||||
 | 
			
		||||
```dart
 | 
			
		||||
Text('Home | ${context.account<AuthenticationCubit<int>, int>()?.email}'),
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Listeners are used to listen to the status of the sign in and sign up forms.
 | 
			
		||||
 | 
			
		||||
```dart
 | 
			
		||||
return SignInListener<int>(
 | 
			
		||||
    onError: (context, status, errorMessage) => ScaffoldMessenger.of(context)
 | 
			
		||||
    ..hideCurrentSnackBar()
 | 
			
		||||
    ..showSnackBar(
 | 
			
		||||
        SnackBar(content: Text(errorMessage ?? 'Sign In Failure')),
 | 
			
		||||
    ),
 | 
			
		||||
    child: ...
 | 
			
		||||
);
 | 
			
		||||
```
 | 
			
		||||
@ -10,7 +10,6 @@ dart pub global run dartdoc --format md \
 | 
			
		||||
	--no-auto-include-dependencies \
 | 
			
		||||
	--no-validate-links \
 | 
			
		||||
	--show-progress \
 | 
			
		||||
	--output "/Users/hpcl/Work/Wyatt/wyatt-packages/wiki/wyatt_authentication_bloc/"
 | 
			
		||||
 | 
			
		||||
sed -i -e "s/\/\/export 'package:firebase_auth\/firebase_auth.dart';/export 'package:firebase_auth\/firebase_auth.dart';/g" lib/wyatt_authentication_bloc.dart
 | 
			
		||||
sed -i -e "s/\/\/export 'package:google_sign_in\/google_sign_in.dart';/export 'package:google_sign_in\/google_sign_in.dart';/g" lib/wyatt_authentication_bloc.dart
 | 
			
		||||
 | 
			
		||||
@ -26,25 +26,49 @@ class MockAuthenticationRepository extends Mock
 | 
			
		||||
 | 
			
		||||
class MockAccount extends Mock implements Account {}
 | 
			
		||||
 | 
			
		||||
class TestAuthenticationCubit extends AuthenticationCubit<int> {
 | 
			
		||||
  TestAuthenticationCubit({required super.authenticationRepository});
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  FutureOrResult<void> onDelete() async => const Ok(null);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  FutureOrResult<int?> onReauthenticate(
 | 
			
		||||
    Result<Account, AppException> result,
 | 
			
		||||
  ) async =>
 | 
			
		||||
      const Ok(null);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  FutureOrResult<int?> onRefresh(Result<Account, AppException> result) async =>
 | 
			
		||||
      const Ok(null);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  FutureOrResult<int?> onSignInFromCache(SessionWrapper<int> wrapper) async =>
 | 
			
		||||
      const Ok(null);
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  FutureOrResult<void> onSignOut() async => const Ok(null);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void main() {
 | 
			
		||||
  group('AuthenticationCubit<T>', () {
 | 
			
		||||
    final MockAccount account = MockAccount();
 | 
			
		||||
    final AccountWrapper<int> wrapper = AccountWrapperModel(account, 10);
 | 
			
		||||
    final SessionWrapper<int> wrapper = SessionWrapper(
 | 
			
		||||
      event: const UnknownAuthenticationEvent(),
 | 
			
		||||
      session: Session(account: account, data: 10),
 | 
			
		||||
    );
 | 
			
		||||
    late AuthenticationRepository<int> authenticationRepository;
 | 
			
		||||
 | 
			
		||||
    setUp(() {
 | 
			
		||||
      authenticationRepository = MockAuthenticationRepository();
 | 
			
		||||
      when(() => authenticationRepository.streamAccount()).thenAnswer(
 | 
			
		||||
      when(() => authenticationRepository.sessionStream()).thenAnswer(
 | 
			
		||||
        (_) => const Stream.empty(),
 | 
			
		||||
      );
 | 
			
		||||
      when(
 | 
			
		||||
        () => authenticationRepository.getAccount(),
 | 
			
		||||
      ).thenAnswer((_) async => Ok(account));
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    test('initial auth state is `unknown`', () {
 | 
			
		||||
      expect(
 | 
			
		||||
        AuthenticationCubit<int>(
 | 
			
		||||
        TestAuthenticationCubit(
 | 
			
		||||
          authenticationRepository: authenticationRepository,
 | 
			
		||||
        ).state,
 | 
			
		||||
        const AuthenticationState<Never>.unknown(),
 | 
			
		||||
@ -53,17 +77,13 @@ void main() {
 | 
			
		||||
 | 
			
		||||
    group('ListenForAuthenticationChanges', () {
 | 
			
		||||
      blocTest<AuthenticationCubit<int>, AuthenticationState<int>>(
 | 
			
		||||
        'emits authenticated when stream contains account',
 | 
			
		||||
        'emits authenticated when stream contains session',
 | 
			
		||||
        setUp: () {
 | 
			
		||||
          when(() => authenticationRepository.streamAccount()).thenAnswer(
 | 
			
		||||
            (_) => Stream.fromIterable([
 | 
			
		||||
              Future.value(
 | 
			
		||||
                Ok(wrapper),
 | 
			
		||||
              )
 | 
			
		||||
            ]),
 | 
			
		||||
          when(() => authenticationRepository.sessionStream()).thenAnswer(
 | 
			
		||||
            (_) => Stream.fromIterable([wrapper]),
 | 
			
		||||
          );
 | 
			
		||||
        },
 | 
			
		||||
        build: () => AuthenticationCubit(
 | 
			
		||||
        build: () => TestAuthenticationCubit(
 | 
			
		||||
          authenticationRepository: authenticationRepository,
 | 
			
		||||
        ),
 | 
			
		||||
        seed: () => const AuthenticationState.unknown(),
 | 
			
		||||
@ -73,39 +93,12 @@ void main() {
 | 
			
		||||
      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)),
 | 
			
		||||
              )
 | 
			
		||||
            ]),
 | 
			
		||||
          when(() => authenticationRepository.sessionStream()).thenAnswer(
 | 
			
		||||
            (_) => Stream.fromIterable(
 | 
			
		||||
                [const SessionWrapper(event: SignedOutEvent())],),
 | 
			
		||||
          );
 | 
			
		||||
        },
 | 
			
		||||
        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(
 | 
			
		||||
        build: () => TestAuthenticationCubit(
 | 
			
		||||
          authenticationRepository: authenticationRepository,
 | 
			
		||||
        ),
 | 
			
		||||
        seed: () => const AuthenticationState.unknown(),
 | 
			
		||||
@ -121,7 +114,7 @@ void main() {
 | 
			
		||||
            () => authenticationRepository.signOut(),
 | 
			
		||||
          ).thenAnswer((_) async => const Ok(null));
 | 
			
		||||
        },
 | 
			
		||||
        build: () => AuthenticationCubit(
 | 
			
		||||
        build: () => TestAuthenticationCubit(
 | 
			
		||||
          authenticationRepository: authenticationRepository,
 | 
			
		||||
        ),
 | 
			
		||||
        act: (cubit) => cubit.signOut(),
 | 
			
		||||
 | 
			
		||||
@ -27,7 +27,7 @@ void main() {
 | 
			
		||||
        const AuthenticationState<void> state =
 | 
			
		||||
            AuthenticationState.unauthenticated();
 | 
			
		||||
        expect(state.status, AuthenticationStatus.unauthenticated);
 | 
			
		||||
        expect(state.accountWrapper, null);
 | 
			
		||||
        expect(state.wrapper, null);
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@ -36,10 +36,13 @@ void main() {
 | 
			
		||||
        final MockAccount account = MockAccount();
 | 
			
		||||
        final AuthenticationState<void> state =
 | 
			
		||||
            AuthenticationState.authenticated(
 | 
			
		||||
          AccountWrapperModel<void>(account, null),
 | 
			
		||||
          SessionWrapper<void>(
 | 
			
		||||
            event: SignedInEvent(account: account),
 | 
			
		||||
            session: Session(account: account),
 | 
			
		||||
          ),
 | 
			
		||||
        );
 | 
			
		||||
        expect(state.status, AuthenticationStatus.authenticated);
 | 
			
		||||
        expect(state.accountWrapper?.account, account);
 | 
			
		||||
        expect(state.wrapper?.session?.account, account);
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@ -49,11 +52,14 @@ void main() {
 | 
			
		||||
        const String extra = 'AwesomeExtraData';
 | 
			
		||||
        final AuthenticationState<String> state =
 | 
			
		||||
            AuthenticationState.authenticated(
 | 
			
		||||
          AccountWrapperModel(account, extra),
 | 
			
		||||
          SessionWrapper<String>(
 | 
			
		||||
            event: SignedInEvent(account: account),
 | 
			
		||||
            session: Session(account: account, data: extra),
 | 
			
		||||
          ),
 | 
			
		||||
        );
 | 
			
		||||
        expect(state.status, AuthenticationStatus.authenticated);
 | 
			
		||||
        expect(state.accountWrapper?.account, account);
 | 
			
		||||
        expect(state.accountWrapper?.data, extra);
 | 
			
		||||
        expect(state.wrapper?.session?.account, account);
 | 
			
		||||
        expect(state.wrapper?.session?.data, extra);
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
@ -39,15 +39,21 @@ void main() {
 | 
			
		||||
 | 
			
		||||
    setUp(() {
 | 
			
		||||
      authenticationRepository = MockAuthenticationRepository();
 | 
			
		||||
      when(
 | 
			
		||||
        () => authenticationRepository.getAccount(),
 | 
			
		||||
      ).thenAnswer((_) async => Ok(account));
 | 
			
		||||
      account = MockAccount();
 | 
			
		||||
 | 
			
		||||
      when(() => authenticationRepository.sessionStream()).thenAnswer(
 | 
			
		||||
        (_) => Stream.fromIterable([
 | 
			
		||||
          SessionWrapper<int>(
 | 
			
		||||
            event: SignedInFromCacheEvent(account: account),
 | 
			
		||||
            session: Session<int>(account: account, data: 10),
 | 
			
		||||
          )
 | 
			
		||||
        ]),
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      when(
 | 
			
		||||
        () => authenticationRepository.refresh(),
 | 
			
		||||
      ).thenAnswer((_) async => const Ok(null));
 | 
			
		||||
      ).thenAnswer((_) async => Ok(account));
 | 
			
		||||
 | 
			
		||||
      account = MockAccount();
 | 
			
		||||
      when(
 | 
			
		||||
        () => account.emailVerified,
 | 
			
		||||
      ).thenAnswer((_) => true);
 | 
			
		||||
@ -129,7 +135,7 @@ void main() {
 | 
			
		||||
        setUp: () {
 | 
			
		||||
          when(
 | 
			
		||||
            () => authenticationRepository.refresh(),
 | 
			
		||||
          ).thenAnswer((_) async => const Ok(null));
 | 
			
		||||
          ).thenAnswer((_) async => Ok(account));
 | 
			
		||||
        },
 | 
			
		||||
        build: () => EmailVerificationCubit(
 | 
			
		||||
          authenticationRepository: authenticationRepository,
 | 
			
		||||
@ -145,7 +151,7 @@ void main() {
 | 
			
		||||
        setUp: () {
 | 
			
		||||
          when(
 | 
			
		||||
            () => authenticationRepository.refresh(),
 | 
			
		||||
          ).thenAnswer((_) async => const Ok(null));
 | 
			
		||||
          ).thenAnswer((_) async => Ok(account));
 | 
			
		||||
          when(() => account.emailVerified).thenAnswer((_) => false);
 | 
			
		||||
        },
 | 
			
		||||
        build: () => EmailVerificationCubit(
 | 
			
		||||
@ -161,7 +167,7 @@ void main() {
 | 
			
		||||
        'emits success with true if verified',
 | 
			
		||||
        setUp: () {
 | 
			
		||||
          when(() => authenticationRepository.refresh())
 | 
			
		||||
              .thenAnswer((_) async => const Ok(null));
 | 
			
		||||
              .thenAnswer((_) async => Ok(account));
 | 
			
		||||
        },
 | 
			
		||||
        build: () => EmailVerificationCubit(
 | 
			
		||||
          authenticationRepository: authenticationRepository,
 | 
			
		||||
@ -183,7 +189,7 @@ void main() {
 | 
			
		||||
        'emits success with false if not verified',
 | 
			
		||||
        setUp: () {
 | 
			
		||||
          when(() => authenticationRepository.refresh())
 | 
			
		||||
              .thenAnswer((_) async => const Ok(null));
 | 
			
		||||
              .thenAnswer((_) async => Ok(account));
 | 
			
		||||
          when(() => account.emailVerified).thenAnswer((_) => false);
 | 
			
		||||
        },
 | 
			
		||||
        build: () => EmailVerificationCubit(
 | 
			
		||||
@ -222,28 +228,6 @@ void main() {
 | 
			
		||||
          )
 | 
			
		||||
        ],
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      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,
 | 
			
		||||
          )
 | 
			
		||||
        ],
 | 
			
		||||
      );
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -59,14 +59,7 @@ void main() {
 | 
			
		||||
      formRepository = MockFormRepository();
 | 
			
		||||
 | 
			
		||||
      when(
 | 
			
		||||
        () => authenticationRepository.signUp(
 | 
			
		||||
          email: any(named: 'email'),
 | 
			
		||||
          password: any(named: 'password'),
 | 
			
		||||
        ),
 | 
			
		||||
      ).thenAnswer((_) async => Ok(account));
 | 
			
		||||
 | 
			
		||||
      when(
 | 
			
		||||
        () => authenticationRepository.signInWithEmailAndPassword(
 | 
			
		||||
        () => authenticationRepository.signUpWithEmailAndPassword(
 | 
			
		||||
          email: any(named: 'email'),
 | 
			
		||||
          password: any(named: 'password'),
 | 
			
		||||
        ),
 | 
			
		||||
@ -318,7 +311,7 @@ void main() {
 | 
			
		||||
        act: (cubit) => cubit.signUpWithEmailPassword(),
 | 
			
		||||
        verify: (_) {
 | 
			
		||||
          verify(
 | 
			
		||||
            () => authenticationRepository.signUp(
 | 
			
		||||
            () => authenticationRepository.signUpWithEmailAndPassword(
 | 
			
		||||
              email: validEmailString,
 | 
			
		||||
              password: validPasswordString,
 | 
			
		||||
            ),
 | 
			
		||||
@ -409,7 +402,7 @@ void main() {
 | 
			
		||||
        'when signUp fails',
 | 
			
		||||
        setUp: () {
 | 
			
		||||
          when(
 | 
			
		||||
            () => authenticationRepository.signUp(
 | 
			
		||||
            () => authenticationRepository.signUpWithEmailAndPassword(
 | 
			
		||||
              email: any(named: 'email'),
 | 
			
		||||
              password: any(named: 'password'),
 | 
			
		||||
            ),
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user