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

feat: Overall code refactor and unit tests introduction #208

Merged
merged 25 commits into from
Jul 3, 2024
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
b62c6bb
feat: added unit tests, removed unused methods
jhomlala Jun 28, 2024
01ae230
feat: added unit tests, fixed invalid code
jhomlala Jun 29, 2024
3ee5802
feat: added melos test script, added CI test step
jhomlala Jun 29, 2024
7c2453a
feat: added tests
jhomlala Jun 29, 2024
eb06cfd
feat: added tests for memory storage
jhomlala Jun 30, 2024
dd591ce
feat: added tests for alice core
jhomlala Jul 1, 2024
c66ab44
refactor: general refactor of alice core
jhomlala Jul 2, 2024
cf3871c
refactor: general refactor
jhomlala Jul 2, 2024
42382ff
fix: fixed tests
jhomlala Jul 2, 2024
9c6af22
feat: added alice parser tests
jhomlala Jul 2, 2024
49f95e0
feat: added equatable
jhomlala Jul 2, 2024
9d74d6e
fix: fixed issues
jhomlala Jul 2, 2024
64d4de1
fix: fixed issues
jhomlala Jul 2, 2024
dcb467a
Merge branch 'master' of https://github.com/jhomlala/alice into feat/…
jhomlala Jul 2, 2024
25e13f3
fix: fixed issues
jhomlala Jul 2, 2024
0de490f
fix: changed headers definition
jhomlala Jul 2, 2024
c005278
feat: added tests
jhomlala Jul 2, 2024
a131b25
refactor: refactored export helper
jhomlala Jul 2, 2024
9c4f4ca
refactor: refactored export helper, added tests
jhomlala Jul 3, 2024
6c0731b
refactor: dart format & minor fixes
jhomlala Jul 3, 2024
5a7f507
refactor: refactor notifications
jhomlala Jul 3, 2024
1adc9dd
refactor: general refactor
jhomlala Jul 3, 2024
cf3a79d
Merge branch 'master' of https://github.com/jhomlala/alice into feat/…
jhomlala Jul 3, 2024
74675b3
refactor: fixed PR comments
jhomlala Jul 3, 2024
c872ad5
feat: updated changelog
jhomlala Jul 3, 2024
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
14 changes: 14 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,17 @@ jobs:
run: ./.github/workflows/scripts/install-tools.sh
- name: Check formatting
run: melos format --output none --set-exit-if-changed

test:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v3
- uses: subosito/flutter-action@v2
with:
channel: stable
cache: true
- name: Install Tools
run: ./.github/workflows/scripts/install-tools.sh
- name: Check tests
run: melos run test
14 changes: 13 additions & 1 deletion melos.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,16 @@ scripts:
fix:
exec: dart fix --apply
format:
exec: dart format .
exec: dart format .
test:
description: Run tests in a specific package.
run: flutter test
exec:
concurrency: 1
packageFilters:
dirExists:
- test
# This tells Melos tests to ignore env variables passed to tests from `melos run test`
# as they could change the behaviour of how tests filter packages.
env:
MELOS_TEST: true
4 changes: 4 additions & 0 deletions packages/alice/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# 1.0.0-dev.9
* Added tests.
* General code refactor.

