From 7dbb66465c453ab18789970dcb60647a3adeaa58 Mon Sep 17 00:00:00 2001 From: "mr.mojtaba" Date: Mon, 2 Jun 2025 09:38:02 +0330 Subject: [PATCH] feat : refresh login test: core --- lib/main.dart | 2 - .../data/services/token_storage_service.dart | 44 ++----- packages/auth/lib/data/utils/safe_call.dart | 61 ++++++++++ packages/auth/lib/hive_registrar.g.dart | 3 +- .../lib/presentation/pages/auth/logic.dart | 4 +- .../presentation/widget/captcha/logic.dart | 4 +- .../build/unit_test_assets/AssetManifest.json | 1 + .../lib/assets/flutter_map_logo.png | Bin 0 -> 2424 bytes .../lib/presentation/utils/color_utils.dart | 2 +- packages/core/lib/utils/safe_call_utils.dart | 59 ++++++--- .../local/hive_local_storage.dart | 112 ++++++++++++++++++ .../infrastructure/local/i_local_storage.dart | 44 +++++++ .../infrastructure/remote/dio_form_data.dart | 23 ++++ .../remote/dio_remote_test.dart | 0 .../infrastructure/remote/dio_response.dart | 20 ++++ .../remote/interfaces/i_form_data.dart | 6 + .../remote/interfaces/i_http_client.dart | 54 +++++++++ .../remote/interfaces/i_http_response.dart | 6 + .../remote/interfaces/i_remote.dart | 4 + packages/core/test/injection/di_test.dart | 18 +++ .../presentation/common/app_color_test.dart | 43 +++++++ .../presentation/utils/color_utils_test.dart | 35 ++++++ .../utils/list_extensions_test.dart | 53 +++++++++ pubspec.yaml | 2 +- 24 files changed, 538 insertions(+), 62 deletions(-) create mode 100644 packages/auth/lib/data/utils/safe_call.dart create mode 100644 packages/core/build/unit_test_assets/AssetManifest.json create mode 100644 packages/core/build/unit_test_assets/packages/flutter_map/lib/assets/flutter_map_logo.png create mode 100644 packages/core/test/infrastructure/local/hive_local_storage.dart create mode 100644 packages/core/test/infrastructure/local/i_local_storage.dart create mode 100644 packages/core/test/infrastructure/remote/dio_form_data.dart create mode 100644 packages/core/test/infrastructure/remote/dio_remote_test.dart create mode 100644 packages/core/test/infrastructure/remote/dio_response.dart create mode 100644 packages/core/test/infrastructure/remote/interfaces/i_form_data.dart create mode 100644 packages/core/test/infrastructure/remote/interfaces/i_http_client.dart create mode 100644 packages/core/test/infrastructure/remote/interfaces/i_http_response.dart create mode 100644 packages/core/test/infrastructure/remote/interfaces/i_remote.dart create mode 100644 packages/core/test/injection/di_test.dart create mode 100644 packages/core/test/presentation/common/app_color_test.dart create mode 100644 packages/core/test/presentation/utils/color_utils_test.dart create mode 100644 packages/core/test/presentation/utils/list_extensions_test.dart diff --git a/lib/main.dart b/lib/main.dart index 42195b2..395decd 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,9 +2,7 @@ import 'package:flutter/material.dart'; import 'package:rasadyar_app/presentation/routes/app_pages.dart'; import 'package:rasadyar_auth/auth.dart'; import 'package:rasadyar_auth/data/services/token_storage_service.dart'; - import 'package:rasadyar_core/core.dart'; - import 'infrastructure/di/di.dart'; import 'infrastructure/service/auth_service.dart'; diff --git a/packages/auth/lib/data/services/token_storage_service.dart b/packages/auth/lib/data/services/token_storage_service.dart index 39d33dd..0c56166 100644 --- a/packages/auth/lib/data/services/token_storage_service.dart +++ b/packages/auth/lib/data/services/token_storage_service.dart @@ -18,58 +18,32 @@ class TokenStorageService extends GetxService { Rxn appModule = Rxn(null); Future init() async { + await Hive.initFlutter(); Hive.registerAdapters(); - IsolatedHive.registerAdapters(); final String? encryptedKey = await _secureStorage.read(key: 'hive_enc_key'); - final encryptionKey = - encryptedKey != null - ? base64Url.decode(encryptedKey) - : Hive.generateSecureKey(); + final encryptionKey = encryptedKey != null ? base64Url.decode(encryptedKey) : Hive.generateSecureKey(); if (encryptedKey == null) { - await _secureStorage.write( - key: 'hive_enc_key', - value: base64UrlEncode(encryptionKey), - ); + await _secureStorage.write(key: 'hive_enc_key', value: base64UrlEncode(encryptionKey)); } await _localStorage.init(); - await _localStorage.openBox( - _boxName, - encryptionCipher: HiveAesCipher(encryptionKey), - ); + await _localStorage.openBox(_boxName, encryptionCipher: HiveAesCipher(encryptionKey)); - accessToken.value = _localStorage.read( - boxName: _boxName, - key: _accessTokenKey, - ); - refreshToken.value = _localStorage.read( - boxName: _boxName, - key: _refreshTokenKey, - ); - appModule.value = _localStorage.read( - boxName: _boxName, - key: _moduleKey, - ); + accessToken.value = _localStorage.read(boxName: _boxName, key: _accessTokenKey); + refreshToken.value = _localStorage.read(boxName: _boxName, key: _refreshTokenKey); + appModule.value = _localStorage.read(boxName: _boxName, key: _moduleKey); } Future saveAccessToken(String token) async { - await _localStorage.save( - boxName: _boxName, - key: _accessTokenKey, - value: token, - ); + await _localStorage.save(boxName: _boxName, key: _accessTokenKey, value: token); accessToken.value = token; accessToken.refresh(); } Future saveRefreshToken(String token) async { - await _localStorage.save( - boxName: _boxName, - key: _refreshTokenKey, - value: token, - ); + await _localStorage.save(boxName: _boxName, key: _refreshTokenKey, value: token); refreshToken.value = token; refreshToken.refresh(); } diff --git a/packages/auth/lib/data/utils/safe_call.dart b/packages/auth/lib/data/utils/safe_call.dart new file mode 100644 index 0000000..6d08637 --- /dev/null +++ b/packages/auth/lib/data/utils/safe_call.dart @@ -0,0 +1,61 @@ +import 'package:flutter/foundation.dart'; +import 'package:rasadyar_auth/auth.dart'; +import 'package:rasadyar_auth/data/repositories/auth_repository_imp.dart'; +import 'package:rasadyar_core/core.dart'; + +import '../models/response/auth/auth_response_model.dart'; +import '../services/token_storage_service.dart'; + +Future safeCall({ + required AppAsyncCallback call, + Function(T result)? onSuccess, + ErrorCallback? onError, + VoidCallback? onComplete, + bool showLoading = false, + bool showError = false, + bool showSuccess = false, + bool showToast = false, + bool showSnackBar = false, + Function()? onShowLoading, + Function()? onHideLoading, + Function()? onShowSuccessMessage, + Function()? onShowErrorMessage, +}) { + final authRepository = diAuth.get(); + TokenStorageService tokenStorageService = Get.find(); + + return gSafeCall( + call: call, + onSuccess: onSuccess, + onError: onError, + onComplete: onComplete, + showLoading: showLoading, + showError: showError, + showSuccess: showSuccess, + showToast: showToast, + showSnackBar: showSnackBar, + onShowLoading: onShowLoading, + onHideLoading: onHideLoading, + onShowSuccessMessage: onShowSuccessMessage, + onShowErrorMessage: onShowErrorMessage, + retryOnAuthError: true, + onTokenRefresh: () { + var token = tokenStorageService.refreshToken.value; + authRepository + .loginWithRefreshToken(authRequest: {"refresh_token": token}) + .then((value) async { + if (value is AuthResponseModel) { + await tokenStorageService.saveAccessToken(value.access!); + } else { + throw Exception("Failed to refresh token"); + } + }) + .catchError((error) { + if (kDebugMode) { + print('Error during token refresh: $error'); + } + throw error; + }); + }, + ); +} diff --git a/packages/auth/lib/hive_registrar.g.dart b/packages/auth/lib/hive_registrar.g.dart index 166b5cb..b46fdff 100644 --- a/packages/auth/lib/hive_registrar.g.dart +++ b/packages/auth/lib/hive_registrar.g.dart @@ -7,8 +7,9 @@ import 'package:rasadyar_auth/data/models/local/user_local/user_local_model.dart extension HiveRegistrar on HiveInterface { void registerAdapters() { - registerAdapter(ModuleAdapter()); registerAdapter(UserLocalModelAdapter()); + registerAdapter(ModuleAdapter()); + } } diff --git a/packages/auth/lib/presentation/pages/auth/logic.dart b/packages/auth/lib/presentation/pages/auth/logic.dart index 59b8167..624328a 100644 --- a/packages/auth/lib/presentation/pages/auth/logic.dart +++ b/packages/auth/lib/presentation/pages/auth/logic.dart @@ -8,6 +8,7 @@ import 'package:rasadyar_auth/data/models/request/login_request/login_request_mo import 'package:rasadyar_auth/data/models/response/auth/auth_response_model.dart'; import 'package:rasadyar_auth/data/repositories/auth_repository_imp.dart'; import 'package:rasadyar_auth/data/services/token_storage_service.dart'; +import 'package:rasadyar_auth/data/utils/safe_call.dart'; import 'package:rasadyar_auth/presentation/widget/captcha/logic.dart'; import 'package:rasadyar_core/core.dart'; @@ -73,7 +74,6 @@ class AuthLogic extends GetxController { @override void onInit() { super.onInit(); - tokenStorageService.init(); } @override @@ -120,8 +120,6 @@ class AuthLogic extends GetxController { await tokenStorageService.saveModule(_module); await tokenStorageService.saveRefreshToken(result?.refresh ?? ''); await tokenStorageService.saveAccessToken(result?.access ?? ''); - - //Get.offAndToNamed(Routes.home); }, onError: (error, stackTrace) { if (error is DioException) { diff --git a/packages/auth/lib/presentation/widget/captcha/logic.dart b/packages/auth/lib/presentation/widget/captcha/logic.dart index 4adb51c..6a89f38 100644 --- a/packages/auth/lib/presentation/widget/captcha/logic.dart +++ b/packages/auth/lib/presentation/widget/captcha/logic.dart @@ -2,10 +2,10 @@ import 'package:flutter/material.dart'; import 'package:rasadyar_auth/data/di/auth_di.dart'; import 'package:rasadyar_auth/data/models/response/captcha/captcha_response_model.dart'; import 'package:rasadyar_auth/data/repositories/auth_repository_imp.dart'; +import 'package:rasadyar_auth/data/utils/safe_call.dart'; import 'package:rasadyar_core/core.dart'; -class CaptchaWidgetLogic extends GetxController - with StateMixin { +class CaptchaWidgetLogic extends GetxController with StateMixin { Rx textController = TextEditingController().obs; RxnString captchaKey = RxnString(); GlobalKey formKey = GlobalKey(); diff --git a/packages/core/build/unit_test_assets/AssetManifest.json b/packages/core/build/unit_test_assets/AssetManifest.json new file mode 100644 index 0000000..52a2006 --- /dev/null +++ b/packages/core/build/unit_test_assets/AssetManifest.json @@ -0,0 +1 @@ +{"packages/cupertino_icons/assets/CupertinoIcons.ttf":["packages/cupertino_icons/assets/CupertinoIcons.ttf"],"packages/flutter_map/lib/assets/flutter_map_logo.png":["packages/flutter_map/lib/assets/flutter_map_logo.png"],"packages/font_awesome_flutter/lib/fonts/fa-brands-400.ttf":["packages/font_awesome_flutter/lib/fonts/fa-brands-400.ttf"],"packages/font_awesome_flutter/lib/fonts/fa-regular-400.ttf":["packages/font_awesome_flutter/lib/fonts/fa-regular-400.ttf"],"packages/font_awesome_flutter/lib/fonts/fa-solid-900.ttf":["packages/font_awesome_flutter/lib/fonts/fa-solid-900.ttf"]} \ No newline at end of file diff --git a/packages/core/build/unit_test_assets/packages/flutter_map/lib/assets/flutter_map_logo.png b/packages/core/build/unit_test_assets/packages/flutter_map/lib/assets/flutter_map_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..8603d0a3d2a91580f77171968c7d13e73fd1482a GIT binary patch literal 2424 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 0 && amount <= 1, 'مقدار تیرگی باید بین 0 و 1 باشد'); + assert(amount >= 0 && amount <= 1, 'Amount must be between 0 and 1'); final hslColor = HSLColor.fromColor(this); final newLightness = (hslColor.lightness - amount).clamp(0.0, 1.0); final hslDarkerColor = hslColor.withLightness(newLightness); diff --git a/packages/core/lib/utils/safe_call_utils.dart b/packages/core/lib/utils/safe_call_utils.dart index e210b40..c4baa0b 100644 --- a/packages/core/lib/utils/safe_call_utils.dart +++ b/packages/core/lib/utils/safe_call_utils.dart @@ -1,14 +1,17 @@ -import 'package:dio/dio.dart'; import 'package:flutter/foundation.dart'; -import 'package:rasadyar_core/core.dart'; -typedef AsyncCallback = Future Function(); +import '../core.dart'; + +typedef AppAsyncCallback = Future Function(); typedef ErrorCallback = void Function(dynamic error, StackTrace? stackTrace); typedef VoidCallback = void Function(); -// تعریف دقیق تابع safeCall -Future safeCall({ - required AsyncCallback call, +/// this is global safe call function +/// A utility function to safely cal l an asynchronous function with error +/// handling and optional loading, success, and error messages. +/// +Future gSafeCall({ + required AppAsyncCallback call, Function(T result)? onSuccess, ErrorCallback? onError, VoidCallback? onComplete, @@ -17,6 +20,8 @@ Future safeCall({ bool showSuccess = false, bool showToast = false, bool showSnackBar = false, + bool retryOnAuthError = false, + Function()? onTokenRefresh, Function()? onShowLoading, Function()? onHideLoading, Function()? onShowSuccessMessage, @@ -34,18 +39,34 @@ Future safeCall({ } onSuccess?.call(result); - - } catch (error, stackTrace) { - if (showError) { - (onShowErrorMessage ?? _defaultShowErrorMessage)(); + if (retryOnAuthError && isTokenExpiredError(error)) { + try { + await onTokenRefresh?.call(); + final retryResult = await call(); + if (showSuccess) { + (onShowSuccessMessage ?? _defaultShowSuccessMessage)(); + } + onSuccess?.call(retryResult); + return; + } catch (retryError, retryStackTrace) { + if (showError) { + (onShowErrorMessage ?? _defaultShowErrorMessage)(); + } + onError?.call(retryError, retryStackTrace); + if (kDebugMode) { + print('safeCall retry error: $retryError\n$retryStackTrace'); + } + } + } else { + if (showError) { + (onShowErrorMessage ?? _defaultShowErrorMessage)(); + } + onError?.call(error, stackTrace); + if (kDebugMode) { + print('safeCall error: $error\n$stackTrace'); + } } - - onError?.call(error, stackTrace); - if (kDebugMode) { - print('safeCall error: $error\n$stackTrace'); - } - } finally { if (showLoading) { (onHideLoading ?? _defaultHideLoading)(); @@ -69,4 +90,8 @@ void _defaultShowSuccessMessage() { void _defaultShowErrorMessage() { // پیاده‌سازی پیش‌فرض -} \ No newline at end of file +} + +bool isTokenExpiredError(dynamic error) { + return error is DioException && error.response?.statusCode == 401; +} diff --git a/packages/core/test/infrastructure/local/hive_local_storage.dart b/packages/core/test/infrastructure/local/hive_local_storage.dart new file mode 100644 index 0000000..53b7b55 --- /dev/null +++ b/packages/core/test/infrastructure/local/hive_local_storage.dart @@ -0,0 +1,112 @@ +import 'package:flutter/foundation.dart'; +import 'package:rasadyar_core/core.dart'; + +import 'i_local_storage.dart'; + +class HiveLocalStorage implements ILocalStorage { + HiveLocalStorage() { + Hive.initFlutter(); + } + + final Map _boxes = {}; + + @override + Future init() async => await Hive.initFlutter(); + + @override + Future openBox( + String boxName, { + HiveCipher? encryptionCipher, + bool crashRecovery = true, + String? path, + Uint8List? bytes, + String? collection, + }) async { + if (!_boxes.containsKey(boxName)) { + final box = await Hive.openBox( + boxName, + encryptionCipher: encryptionCipher, + crashRecovery: crashRecovery, + ); + _boxes[boxName] = box; + } + } + + @override + T? read({required String boxName, required String key}) { + try { + Box? box = getBox(boxName); + return box?.get(key) as T?; + } on Exception catch (e) { + eLog(e); + return null; + } + } + + @override + Future add({required String boxName, required dynamic value}) async { + Box? box = getBox(boxName); + await box?.add(value); + } + + @override + Future addAll({ + required String boxName, + required Iterable values, + }) async { + Box? box = getBox(boxName); + await box?.addAll(values); + } + + Box? getBox(String boxName) { + final box = _boxes[boxName]; + if (box is Box) { + return box; + } else { + throw Exception('Box $boxName is not of exist'); + } + } + + @override + Future clear(String boxName) async { + await _boxes[boxName]?.clear(); + } + + @override + Future close(String boxName) async => await _boxes[boxName]?.close(); + + @override + Future deleteValue({ + required String boxName, + required String key, + }) async { + Box? box = getBox(boxName); + await box?.delete(key); + } + + @override + Future save({ + required String boxName, + required String key, + required value, + }) async { + Box? box = getBox(boxName); + await box?.put(key, value); + } + + @override + Future saveAll({required String boxName, required Map entries}) async { + Box? box = getBox(boxName); + await box?.putAll(entries); + } + + @override + Future saveAt({ + required String boxName, + required int index, + required value, + }) async { + Box? box = getBox(boxName); + await box?.putAt(index, value); + } +} diff --git a/packages/core/test/infrastructure/local/i_local_storage.dart b/packages/core/test/infrastructure/local/i_local_storage.dart new file mode 100644 index 0000000..7cb4f49 --- /dev/null +++ b/packages/core/test/infrastructure/local/i_local_storage.dart @@ -0,0 +1,44 @@ +import 'package:flutter/foundation.dart'; +import 'package:hive_ce/hive.dart'; + +abstract class ILocalStorage { + Future init(); + + Future openBox( + String boxName, { + HiveCipher? encryptionCipher, + bool crashRecovery = true, + String? path, + Uint8List? bytes, + String? collection, + }); + + T? read({required String boxName, required String key}); + + Future deleteValue({required String boxName, required String key}); + + Future add({required String boxName, required E value}); + + Future addAll({required String boxName, required Iterable values}); + + Future clear(String boxName); + + Future close(String boxName); + + Future save({ + required String boxName, + required String key, + required dynamic value, + }); + + Future saveAt({ + required String boxName, + required int index, + required dynamic value, + }); + + Future saveAll({ + required String boxName, + required Map entries, + }); +} diff --git a/packages/core/test/infrastructure/remote/dio_form_data.dart b/packages/core/test/infrastructure/remote/dio_form_data.dart new file mode 100644 index 0000000..8e74832 --- /dev/null +++ b/packages/core/test/infrastructure/remote/dio_form_data.dart @@ -0,0 +1,23 @@ +import 'package:dio/dio.dart'; +import 'package:flutter/foundation.dart'; + +import 'interfaces/i_form_data.dart'; + +class DioFormData implements IFormData { + final FormData _formData = FormData(); + + @override + void addFile(String field, Uint8List bytes, String filename) { + _formData.files.add(MapEntry( + field, + MultipartFile.fromBytes(bytes, filename: filename), + )); + } + + @override + void addField(String key, String value) { + _formData.fields.add(MapEntry(key, value)); + } + + FormData get raw => _formData; +} diff --git a/packages/core/test/infrastructure/remote/dio_remote_test.dart b/packages/core/test/infrastructure/remote/dio_remote_test.dart new file mode 100644 index 0000000..e69de29 diff --git a/packages/core/test/infrastructure/remote/dio_response.dart b/packages/core/test/infrastructure/remote/dio_response.dart new file mode 100644 index 0000000..30f54eb --- /dev/null +++ b/packages/core/test/infrastructure/remote/dio_response.dart @@ -0,0 +1,20 @@ +import 'interfaces/i_http_response.dart'; +import 'package:dio/dio.dart'; + +class DioResponse implements IHttpResponse { + final Response _response; + + DioResponse(this._response); + + @override + T? get data => _response.data; + + @override + int get statusCode => _response.statusCode ?? 0; + + @override + Map? get headers => _response.headers.map; + + @override + bool get isSuccessful => statusCode >= 200 && statusCode < 300; +} diff --git a/packages/core/test/infrastructure/remote/interfaces/i_form_data.dart b/packages/core/test/infrastructure/remote/interfaces/i_form_data.dart new file mode 100644 index 0000000..ddbda85 --- /dev/null +++ b/packages/core/test/infrastructure/remote/interfaces/i_form_data.dart @@ -0,0 +1,6 @@ +import 'package:flutter/foundation.dart'; + +abstract class IFormData{ + void addFile(String field, Uint8List bytes, String filename); + void addField(String key, String value); +} \ No newline at end of file diff --git a/packages/core/test/infrastructure/remote/interfaces/i_http_client.dart b/packages/core/test/infrastructure/remote/interfaces/i_http_client.dart new file mode 100644 index 0000000..9550c18 --- /dev/null +++ b/packages/core/test/infrastructure/remote/interfaces/i_http_client.dart @@ -0,0 +1,54 @@ + +import 'dart:typed_data'; +import 'package:dio/dio.dart'; +import 'i_form_data.dart'; +import 'i_http_response.dart'; + +abstract class IHttpClient { + Future init(); + + Future> get( + String path, { + Map? queryParameters, + Map? headers, + ProgressCallback? onReceiveProgress, + }); + + + Future> post( + String path, { + dynamic data, + Map? queryParameters, + Map? headers, + ProgressCallback? onSendProgress, + ProgressCallback? onReceiveProgress, + }); + + Future> put( + String path, { + dynamic data, + Map? queryParameters, + Map? headers, + ProgressCallback? onSendProgress, + ProgressCallback? onReceiveProgress, + }); + + Future> delete( + String path, { + dynamic data, + Map? queryParameters, + Map? headers, + }); + + Future> download( + String url, { + ProgressCallback? onReceiveProgress, + }); + + Future> upload( + String path, { + required IFormData formData, + Map? headers, + ProgressCallback? onSendProgress, + }); +} diff --git a/packages/core/test/infrastructure/remote/interfaces/i_http_response.dart b/packages/core/test/infrastructure/remote/interfaces/i_http_response.dart new file mode 100644 index 0000000..461146a --- /dev/null +++ b/packages/core/test/infrastructure/remote/interfaces/i_http_response.dart @@ -0,0 +1,6 @@ +abstract class IHttpResponse { + T? get data; + int get statusCode; + Map? get headers; + bool get isSuccessful; +} diff --git a/packages/core/test/infrastructure/remote/interfaces/i_remote.dart b/packages/core/test/infrastructure/remote/interfaces/i_remote.dart new file mode 100644 index 0000000..648883b --- /dev/null +++ b/packages/core/test/infrastructure/remote/interfaces/i_remote.dart @@ -0,0 +1,4 @@ +abstract class IRemote{ + Future init(); +} + diff --git a/packages/core/test/injection/di_test.dart b/packages/core/test/injection/di_test.dart new file mode 100644 index 0000000..369847c --- /dev/null +++ b/packages/core/test/injection/di_test.dart @@ -0,0 +1,18 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:logger/logger.dart'; +import 'package:rasadyar_core/core.dart'; + +void main() { + setUp(() async { + await setupAllCoreProvider(); + }); + + group('di', () { + test('is ready', () { + expect(diCore.isRegistered(), isTrue); + expect(diCore.isRegistered(), isTrue); + expect(diCore.get(), isA()); + expect(diCore.get(), isA()); + }); + }); +} diff --git a/packages/core/test/presentation/common/app_color_test.dart b/packages/core/test/presentation/common/app_color_test.dart new file mode 100644 index 0000000..34a174f --- /dev/null +++ b/packages/core/test/presentation/common/app_color_test.dart @@ -0,0 +1,43 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:rasadyar_core/presentation/common/app_color.dart'; + + +void main() { + group('AppColor', () { + test('blue colors', () { + expect(AppColor.blueLight, const Color(0xFFeaefff)); + expect(AppColor.blueLightHover, const Color(0xFFe0e7ff)); + expect(AppColor.blueLightActive, const Color(0xFFbecdff)); + expect(AppColor.blueNormal, const Color(0xFF2d5fff)); + expect(AppColor.blueNormalHover, const Color(0xFF2956e6)); + expect(AppColor.blueNormalActive, const Color(0xFF244ccc)); + expect(AppColor.blueDark, const Color(0xFF2247bf)); + expect(AppColor.blueDarkHover, const Color(0xFF1b3999)); + expect(AppColor.blueDarkActive, const Color(0xFF142b73)); + expect(AppColor.blueDarker, const Color(0xFF102159)); + }); + + test('green colors', () { + expect(AppColor.greenLight, const Color(0xFFe6faf5)); + expect(AppColor.greenLightHover, const Color(0xFFd9f7f0)); + expect(AppColor.greenLightActive, const Color(0xFFb0efdf)); + expect(AppColor.greenNormal, const Color(0xFF00cc99)); + expect(AppColor.greenNormalHover, const Color(0xFF00b88a)); + expect(AppColor.greenNormalActive, const Color(0xFF00a37a)); + expect(AppColor.greenDark, const Color(0xFF009973)); + expect(AppColor.greenDarkHover, const Color(0xFF007a5c)); + expect(AppColor.greenDarkActive, const Color(0xFF005c45)); + expect(AppColor.greenDarker, const Color(0xFF004736)); + }); + + test('category colors', () { + expect(AppColor.confirm, AppColor.greenNormalActive); + expect(AppColor.warning, AppColor.yellowNormal); + expect(AppColor.error, AppColor.redNormal); + expect(AppColor.info, AppColor.tealNormal); + }); + + + }); +} \ No newline at end of file diff --git a/packages/core/test/presentation/utils/color_utils_test.dart b/packages/core/test/presentation/utils/color_utils_test.dart new file mode 100644 index 0000000..c628757 --- /dev/null +++ b/packages/core/test/presentation/utils/color_utils_test.dart @@ -0,0 +1,35 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:rasadyar_core/presentation/utils/color_utils.dart'; + +void main() { + group('ColorUtils extension', () { + const baseColor = Color(0xFF42A5F5); + + test('disabledColor returns color with alpha 38', () { + Color disabled = baseColor.disabledColor; + expect(disabled.a, 0.14901960784313725); + expect(disabled.r, baseColor.r); + expect(disabled.g, baseColor.g); + expect(disabled.b, baseColor.b); + }); + + test('hoverColor returns color darkened by 0.5', () { + Color hover = baseColor.hoverColor; + final expected = + HSLColor.fromColor( + baseColor, + ).withLightness((HSLColor.fromColor(baseColor).lightness - 0.5).clamp(0.0, 1.0)).toColor(); + expect(hover.g, expected.g); + }); + + test('pressedColor returns color darkened by 0.1', () { + Color pressed = baseColor.pressedColor; + final expected = + HSLColor.fromColor( + baseColor, + ).withLightness((HSLColor.fromColor(baseColor).lightness - 0.1).clamp(0.0, 1.0)).toColor(); + expect(pressed.r, expected.r); + }); + }); +} diff --git a/packages/core/test/presentation/utils/list_extensions_test.dart b/packages/core/test/presentation/utils/list_extensions_test.dart new file mode 100644 index 0000000..d3a3e76 --- /dev/null +++ b/packages/core/test/presentation/utils/list_extensions_test.dart @@ -0,0 +1,53 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:rasadyar_core/presentation/utils/list_extensions.dart'; + +void main(){ + group('toggle test', (){ + + List list = [1, 2, 3]; + + test('should remove item if it exists', () { + list.toggle(2); + expect(list, [1, 3]); + }); + + + test('should add item if it not exists', () { + list.toggle(10); + expect(list, [1, 3 , 10]); + }); + + + + + }); + group('insert to first item' ,(){ + + List list = [1, 2, 3]; + + test('should insert item at start if it does not exist', () { + list.ensureContainsAtStart(0); + expect(list, [0, 1, 2, 3]); + }); + + test('should not insert item at start if it already exists', () { + list.ensureContainsAtStart(2); + expect(list, [0, 1, 2, 3]); + }); + + + + }); + group('clear and add item' ,(){ + + List list = [1, 2, 3]; + + test('must be clear and add item ', () { + list.resetWith(20); + expect(list, [20]); + }); + + + + }); +} diff --git a/pubspec.yaml b/pubspec.yaml index b8726e5..2ec5d9e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: rasadyar_app description: "A new Flutter project." publish_to: 'none' -version: 1.0.0+1 +version: 1.2.0+2 environment: sdk: ^3.7.2