Skip to content

Commit 5fab0be

Browse files
Increasing Unit Test Coverage and adding Firebase Analytics and Crashlytics (#169)
* test: Added tests for api service * feat: Added Firebase analytics and crashlytics * feat: Added environment variables for sensitive info * Create .env
1 parent e5c3051 commit 5fab0be

File tree

9 files changed

+336
-33
lines changed

9 files changed

+336
-33
lines changed

.env

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
apiKey=
2+
appId=
3+
messagingSenderId=
4+
projectId=
5+
storageBucket=
6+
iosClientId=
7+
iosBundleId=

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
.buildlog/
1010
.history
1111
.svn/
12+
*.env
1213

1314
# IntelliJ related
1415
*.iml
@@ -42,3 +43,9 @@ app.*.map.json
4243

4344
# Exceptions to above rules.
4445
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
46+
47+
# Firebase
48+
android/app/google-services.json
49+
ios/firebase_app_id_file.json
50+
ios/Runner/GoogleService-Info.plist
51+

lib/firebase_options.dart

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// File generated by FlutterFire CLI.
2+
// ignore_for_file: lines_longer_than_80_chars, avoid_classes_with_only_static_members
3+
import 'package:firebase_core/firebase_core.dart' show FirebaseOptions;
4+
import 'package:flutter/foundation.dart'
5+
show defaultTargetPlatform, kIsWeb, TargetPlatform;
6+
import 'package:flutter_dotenv/flutter_dotenv.dart';
7+
8+
/// Default [FirebaseOptions] for use with your Firebase apps.
9+
///
10+
/// Example:
11+
/// ```dart
12+
/// import 'firebase_options.dart';
13+
/// // ...
14+
/// await Firebase.initializeApp(
15+
/// options: DefaultFirebaseOptions.currentPlatform,
16+
/// );
17+
/// ```
18+
class DefaultFirebaseOptions {
19+
static FirebaseOptions get currentPlatform {
20+
if (kIsWeb) {
21+
throw UnsupportedError(
22+
'DefaultFirebaseOptions have not been configured for web - '
23+
'you can reconfigure this by running the FlutterFire CLI again.',
24+
);
25+
}
26+
switch (defaultTargetPlatform) {
27+
case TargetPlatform.android:
28+
return android;
29+
case TargetPlatform.iOS:
30+
return ios;
31+
case TargetPlatform.macOS:
32+
throw UnsupportedError(
33+
'DefaultFirebaseOptions have not been configured for macos - '
34+
'you can reconfigure this by running the FlutterFire CLI again.',
35+
);
36+
case TargetPlatform.windows:
37+
throw UnsupportedError(
38+
'DefaultFirebaseOptions have not been configured for windows - '
39+
'you can reconfigure this by running the FlutterFire CLI again.',
40+
);
41+
case TargetPlatform.linux:
42+
throw UnsupportedError(
43+
'DefaultFirebaseOptions have not been configured for linux - '
44+
'you can reconfigure this by running the FlutterFire CLI again.',
45+
);
46+
default:
47+
throw UnsupportedError(
48+
'DefaultFirebaseOptions are not supported for this platform.',
49+
);
50+
}
51+
}
52+
53+
static FirebaseOptions android = FirebaseOptions(
54+
apiKey: dotenv.env['apiKey'] ?? "",
55+
appId: dotenv.env['appId'] ?? "",
56+
messagingSenderId: dotenv.env['messagingSenderId'] ?? "",
57+
projectId: dotenv.env['projectId'] ?? "",
58+
storageBucket: dotenv.env['storageBucket'] ?? "",
59+
);
60+
61+
static FirebaseOptions ios = FirebaseOptions(
62+
apiKey: dotenv.env['apiKey'] ?? "",
63+
appId: dotenv.env['appId'] ?? "",
64+
messagingSenderId: dotenv.env['messagingSenderId'] ?? "",
65+
projectId: dotenv.env['projectId'] ?? "",
66+
storageBucket: dotenv.env['storageBucket'] ?? "",
67+
iosClientId: dotenv.env['iosClientId'] ?? "",
68+
iosBundleId: dotenv.env['iosBundleId'] ?? "",
69+
);
70+
}

lib/main.dart

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
// ignore_for_file: import_of_legacy_library_into_null_safe
22

3+
import 'dart:async';
4+
5+
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
36
import 'package:flutter/material.dart';
7+
import 'package:flutter_dotenv/flutter_dotenv.dart';
48
import 'package:hive/hive.dart';
59
import 'package:injectable/injectable.dart';
610
import 'package:path_provider/path_provider.dart';
@@ -11,23 +15,39 @@ import 'package:rutorrentflutter/services/state_services/user_preferences_servic
1115
import 'package:rutorrentflutter/theme/app_state_notifier.dart';
1216
import 'package:rutorrentflutter/theme/app_theme.dart';
1317
import 'package:rutorrentflutter/ui/widgets/smart_widgets/bottom_sheets/bottom_sheet_setup.dart';
18+
import 'package:firebase_core/firebase_core.dart';
19+
import 'firebase_options.dart';
1420
import 'package:stacked/stacked.dart';
1521
import 'package:stacked_services/stacked_services.dart';
1622

1723
void main() async {
18-
WidgetsFlutterBinding.ensureInitialized();
19-
//Setting up Hive DB
20-
final appDir = await getApplicationDocumentsDirectory();
21-
Hive.init(appDir.path);
22-
await Hive.openBox('DB');
23-
//To work in development environment, simply change the environment to Environment.dev below
24-
setupLocator(environment: Environment.prod);
25-
//Setting custom Bottom Sheet
26-
setUpBottomSheetUi();
27-
//Setting up Services
28-
locator<NotificationService>().init();
29-
await locator<UserPreferencesService>().init();
30-
runApp(MyApp());
24+
//Zone guarded for Firebase Crashlytics to catch errors
25+
runZonedGuarded<Future<void>>(() async {
26+
WidgetsFlutterBinding.ensureInitialized();
27+
//Setting up Hive DB
28+
final appDir = await getApplicationDocumentsDirectory();
29+
Hive.init(appDir.path);
30+
await Hive.openBox('DB');
31+
//To work in development environment, simply change the environment to Environment.dev below
32+
setupLocator(environment: Environment.prod);
33+
//Setting custom Bottom Sheet
34+
setUpBottomSheetUi();
35+
//Setting up Services
36+
locator<NotificationService>().init();
37+
await locator<UserPreferencesService>().init();
38+
//Setting up Firebase
39+
await dotenv.load(fileName: ".env");
40+
await Firebase.initializeApp(
41+
options: DefaultFirebaseOptions.currentPlatform,
42+
);
43+
44+
// Make sure to comment out this line in development to see Errors
45+
// FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterFatalError;
46+
47+
runApp(MyApp());
48+
},
49+
(error, stack) =>
50+
FirebaseCrashlytics.instance.recordError(error, stack, fatal: true));
3151
}
3252

3353
class MyApp extends StatelessWidget {

pubspec.lock

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,62 @@ packages:
239239
url: "https://pub.dartlang.org"
240240
source: hosted
241241
version: "2.0.1"
242+
firebase_analytics:
243+
dependency: "direct main"
244+
description:
245+
name: firebase_analytics
246+
url: "https://pub.dartlang.org"
247+
source: hosted
248+
version: "9.3.3"
249+
firebase_analytics_platform_interface:
250+
dependency: transitive
251+
description:
252+
name: firebase_analytics_platform_interface
253+
url: "https://pub.dartlang.org"
254+
source: hosted
255+
version: "3.3.3"
256+
firebase_analytics_web:
257+
dependency: transitive
258+
description:
259+
name: firebase_analytics_web
260+
url: "https://pub.dartlang.org"
261+
source: hosted
262+
version: "0.4.2+3"
263+
firebase_core:
264+
dependency: "direct main"
265+
description:
266+
name: firebase_core
267+
url: "https://pub.dartlang.org"
268+
source: hosted
269+
version: "1.21.1"
270+
firebase_core_platform_interface:
271+
dependency: transitive
272+
description:
273+
name: firebase_core_platform_interface
274+
url: "https://pub.dartlang.org"
275+
source: hosted
276+
version: "4.5.1"
277+
firebase_core_web:
278+
dependency: transitive
279+
description:
280+
name: firebase_core_web
281+
url: "https://pub.dartlang.org"
282+
source: hosted
283+
version: "1.7.2"
284+
firebase_crashlytics:
285+
dependency: "direct main"
286+
description:
287+
name: firebase_crashlytics
288+
url: "https://pub.dartlang.org"
289+
source: hosted
290+
version: "2.8.9"
291+
firebase_crashlytics_platform_interface:
292+
dependency: transitive
293+
description:
294+
name: firebase_crashlytics_platform_interface
295+
url: "https://pub.dartlang.org"
296+
source: hosted
297+
version: "3.2.15"
242298
fixnum:
243299
dependency: transitive
244300
description:
@@ -272,6 +328,13 @@ packages:
272328
description: flutter
273329
source: sdk
274330
version: "0.0.0"
331+
flutter_dotenv:
332+
dependency: "direct main"
333+
description:
334+
name: flutter_dotenv
335+
url: "https://pub.dartlang.org"
336+
source: hosted
337+
version: "5.0.2"
275338
flutter_launcher_icons:
276339
dependency: "direct dev"
277340
description:

pubspec.yaml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: rutorrentflutter
22
description: A rutorrent-based client in flutter
33

44
publish_to: "none"
5-
version: 1.0.0-alpha+4
5+
version: 1.0.0-alpha+6
66

77
environment:
88
sdk: ">=2.12.0-0 <3.0.0"
@@ -45,6 +45,10 @@ dependencies:
4545
webview_flutter_plus: ^0.3.0+2
4646
percent_indicator: ^4.2.2
4747
logger:
48+
firebase_core: ^1.21.1
49+
firebase_analytics: ^9.3.3
50+
firebase_crashlytics: ^2.8.9
51+
flutter_dotenv: ^5.0.2
4852
# webviewx: ^0.2.1
4953

5054
dev_dependencies:
@@ -66,6 +70,7 @@ flutter:
6670
assets:
6771
- assets/logo/
6872
- assets/animation/
73+
- .env
6974

7075
fonts:
7176
- family: SFUIDisplay

test/helpers/mock_ioclient.dart

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import 'dart:convert';
2-
32
import 'package:http/http.dart';
43
import 'package:logger/logger.dart';
54
import 'package:rutorrentflutter/app/app.logger.dart';
@@ -11,26 +10,37 @@ Logger log = getLogger("MockIOClientExtention");
1110
/// The [MockIOClientExtention] class helps in mocking API calls
1211
/// and returning the proper response.
1312
class MockIOClientExtention extends MockIOClient {
13+
@override
1414
post(Uri? url,
1515
{Map<String, String>? headers, Object? body, Encoding? encoding}) async {
16-
log.e(url.toString());
17-
log.e(TestData.historyPluginUrl);
18-
log.e(TestData.diskSpacePluginUrl);
19-
log.e(headers);
20-
log.e(body);
21-
2216
switch (url.toString()) {
2317
case (TestData.historyPluginUrl):
2418
return Response(TestData.updateHistoryJSONReponse, 200);
2519

2620
case (TestData.httpRpcPluginUrl):
27-
log.e(body, TestData.getAllAccountsTorrentListBody);
28-
log.e(body.toString() ==
29-
TestData.getAllAccountsTorrentListBody.toString());
30-
return body.toString() ==
31-
TestData.getAllAccountsTorrentListBody.toString()
32-
? Response(TestData.getAllAccountsTorrentListResponse, 200)
33-
: Response("", 404);
21+
if (body.toString() ==
22+
TestData.getAllAccountsTorrentListBody.toString())
23+
return Response(TestData.getAllAccountsTorrentListResponse, 200);
24+
else if (body.toString() == TestData.getFilesBody.toString())
25+
return Response(jsonEncode(TestData.getFilesResponse), 200);
26+
else if (body.toString() == TestData.getTrackersBody.toString())
27+
return Response(jsonEncode(TestData.getTrackersResponse), 200);
28+
29+
return Response("", 404);
30+
31+
case (TestData.explorerPluginUrl):
32+
if (body.toString() == TestData.getDiskFilesBody.toString())
33+
return Response(TestData.getDiskFilesResponse.toString(), 200);
34+
35+
return Response("", 404);
36+
37+
case (TestData.rssPluginUrl):
38+
if (body == null)
39+
return Response(jsonEncode(TestData.loadRSSResponse), 200);
40+
else if (body.toString() == TestData.getRSSFiltersBody.toString())
41+
return Response(jsonEncode(TestData.getRSSFiltersResponse), 200);
42+
43+
return Response("", 404);
3444

3545
case "":
3646
return Response("", 200);
@@ -42,11 +52,6 @@ class MockIOClientExtention extends MockIOClient {
4252

4353
@override
4454
Future<Response> get(Uri? url, {Map<String, String>? headers}) async {
45-
log.e(url.toString());
46-
log.e(TestData.historyPluginUrl);
47-
log.e(TestData.diskSpacePluginUrl);
48-
log.e(headers);
49-
5055
switch (url.toString()) {
5156
case (TestData.diskSpacePluginUrl):
5257
return Response(TestData.updateDiskSpaceResponse, 200);

test/helpers/test_data.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22
import 'package:flutter/foundation.dart';
33
import 'package:rutorrentflutter/models/account.dart';
44
import 'package:rutorrentflutter/models/history_item.dart';
5+
import 'package:rutorrentflutter/models/rss_filter.dart';
56
import 'package:rutorrentflutter/models/torrent.dart';
67
import 'package:rutorrentflutter/services/services_info.dart';
78

89
/// This class contains the test data used in tests to remove non-deterministic behaviour
910
class TestData {
1011
static const url = "http://localhost:8080";
12+
static const String hash = "EB25F7EC8FDE4DA888C197DB0975FFF549C9D7FB";
1113

1214
static ValueNotifier<List<Account>> accounts = ValueNotifier([
1315
Account(username: "test", password: "test", url: "http://localhost:8080")
@@ -24,6 +26,7 @@ class TestData {
2426
];
2527

2628
static Torrent get torrent => Torrent("dddd");
29+
static RSSFilter get rssFilter => RSSFilter("", 0, "", "", "", "");
2730

2831
static const httpRpcPluginUrl = url + '/plugins/httprpc/action.php';
2932

0 commit comments

Comments
 (0)