# 1.0.0-dev.8
* Added storage abstractions (by Klemen Tusar https://github.com/techouse).
* Added in memory storage implementation (by Klemen Tusar https://github.com/techouse).
Expand Down
11 changes: 9 additions & 2 deletions packages/alice/lib/alice.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:alice/core/alice_adapter.dart';
import 'package:alice/core/alice_core.dart';
import 'package:alice/core/alice_logger.dart';
import 'package:alice/core/alice_memory_storage.dart';
import 'package:alice/core/alice_storage.dart';
import 'package:alice/model/alice_http_call.dart';
Expand All @@ -9,6 +10,7 @@ import 'package:flutter/widgets.dart';

export 'package:alice/core/alice_store.dart';
export 'package:alice/model/alice_log.dart';
export 'package:alice/core/alice_memory_storage.dart';

class Alice {
/// Should user be notified with notification when there's new request caught
Expand All @@ -33,11 +35,16 @@ class Alice {
/// Flag used to show/hide share button
final bool? showShareButton;

GlobalKey<NavigatorState>? _navigatorKey;
/// Alice core instance
late final AliceCore _aliceCore;

/// Alice storage instance
final AliceStorage? _aliceStorage;

/// Navigator key used for navigating to inspector
GlobalKey<NavigatorState>? _navigatorKey;


/// Creates alice instance.
Alice({
GlobalKey<NavigatorState>? navigatorKey,
Expand All @@ -55,13 +62,13 @@ class Alice {
showNotification: showNotification,
showInspectorOnShake: showInspectorOnShake,
notificationIcon: notificationIcon,
maxCallsCount: maxCallsCount,
directionality: directionality,
showShareButton: showShareButton,
aliceStorage: _aliceStorage ??
AliceMemoryStorage(
maxCallsCount: maxCallsCount,
),
aliceLogger: AliceLogger(),
);
}

Expand Down
106 changes: 65 additions & 41 deletions packages/alice/lib/core/alice_core.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import 'dart:async' show FutureOr, StreamSubscription;
import 'dart:io' show Platform;

import 'package:alice/core/alice_logger.dart';
import 'package:alice/core/alice_storage.dart';
import 'package:alice/core/alice_utils.dart';
import 'package:alice/helper/alice_save_helper.dart';
import 'package:alice/helper/alice_export_helper.dart';
import 'package:alice/helper/operating_system.dart';
import 'package:alice/model/alice_export_result.dart';
import 'package:alice/model/alice_http_call.dart';
import 'package:alice/model/alice_http_error.dart';
import 'package:alice/model/alice_http_response.dart';
Expand All @@ -30,8 +31,13 @@ class AliceCore {
/// Icon url for notification
final String notificationIcon;

/// Storage used for Alice to keep calls data.
final AliceStorage _aliceStorage;

/// Logger used for Alice to keep logs;
final AliceLogger _aliceLogger;

/// Notification configuration for Alice.
late final NotificationDetails _notificationDetails = NotificationDetails(
android: AndroidNotificationDetails(
'Alice',
Expand All @@ -44,52 +50,52 @@ class AliceCore {
iOS: const DarwinNotificationDetails(presentSound: false),
);

///Max number of calls that are stored in memory. When count is reached, FIFO
///method queue will be used to remove elements.
final int maxCallsCount;

///Directionality of app. If null then directionality of context will be used.
final TextDirection? directionality;

///Flag used to show/hide share button
final bool? showShareButton;

final AliceLogger _aliceLogger = AliceLogger();

FlutterLocalNotificationsPlugin? _flutterLocalNotificationsPlugin;

/// Navigator key used for inspector navigator.
GlobalKey<NavigatorState>? navigatorKey;

/// Flag used to determine whether is inspector opened
bool _isInspectorOpened = false;

/// Detector used to detect device shakes
ShakeDetector? _shakeDetector;

/// Subscription for call changes
StreamSubscription<List<AliceHttpCall>>? _callsSubscription;

String? _notificationMessage;
/// Currently displayed notification message
String? _notificationMessageDisplayed;

String? _notificationMessageShown;
/// Is current notification being processed
bool _isNotificationProcessing = false;

bool _notificationProcessing = false;
/// Notification plugin instance
FlutterLocalNotificationsPlugin? _flutterLocalNotificationsPlugin;

/// Creates alice core instance
AliceCore(
this.navigatorKey, {
required this.showNotification,
required this.showInspectorOnShake,
required this.notificationIcon,
required this.maxCallsCount,
required AliceStorage aliceStorage,
required AliceLogger aliceLogger,
this.directionality,
this.showShareButton,
}) : _aliceStorage = aliceStorage {
}) : _aliceStorage = aliceStorage,
_aliceLogger = aliceLogger {
_subscribeToCallChanges();
if (showNotification) {
_initializeNotificationsPlugin();
_requestNotificationPermissions();
_aliceStorage.subscribeToCallChanges(onCallsChanged);
}
if (showInspectorOnShake) {
if (Platform.isAndroid || Platform.isIOS) {
if (OperatingSystem.isAndroid() || OperatingSystem.isMacOS()) {
_shakeDetector = ShakeDetector.autoStart(
onPhoneShake: navigateToCallListScreen,
shakeThresholdGravity: 4,
Expand All @@ -101,10 +107,10 @@ class AliceCore {
/// Dispose subjects and subscriptions
void dispose() {
_shakeDetector?.stopListening();
_callsSubscription?.cancel();
_unsubscribeFromCallChanges();
}

@protected
/// Initialises notification settings.
void _initializeNotificationsPlugin() {
_flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
final AndroidInitializationSettings initializationSettingsAndroid =
Expand All @@ -129,15 +135,11 @@ class AliceCore {
Future<void> onCallsChanged(List<AliceHttpCall>? calls) async {
if (calls != null && calls.isNotEmpty) {
final AliceStats stats = _aliceStorage.getStats();

_notificationMessage = _getNotificationMessage(stats);
if (_notificationMessage != _notificationMessageShown &&
!_notificationProcessing) {
await _showLocalNotification(stats);
}
_showStatsNotification(stats: stats);
}
}

/// Called when notification has been clicked. It navigates to calls screen.
Future<void> _onDidReceiveNotificationResponse(
NotificationResponse response,
) async {
Expand Down Expand Up @@ -165,6 +167,7 @@ class AliceCore {
/// Get context from navigator key. Used to open inspector route.
BuildContext? getContext() => navigatorKey?.currentState?.overlay?.context;

/// Formats [stats] for notification message.
String _getNotificationMessage(AliceStats stats) => <String>[
if (stats.loading > 0)
'${getContext()?.i18n(AliceTranslationKey.notificationLoading)} ${stats.loading}',
Expand All @@ -176,8 +179,9 @@ class AliceCore {
'${getContext()?.i18n(AliceTranslationKey.notificationError)} ${stats.errors}',
].join(' | ');

/// Requests notification permissions to display stats notification.
Future<void> _requestNotificationPermissions() async {
if (Platform.isIOS || Platform.isMacOS) {
if (OperatingSystem.isIOS() || OperatingSystem.isMacOS()) {
await _flutterLocalNotificationsPlugin
?.resolvePlatformSpecificImplementation<
IOSFlutterLocalNotificationsPlugin>()
Expand All @@ -194,7 +198,7 @@ class AliceCore {
badge: true,
sound: true,
);
} else if (Platform.isAndroid) {
} else if (OperatingSystem.isAndroid()) {
final AndroidFlutterLocalNotificationsPlugin? androidImplementation =
_flutterLocalNotificationsPlugin
?.resolvePlatformSpecificImplementation<
Expand All @@ -204,11 +208,19 @@ class AliceCore {
}
}

Future<void> _showLocalNotification(AliceStats stats) async {
/// Shows current stats notification. It formats [stats] into simple
/// notification which is displayed when stats has changed.
Future<void> _showStatsNotification({required AliceStats stats}) async {
try {
_notificationProcessing = true;
if (_isNotificationProcessing) {
return;
}
final message = _getNotificationMessage(stats);
if (message == _notificationMessageDisplayed) {
return;
}

final String? message = _notificationMessage;
_isNotificationProcessing = true;

await _flutterLocalNotificationsPlugin?.show(
0,
Expand All @@ -220,9 +232,11 @@ class AliceCore {
payload: '',
);

_notificationMessageShown = message;
_notificationMessageDisplayed = message;
} catch (error) {
AliceUtils.log(error.toString());
} finally {
_notificationProcessing = false;
_isNotificationProcessing = false;
}
}

Expand All @@ -237,32 +251,42 @@ class AliceCore {
FutureOr<void> addResponse(AliceHttpResponse response, int requestId) =>
_aliceStorage.addResponse(response, requestId);

/// Add alice http call to calls subject
FutureOr<void> addHttpCall(AliceHttpCall aliceHttpCall) =>
_aliceStorage.addHttpCall(aliceHttpCall);

/// Remove all calls from calls subject
FutureOr<void> removeCalls() => _aliceStorage.removeCalls();

/// Selects call with given [requestId]. It may return null.
@protected
AliceHttpCall? selectCall(int requestId) =>
_aliceStorage.selectCall(requestId);

/// Returns stream which returns list of HTTP calls
Stream<List<AliceHttpCall>> get callsStream => _aliceStorage.callsStream;

/// Returns all stored HTTP calls.
List<AliceHttpCall> getCalls() => _aliceStorage.getCalls();

/// Save all calls to file
void saveHttpRequests(BuildContext context) {
AliceSaveHelper.saveCalls(context, _aliceStorage.getCalls());
/// Save all calls to file.
Future<AliceExportResult> saveCallsToFile(BuildContext context) {
return AliceExportHelper.saveCallsToFile(context, _aliceStorage.getCalls());
}

/// Adds new log to Alice logger.
void addLog(AliceLog log) => _aliceLogger.logs.add(log);
void addLog(AliceLog log) => _aliceLogger.add(log);

/// Adds list of logs to Alice logger
void addLogs(List<AliceLog> logs) => _aliceLogger.logs.addAll(logs);
void addLogs(List<AliceLog> logs) => _aliceLogger.addAll(logs);

/// Returns flag which determines whether inspector is opened
bool get isInspectorOpened => _isInspectorOpened;

/// Subscribes to storage for call changes.
void _subscribeToCallChanges() {
_callsSubscription = _aliceStorage.callsStream.listen(onCallsChanged);
}

/// Unsubscribes storage for call changes.
void _unsubscribeFromCallChanges() {
_callsSubscription?.cancel();
_callsSubscription = null;
}
}
13 changes: 10 additions & 3 deletions packages/alice/lib/core/alice_logger.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'dart:io' show Platform, Process, ProcessResult;
import 'dart:io' show Process, ProcessResult;

import 'package:alice/helper/operating_system.dart';
import 'package:alice/model/alice_log.dart';
import 'package:alice/utils/num_comparison.dart';
import 'package:flutter/foundation.dart';
Expand Down Expand Up @@ -30,6 +31,12 @@ class AliceLogger {
}
}

void addAll(List<AliceLog> logs) {
for (var log in logs) {
add(log);
}
}

void add(AliceLog log) {
late final int index;
if (logs.isEmpty || !log.timestamp.isBefore(logs.last.timestamp)) {
Expand Down Expand Up @@ -69,7 +76,7 @@ class AliceLogger {

/// Returns raw logs from Android via ADB.
Future<String> getAndroidRawLogs() async {
if (Platform.isAndroid) {
if (OperatingSystem.isAndroid()) {
final ProcessResult process =
await Process.run('logcat', ['-v', 'raw', '-d']);
return process.stdout as String;
Expand All @@ -79,7 +86,7 @@ class AliceLogger {

/// Clears all raw logs.
Future<void> clearAndroidRawLogs() async {
if (Platform.isAndroid) {
if (OperatingSystem.isAndroid()) {
await Process.run('logcat', ['-c']);
}
}
Expand Down
Loading
Loading