Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

33 firebase error handler #35

Merged
merged 4 commits into from
Aug 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,22 @@ import 'package:niira/screens/lobby.dart';
import 'package:niira/screens/welcome.dart';
import 'package:niira/services/auth/auth_service.dart';
import 'package:niira/services/auth/firebase_auth_service.dart';
import 'package:niira/services/auth/navigation_service.dart';
import 'package:provider/provider.dart';

void main() {
final authService = FirebaseAuthService(FirebaseAuth.instance);
runApp(MyApp(authService));
final nav = NavigationService();

final authService = FirebaseAuthService(FirebaseAuth.instance, nav);
runApp(MyApp(authService, nav.navigatorKey));
}

class MyApp extends StatelessWidget {
final AuthService _authService;
final GlobalKey<NavigatorState> _navigatorKey;

MyApp(this._authService);
MyApp(this._authService, this._navigatorKey);

// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MultiProvider(
Expand All @@ -25,6 +28,7 @@ class MyApp extends StatelessWidget {
],
child: MaterialApp(
title: 'Flutter Demo',
navigatorKey: _navigatorKey,
theme: ThemeData(
brightness: Brightness.light,
primaryColor: Color.fromRGBO(247, 152, 0, 1),
Expand Down
15 changes: 14 additions & 1 deletion lib/screens/sign_in.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import 'package:flutter/material.dart';
import 'package:niira/loading.dart';
import 'package:niira/screens/create_account.dart';
import 'package:niira/services/auth/auth_service.dart';
import 'package:provider/provider.dart';
import 'package:niira/extensions/custom_colors_extension.dart';

class SignInScreen extends StatefulWidget {
Expand Down Expand Up @@ -105,14 +107,25 @@ class _SignInScreenState extends State<SignInScreen> {
RaisedButton(
key: Key('sign_in_submit_btn'),
color: Theme.of(context).primaryColor,
onPressed: () {
onPressed: () async {
if (_formKey.currentState.validate()) {
setState(() {
_waitingForAuthResult = true;
});
} else {
_autoValidateForm = true;
}

final authResult = await context
.read<AuthService>()
.signInWithEmail(_email, _password);

// stop loading animation
tamari-gray marked this conversation as resolved.
Show resolved Hide resolved
if (authResult == null) {
setState(() {
_waitingForAuthResult = false;
});
}
},
child: Text('Submit'),
),
Expand Down
46 changes: 42 additions & 4 deletions lib/services/auth/firebase_auth_service.dart
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/services.dart';
import 'package:niira/models/user_data.dart';
import 'package:niira/services/auth/auth_service.dart';

import 'package:niira/extensions/firebase_user_extensions.dart';
import 'package:niira/services/auth/navigation_service.dart';

class FirebaseAuthService implements AuthService {
final FirebaseAuth _firebaseAuth;
final NavigationService _navService;

FirebaseAuthService(this._firebaseAuth);
FirebaseAuthService(this._firebaseAuth, this._navService);

@override
Future<String> getCurrentUserId() async {
Expand All @@ -21,9 +24,44 @@ class FirebaseAuthService implements AuthService {

@override
Future<UserData> signInWithEmail(String email, String password) async {
final authResult = await _firebaseAuth.signInWithEmailAndPassword(
email: email, password: password);
return authResult.user.toData();
try {
final result = await _firebaseAuth.signInWithEmailAndPassword(
email: email, password: password);
return result.user.toData();
} on PlatformException catch (error) {
String customErrorMessage;
switch (error.code) {
tamari-gray marked this conversation as resolved.
Show resolved Hide resolved
case 'ERROR_INVALID_EMAIL':
customErrorMessage = 'Invalid email address.';
break;
case 'ERROR_WRONG_PASSWORD':
customErrorMessage = 'Wrong password for this account.';
break;
case 'ERROR_USER_NOT_FOUND':
customErrorMessage = 'No user found with this email.';
break;
case 'ERROR_USER_DISABLED':
customErrorMessage = 'User with this email has been disabled.';
break;
case 'ERROR_TOO_MANY_REQUESTS':
customErrorMessage = 'Too many requests. Try again later.';
break;
case 'ERROR_OPERATION_NOT_ALLOWED':
customErrorMessage =
'Signing in with Email and Password is not enabled.';
break;
default:
customErrorMessage = 'An undefined Error happened.';
}
_navService.displayError(customErrorMessage);

return null;
} catch (e) {
// non platform specific errors
print('caught error: $e');
_navService.displayError(e);
return null;
}
}

@override
Expand Down
35 changes: 35 additions & 0 deletions lib/services/auth/navigation_service.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

class NavigationService {
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();

Future<dynamic> navigateTo(String routeName) {
return navigatorKey.currentState.pushNamed(routeName);
}

void displayError(dynamic error) {
final message = error.toString();

// filter out messages user doesnt want to see here

// show dialog
final newContext = navigatorKey.currentState.overlay.context;
showDialog<dynamic>(
tamari-gray marked this conversation as resolved.
Show resolved Hide resolved
context: newContext,
builder: (context) {
return AlertDialog(
key: Key('error_dialog'),
title: Text('$message'),
actions: [
RaisedButton(
child: Text('dismiss'),
onPressed: (() {
Navigator.pop(context);
}),
)
],
);
});
}
}
114 changes: 105 additions & 9 deletions test/auth_section/sign_in/widget_test.dart
Original file line number Diff line number Diff line change
@@ -1,17 +1,99 @@
import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
import 'package:niira/models/user_data.dart';
import 'package:niira/screens/sign_in.dart';
import 'package:niira/services/auth/auth_service.dart';
import 'package:niira/services/auth/navigation_service.dart';
import 'package:provider/provider.dart';

class MockNavigatorObserver extends Mock implements NavigatorObserver {}

class MockFirebaseAuthService extends AuthService {
final NavigationService _nav;
MockFirebaseAuthService(this._nav);
@override
Future<String> getCurrentUserId() {
// return 'yeet';
}

Stream<UserData> get streamOfAuthState {}

@override
Future<UserData> signInWithEmail(String email, String password) async {
final errors = ['user not found', 'wronf password'];
// Timer(Duration(seconds: 1), () {
_nav.displayError(errors[0]);
// });
return UserData(
uid: null,
providerId: null,
displayName: null,
photoUrl: null,
email: null,
phoneNumber: null,
createdOn: null,
lastSignedInOn: null,
isAnonymous: null,
isEmailVerified: null,
providers: null);
}

@override
Future<void> signOut() {
return null;
}
}

void main() {
group('auth tests', () {
testWidgets('auth error: shows dialog with error message',
(WidgetTester tester) async {
final navService = NavigationService();
await tester.pumpWidget(MultiProvider(
providers: [
Provider<AuthService>(
create: (_) => MockFirebaseAuthService(navService)),
],
child: MaterialApp(
navigatorKey: navService.navigatorKey,
home: SignInScreen(),
)));

final emailField = find.byKey(Key('email_field'));
expect(emailField, findsOneWidget);
await tester.enterText(emailField, '[email protected]');

final passwordFeild = find.byKey(Key('password_field'));
expect(passwordFeild, findsOneWidget);
await tester.enterText(passwordFeild, 'password-test');

await tester.tap(find.byKey(Key('sign_in_submit_btn')));
await tester.pump();
await tester.pump();

expect(find.byKey(Key('loading_indicator')), findsOneWidget);
await tester.pump();

expect(find.byKey(Key('error_dialog')), findsOneWidget);
expect(find.text('user not found'), findsOneWidget);
});
});
group('validation tests', () {
testWidgets('show error messages on empty text feilds',
(WidgetTester tester) async {
await tester.pumpWidget(MaterialApp(
home: SignInScreen(),
));
final navService = NavigationService();
await tester.pumpWidget(MultiProvider(
providers: [
Provider<AuthService>(
create: (_) => MockFirebaseAuthService(navService)),
],
child: MaterialApp(
navigatorKey: navService.navigatorKey,
home: SignInScreen(),
)));

// press submit with empty form
await tester.tap(find.byKey(Key('sign_in_submit_btn')));
Expand All @@ -23,9 +105,16 @@ void main() {
});
testWidgets('show error messages on invalid email ',
(WidgetTester tester) async {
await tester.pumpWidget(MaterialApp(
home: SignInScreen(),
));
final navService = NavigationService();
await tester.pumpWidget(MultiProvider(
providers: [
Provider<AuthService>(
create: (_) => MockFirebaseAuthService(navService)),
],
child: MaterialApp(
navigatorKey: navService.navigatorKey,
home: SignInScreen(),
)));

// input invalid text into fields
final emailField = find.byKey(Key('email_field'));
Expand All @@ -40,9 +129,16 @@ void main() {
});
testWidgets('show loading animation on successfull validation + submit',
(WidgetTester tester) async {
await tester.pumpWidget(MaterialApp(
home: SignInScreen(),
));
final navService = NavigationService();
await tester.pumpWidget(MultiProvider(
providers: [
Provider<AuthService>(
create: (_) => MockFirebaseAuthService(navService)),
],
child: MaterialApp(
navigatorKey: navService.navigatorKey,
home: SignInScreen(),
)));

// submit valid credentials
final emailField = find.byKey(Key('email_field'));
Expand Down
4 changes: 3 additions & 1 deletion test_driver/app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,17 @@ import 'package:flutter/material.dart';
import 'package:flutter_driver/driver_extension.dart';
import 'package:niira/main.dart';
import 'package:niira/models/user_data.dart';
import 'package:niira/services/auth/navigation_service.dart';

import 'mocks/services/mock_auth_service.dart';

void main() {
enableFlutterDriverExtension();

final navService = NavigationService();
final controller = StreamController<UserData>();
final mockAuthService = MockAuthService(controller);
mockAuthService.signInWithEmail('email', 'password');

runApp(MyApp(mockAuthService));
runApp(MyApp(mockAuthService, navService.navigatorKey));
}