diff --git a/.fvm/fvm_config.json b/.fvm/fvm_config.json deleted file mode 100644 index 0b54542ff..000000000 --- a/.fvm/fvm_config.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "flutterSdkVersion": "3.19.6", - "flavors": {} -} \ No newline at end of file diff --git a/.github/workflows/credential_manifest.yaml b/.github/workflows/credential_manifest.yaml index 3a78be899..e87ee708f 100644 --- a/.github/workflows/credential_manifest.yaml +++ b/.github/workflows/credential_manifest.yaml @@ -1,13 +1,13 @@ name: credential_manifest -on: [ pull_request, push ] +on: [pull_request, push] jobs: build: uses: TalaoDAO/AltMe/.github/workflows/flutter_package.yaml@main with: flutter_channel: stable - flutter_version: 3.19.6 + flutter_version: 3.22.1 min_coverage: 30 working_directory: packages/credential_manifest - dart_sdk: 3.3.4 + dart_sdk: 3.4.1 diff --git a/.github/workflows/cryptocurrency_keys.yaml b/.github/workflows/cryptocurrency_keys.yaml index 41c60bf47..a0e4f85c8 100644 --- a/.github/workflows/cryptocurrency_keys.yaml +++ b/.github/workflows/cryptocurrency_keys.yaml @@ -1,13 +1,13 @@ name: cryptocurrency_keys -on: [ pull_request, push ] +on: [pull_request, push] jobs: build: uses: TalaoDAO/AltMe/.github/workflows/flutter_package.yaml@main with: flutter_channel: stable - flutter_version: 3.19.6 + flutter_version: 3.22.1 min_coverage: 30 working_directory: packages/cryptocurrency_keys - dart_sdk: 3.3.4 + dart_sdk: 3.4.1 diff --git a/.github/workflows/did_kit.yaml b/.github/workflows/did_kit.yaml new file mode 100644 index 000000000..2647f7254 --- /dev/null +++ b/.github/workflows/did_kit.yaml @@ -0,0 +1,14 @@ +name: did_kit + +on: [pull_request, push] + +jobs: + build: + uses: TalaoDAO/AltMe/.github/workflows/flutter_package.yaml@main + with: + flutter_channel: stable + flutter_version: 3.22.1 + min_coverage: 30 + working_directory: packages/did_kit + dart_sdk: 3.4.1 + build_runner: false diff --git a/.github/workflows/jwt_decode.yaml b/.github/workflows/jwt_decode.yaml index 9f5dd42f6..aed836c8f 100644 --- a/.github/workflows/jwt_decode.yaml +++ b/.github/workflows/jwt_decode.yaml @@ -1,14 +1,14 @@ name: jwt_decode -on: [ pull_request, push ] +on: [pull_request, push] jobs: build: uses: TalaoDAO/AltMe/.github/workflows/flutter_package.yaml@main with: flutter_channel: stable - flutter_version: 3.19.6 + flutter_version: 3.22.1 min_coverage: 30 working_directory: packages/jwt_decode - dart_sdk: 3.3.4 + dart_sdk: 3.4.1 build_runner: false diff --git a/.github/workflows/key_generator.yaml b/.github/workflows/key_generator.yaml index 7be1bb9dc..e0f9419df 100644 --- a/.github/workflows/key_generator.yaml +++ b/.github/workflows/key_generator.yaml @@ -1,14 +1,14 @@ name: key_generator -on: [ pull_request, push ] +on: [pull_request, push] jobs: build: uses: TalaoDAO/AltMe/.github/workflows/flutter_package.yaml@main with: flutter_channel: stable - flutter_version: 3.19.6 + flutter_version: 3.22.1 min_coverage: 30 working_directory: packages/key_generator - dart_sdk: 3.3.4 + dart_sdk: 3.4.1 build_runner: false diff --git a/.github/workflows/oidc4vc.yaml b/.github/workflows/oidc4vc.yaml new file mode 100644 index 000000000..cb83c408f --- /dev/null +++ b/.github/workflows/oidc4vc.yaml @@ -0,0 +1,14 @@ +name: oidc4vc + +on: [pull_request, push] + +jobs: + build: + uses: TalaoDAO/AltMe/.github/workflows/flutter_package.yaml@main + with: + flutter_channel: stable + flutter_version: 3.22.1 + min_coverage: 30 + working_directory: packages/oidc4vc + dart_sdk: 3.4.1 + build_runner: true diff --git a/.github/workflows/polygonid.yaml b/.github/workflows/polygonid.yaml index 9c04e9c6a..507dffe6e 100644 --- a/.github/workflows/polygonid.yaml +++ b/.github/workflows/polygonid.yaml @@ -1,14 +1,14 @@ name: polygonid -on: [ pull_request, push ] +on: [pull_request, push] jobs: build: uses: TalaoDAO/AltMe/.github/workflows/flutter_package.yaml@main with: flutter_channel: stable - flutter_version: 3.19.6 + flutter_version: 3.22.1 min_coverage: 30 working_directory: packages/polygonid - dart_sdk: 3.3.4 + dart_sdk: 3.4.1 build_runner: false diff --git a/.github/workflows/secure_storage.yaml b/.github/workflows/secure_storage.yaml new file mode 100644 index 000000000..40b656652 --- /dev/null +++ b/.github/workflows/secure_storage.yaml @@ -0,0 +1,14 @@ +name: secure_storage + +on: [pull_request, push] + +jobs: + build: + uses: TalaoDAO/AltMe/.github/workflows/flutter_package.yaml@main + with: + flutter_channel: stable + flutter_version: 3.22.1 + min_coverage: 30 + working_directory: packages/secure_storage + dart_sdk: 3.4.1 + build_runner: false diff --git a/lib/app/shared/dio_client/dio_client.dart b/lib/app/shared/dio_client/dio_client.dart index 462b48704..083528646 100644 --- a/lib/app/shared/dio_client/dio_client.dart +++ b/lib/app/shared/dio_client/dio_client.dart @@ -55,35 +55,14 @@ class DioClient { bool isCachingEnabled = false, }) async { try { - final isInternetAvailable = await isConnected(); - if (!isInternetAvailable) { - throw NetworkException( - message: NetworkError.NETWORK_ERROR_NO_INTERNET_CONNECTION, - ); - } - final stopwatch = Stopwatch()..start(); await getSpecificHeader(uri, headers); log.i('uri - $uri'); - - final cachedData = await secureStorageProvider.get(uri); dynamic response; - if (!isCachingEnabled || cachedData == null) { - response = await dio.get( - uri, - queryParameters: queryParameters, - options: options, - cancelToken: cancelToken, - onReceiveProgress: onReceiveProgress, - ); - } else { - final cachedDataJson = jsonDecode(cachedData); - final expiry = int.parse(cachedDataJson['expiry'].toString()); - - final isExpired = DateTime.now().millisecondsSinceEpoch > expiry; - - if (isExpired) { + if (isCachingEnabled) { + final cachedData = await secureStorageProvider.get(uri); + if (cachedData == null) { response = await dio.get( uri, queryParameters: queryParameters, @@ -92,18 +71,34 @@ class DioClient { onReceiveProgress: onReceiveProgress, ); } else { - /// directly return cached data - /// returned here to avoid the caching override everytime - final response = await cachedDataJson['data']; - log.i('Time - ${stopwatch.elapsed}'); - return response; + final cachedDataJson = jsonDecode(cachedData); + final expiry = int.parse(cachedDataJson['expiry'].toString()); + + final isExpired = DateTime.now().millisecondsSinceEpoch > expiry; + if (isExpired) { + response = await dio.get( + uri, + queryParameters: queryParameters, + options: options, + cancelToken: cancelToken, + onReceiveProgress: onReceiveProgress, + ); + } else { + /// directly return cached data + /// returned here to avoid the caching override everytime + final response = await cachedDataJson['data']; + return response; + } } + } else { + response = await dio.get( + uri, + queryParameters: queryParameters, + options: options, + cancelToken: cancelToken, + onReceiveProgress: onReceiveProgress, + ); } - final expiry = - DateTime.now().add(const Duration(days: 2)).millisecondsSinceEpoch; - - final value = {'expiry': expiry, 'data': response.data}; - await secureStorageProvider.set(uri, jsonEncode(value)); log.i('Time - ${stopwatch.elapsed}'); return response.data; @@ -153,13 +148,6 @@ class DioClient { }, }) async { try { - final isInternetAvailable = await isConnected(); - if (!isInternetAvailable) { - throw NetworkException( - message: NetworkError.NETWORK_ERROR_NO_INTERNET_CONNECTION, - ); - } - final stopwatch = Stopwatch()..start(); await getSpecificHeader(uri, headers); final response = await dio.post( diff --git a/lib/app/shared/enum/credential_category.dart b/lib/app/shared/enum/credential_category.dart index af7c4faed..cc21cc31f 100644 --- a/lib/app/shared/enum/credential_category.dart +++ b/lib/app/shared/enum/credential_category.dart @@ -1,6 +1,5 @@ import 'package:altme/l10n/l10n.dart'; import 'package:equatable/equatable.dart'; -import 'package:flutter/material.dart'; enum CredentialCategory { advantagesCards, @@ -80,8 +79,7 @@ extension CredentialCategoryX on CredentialCategory { } } - CredentialCategoryConfig config(BuildContext context) { - final l10n = context.l10n; + CredentialCategoryConfig config(AppLocalizations l10n) { switch (this) { case CredentialCategory.advantagesCards: return CredentialCategoryConfig( diff --git a/lib/app/shared/extension/credential_status.dart b/lib/app/shared/enum/status/credential_status_extension.dart similarity index 91% rename from lib/app/shared/extension/credential_status.dart rename to lib/app/shared/enum/status/credential_status_extension.dart index 020172507..526687c22 100644 --- a/lib/app/shared/extension/credential_status.dart +++ b/lib/app/shared/enum/status/credential_status_extension.dart @@ -1,6 +1,5 @@ import 'package:altme/app/app.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; extension CredentialStatusExtension on CredentialStatus { @@ -44,7 +43,7 @@ extension CredentialStatusExtension on CredentialStatus { Color color(BuildContext context) { switch (this) { case CredentialStatus.active: - return Theme.of(context).colorScheme.activeColor; + return Theme.of(context).colorScheme.onTertiary; case CredentialStatus.invalidStatus: case CredentialStatus.expired: case CredentialStatus.pending: @@ -52,7 +51,7 @@ extension CredentialStatusExtension on CredentialStatus { case CredentialStatus.invalidSignature: case CredentialStatus.statusListInvalidSignature: case CredentialStatus.noStatus: - return Theme.of(context).colorScheme.inactiveColor; + return Theme.of(context).colorScheme.error; } } } diff --git a/lib/app/shared/enum/status/status.dart b/lib/app/shared/enum/status/status.dart index 633bdfb16..f924629ab 100644 --- a/lib/app/shared/enum/status/status.dart +++ b/lib/app/shared/enum/status/status.dart @@ -2,6 +2,7 @@ export 'app_status.dart'; export 'beacon_status.dart'; export 'credential_detail_tab_status.dart'; export 'credential_status.dart'; +export 'credential_status_extension.dart'; export 'credentials_status.dart'; export 'home_status.dart'; export 'kyc_verification_status.dart'; diff --git a/lib/app/shared/enum/type/message_type/message_type.dart b/lib/app/shared/enum/type/message_type/message_type.dart index 957cf94ee..ba575dc44 100644 --- a/lib/app/shared/enum/type/message_type/message_type.dart +++ b/lib/app/shared/enum/type/message_type/message_type.dart @@ -1,5 +1,4 @@ import 'package:altme/app/shared/constants/icon_strings.dart'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; part 'message_type_extension.dart'; diff --git a/lib/app/shared/enum/type/message_type/message_type_extension.dart b/lib/app/shared/enum/type/message_type/message_type_extension.dart index e33279aaa..59019c38f 100644 --- a/lib/app/shared/enum/type/message_type/message_type_extension.dart +++ b/lib/app/shared/enum/type/message_type/message_type_extension.dart @@ -4,13 +4,13 @@ extension MessageTypeX on MessageType { Color getColor(BuildContext context) { switch (this) { case MessageType.error: - return Theme.of(context).colorScheme.alertErrorMessage; + return Theme.of(context).colorScheme.error; case MessageType.warning: - return Theme.of(context).colorScheme.alertWarningMessage; + return Theme.of(context).colorScheme.onErrorContainer; case MessageType.info: - return Theme.of(context).colorScheme.alertInfoMessage; + return Theme.of(context).colorScheme.outline; case MessageType.success: - return Theme.of(context).colorScheme.alertSuccessMessage; + return Theme.of(context).colorScheme.onTertiary; } } diff --git a/lib/app/shared/extension/extension.dart b/lib/app/shared/extension/extension.dart index 1703838c0..e333dbbdc 100644 --- a/lib/app/shared/extension/extension.dart +++ b/lib/app/shared/extension/extension.dart @@ -1,6 +1,5 @@ export 'bigint_extension.dart'; -export 'credential_status.dart'; export 'double_extension.dart'; export 'iterable_extension.dart'; export 'string_extension.dart'; -export 'unit8List_extension.dart'; +export 'unit8_list_extension.dart'; diff --git a/lib/app/shared/extension/unit8List_extension.dart b/lib/app/shared/extension/unit8_list_extension.dart similarity index 100% rename from lib/app/shared/extension/unit8List_extension.dart rename to lib/app/shared/extension/unit8_list_extension.dart diff --git a/lib/app/shared/helper_functions/helper_functions.dart b/lib/app/shared/helper_functions/helper_functions.dart index e38e23b46..2d8e67fd5 100644 --- a/lib/app/shared/helper_functions/helper_functions.dart +++ b/lib/app/shared/helper_functions/helper_functions.dart @@ -1,16 +1,13 @@ import 'dart:convert'; -import 'dart:io'; import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/oidc4vc/oidc4vc.dart'; import 'package:altme/selective_disclosure/selective_disclosure.dart'; import 'package:asn1lib/asn1lib.dart' as asn1lib; -import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:convert/convert.dart'; import 'package:credential_manifest/credential_manifest.dart'; import 'package:dartez/dartez.dart'; -import 'package:device_info_plus/device_info_plus.dart'; import 'package:dio/dio.dart'; import 'package:fast_base58/fast_base58.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; @@ -25,6 +22,9 @@ import 'package:secure_storage/secure_storage.dart'; import 'package:x509/x509.dart' as x509; import 'package:x509/x509.dart'; +export 'is_connected_to_internet.dart'; +export 'test_platform.dart'; + String generateDefaultAccountName( int accountIndex, List accountNameList, @@ -38,10 +38,6 @@ String generateDefaultAccountName( } } -bool get isAndroid => Platform.isAndroid; - -bool get isIOS => Platform.isIOS; - String getIssuerDid({required Uri uriToCheck}) { String did = ''; uriToCheck.queryParameters.forEach((key, value) { @@ -75,11 +71,15 @@ KeyStoreModel getKeysFromSecretKey({required String secretKey}) { ); } -String stringToHexPrefixedWith05({required String payload}) { +String stringToHexPrefixedWith05({ + required String payload, + DateTime? dateTime, +}) { + dateTime ??= DateTime.now(); final String formattedInput = [ 'Tezos Signed Message:', 'altme.io', - DateTime.now().toString(), + dateTime.toString(), payload, ].join(' '); @@ -94,23 +94,6 @@ String stringToHexPrefixedWith05({required String payload}) { return payloadBytes; } -Future isConnected() async { - final log = getLogger('Check Internet Connection'); - - if (!isAndroid) { - if (!(await DeviceInfoPlugin().iosInfo).isPhysicalDevice) { - return true; - } - } - final connectivityResult = await Connectivity().checkConnectivity(); - if (connectivityResult == ConnectivityResult.mobile || - connectivityResult == ConnectivityResult.wifi) { - return true; - } - log.e('No Internet Connection'); - return false; -} - String getCredentialName(String constraints) { final dynamic constraintsJson = jsonDecode(constraints); final fieldsPath = JsonPath(r'$..fields'); @@ -238,11 +221,13 @@ Future getStoragePermission() async { return false; } -String getDateTimeWithoutSpace() { - final dateTime = DateTime.fromMicrosecondsSinceEpoch( - DateTime.now().microsecondsSinceEpoch, +String getDateTimeWithoutSpace({DateTime? dateTime}) { + dateTime ??= DateTime.now(); + + final dateTimeString = DateTime.fromMicrosecondsSinceEpoch( + dateTime.microsecondsSinceEpoch, ).toString().replaceAll(' ', '-'); - return dateTime; + return dateTimeString; } int getIndexValue({ @@ -273,12 +258,23 @@ Future getPrivateKey({ required ProfileCubit profileCubit, required DidKeyType didKeyType, }) async { + final customOidc4vcProfile = profileCubit.state.model.profileSetting + .selfSovereignIdentityOptions.customOidc4vcProfile; + + if (customOidc4vcProfile.clientType == ClientType.p256JWKThumprint) { + final privateKey = + await getP256KeyToGetAndPresentVC(profileCubit.secureStorageProvider); + + return privateKey; + } + final mnemonic = await profileCubit.secureStorageProvider .get(SecureStorageKeys.ssiMnemonic); switch (didKeyType) { case DidKeyType.edDSA: - final ssiKey = await getSecureStorage.get(SecureStorageKeys.ssiKey); + final ssiKey = await profileCubit.secureStorageProvider + .get(SecureStorageKeys.ssiKey); return ssiKey.toString(); case DidKeyType.secp256k1: @@ -719,6 +715,7 @@ Future< final OpenIdConfiguration openIdConfiguration = await oidc4vc.getOpenIdConfig( baseUrl: issuer, isAuthorizationServer: false, + dio: client.dio, ); if (preAuthorizedCode == null) { @@ -743,6 +740,7 @@ Future< authorizationServerConfiguration = await oidc4vc.getOpenIdConfig( baseUrl: authorizationServer, isAuthorizationServer: true, + dio: client.dio, ); } @@ -998,6 +996,7 @@ Future isEBSIV3ForVerifiers({ await oidc4vc.getOpenIdConfig( baseUrl: clientId, isAuthorizationServer: false, + dio: Dio(), ); final subjectTrustFrameworksSupported = @@ -1164,7 +1163,7 @@ MessageHandler getMessageHandler(dynamic e) { 'error_description': 'Failed to extract header from jwt.', }, ); - } else if (stringException.contains('INVALID_TOKEN')) { + } else if (stringException.contains('INVALID_PAYLOAD')) { return ResponseMessage( data: { 'error': 'invalid_format', @@ -1585,8 +1584,11 @@ Future<(String?, String?, String?, String?)> getClientDetails({ final didKeyType = customOidc4vcProfile.defaultDid; - final privateKey = - await getP256KeyToGetAndPresentVC(profileCubit.secureStorageProvider); + final String privateKey = await fetchPrivateKey( + profileCubit: profileCubit, + isEBSIV3: isEBSIV3, + didKeyType: didKeyType, + ); final (did, _) = await fetchDidAndKid( privateKey: privateKey, @@ -1600,7 +1602,7 @@ Future<(String?, String?, String?, String?)> getClientDetails({ did: '', // just added as it is required field mediaType: MediaType.basic, // just added as it is required field clientType: - ClientType.p256JWKThumprint, // just added as it is required field + customOidc4vcProfile.clientType, // just added as it is required field proofHeaderType: customOidc4vcProfile.proofHeader, clientId: '', // just added as it is required field ); @@ -1867,7 +1869,7 @@ List getStringCredentialsForToken({ presentJwtVc = false; presentJwtVcJson = true; presentVcSdJwt = false; - } else if (presentJwtVc && vcFormatType == VCFormatType.vcSdJWT) { + } else if (presentVcSdJwt && vcFormatType == VCFormatType.vcSdJWT) { presentLdpVc = false; presentJwtVc = false; presentJwtVcJson = false; @@ -2134,7 +2136,10 @@ String? getWalletAddress(CredentialSubjectModel credentialSubjectModel) { return null; } -Future fetchRpcUrl(BlockchainNetwork blockchainNetwork) async { +Future fetchRpcUrl({ + required BlockchainNetwork blockchainNetwork, + required DotEnv dotEnv, +}) async { String rpcUrl = ''; if (blockchainNetwork is BinanceNetwork || @@ -2142,8 +2147,8 @@ Future fetchRpcUrl(BlockchainNetwork blockchainNetwork) async { rpcUrl = blockchainNetwork.rpcNodeUrl as String; } else { if (blockchainNetwork.networkname == 'Mainnet') { - await dotenv.load(); - final String infuraApiKey = dotenv.get('INFURA_API_KEY'); + await dotEnv.load(); + final String infuraApiKey = dotEnv.get('INFURA_API_KEY'); late String prefixUrl; diff --git a/lib/app/shared/helper_functions/is_connected_to_internet.dart b/lib/app/shared/helper_functions/is_connected_to_internet.dart new file mode 100644 index 000000000..ae06c4dc9 --- /dev/null +++ b/lib/app/shared/helper_functions/is_connected_to_internet.dart @@ -0,0 +1,21 @@ +import 'package:altme/app/logger/get_logger.dart'; +import 'package:altme/app/shared/helper_functions/test_platform.dart'; +import 'package:connectivity_plus/connectivity_plus.dart'; +import 'package:device_info_plus/device_info_plus.dart'; + +Future isConnectedToInternet() async { + final log = getLogger('Check Internet Connection'); + + if (!isAndroid) { + if (!(await DeviceInfoPlugin().iosInfo).isPhysicalDevice) { + return true; + } + } + final connectivityResult = await Connectivity().checkConnectivity(); + if (connectivityResult == ConnectivityResult.mobile || + connectivityResult == ConnectivityResult.wifi) { + return true; + } + log.e('No Internet Connection'); + return false; +} diff --git a/lib/app/shared/helper_functions/test_platform.dart b/lib/app/shared/helper_functions/test_platform.dart new file mode 100644 index 000000000..5e9a3e92b --- /dev/null +++ b/lib/app/shared/helper_functions/test_platform.dart @@ -0,0 +1,5 @@ +import 'dart:io'; + +bool get isAndroid => Platform.isAndroid; + +bool get isIOS => Platform.isIOS; diff --git a/lib/app/shared/issuer/check_issuer.dart b/lib/app/shared/issuer/check_issuer.dart index 7b4851735..e5a2308c8 100644 --- a/lib/app/shared/issuer/check_issuer.dart +++ b/lib/app/shared/issuer/check_issuer.dart @@ -28,8 +28,6 @@ class CheckIssuer { try { log.i('fetching issuer data'); - final dynamic response = - await client.get('$checkIssuerServerUrl/$didToTest'); if (checkIssuerServerUrl == Urls.checkIssuerEbsiUrl) { return Issuer( preferredName: '', @@ -44,6 +42,8 @@ class CheckIssuer { ); } + final dynamic response = + await client.get('$checkIssuerServerUrl/$didToTest'); final issuer = Issuer.fromJson(response['issuer'] as Map); diff --git a/lib/app/shared/loading/loading_view.dart b/lib/app/shared/loading/loading_view.dart index c6caa7813..a2ed95cf9 100644 --- a/lib/app/shared/loading/loading_view.dart +++ b/lib/app/shared/loading/loading_view.dart @@ -3,7 +3,7 @@ import 'dart:async'; import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -54,7 +54,7 @@ class LoadingView { minWidth: size.width * 0.5, ), decoration: BoxDecoration( - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surfaceDim, borderRadius: BorderRadius.circular(10), ), child: Padding( @@ -80,7 +80,7 @@ class LoadingView { return Text( snapshopt.data.toString(), textAlign: TextAlign.center, - style: Theme.of(context).textTheme.loadingText, + style: Theme.of(context).textTheme.titleMedium, ); } else { return Container(); diff --git a/lib/app/shared/local_notification/local_notification.dart b/lib/app/shared/local_notification/local_notification.dart deleted file mode 100644 index 65de789f9..000000000 --- a/lib/app/shared/local_notification/local_notification.dart +++ /dev/null @@ -1,82 +0,0 @@ -import 'package:altme/app/app.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter_local_notifications/flutter_local_notifications.dart'; -import 'package:flutter_native_timezone/flutter_native_timezone.dart'; -import 'package:timezone/data/latest.dart' as tz; -import 'package:timezone/timezone.dart' as tz; - -class LocalNotification { - factory LocalNotification() { - return _localNotification; - } - - LocalNotification._internal(); - - static final LocalNotification _localNotification = - LocalNotification._internal(); - - final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = - FlutterLocalNotificationsPlugin(); - - Future init() async { - await _configureLocalTimeZone(); - const android = AndroidInitializationSettings('@mipmap/ic_launcher'); - const iOS = DarwinInitializationSettings(); - const initSettings = InitializationSettings(android: android, iOS: iOS); - - await flutterLocalNotificationsPlugin.initialize( - initSettings, - onDidReceiveNotificationResponse: - (NotificationResponse notificationResponse) { - switch (notificationResponse.notificationResponseType) { - case NotificationResponseType.selectedNotification: - case NotificationResponseType.selectedNotificationAction: - _onSelectNotification(notificationResponse.payload); - } - }, - ); - } - - Future _configureLocalTimeZone() async { - if (kIsWeb) { - return; - } - tz.initializeTimeZones(); - final String timeZoneName = await FlutterNativeTimezone.getLocalTimezone(); - tz.setLocalLocation(tz.getLocation(timeZoneName)); - } - - Future showNotification({ - String? link, - String? title, - String? message, - }) async { - const android = AndroidNotificationDetails( - 'channel id', - 'channel name', - channelDescription: 'channel description', - priority: Priority.high, - importance: Importance.max, - ); - const iOS = DarwinNotificationDetails(); - const platform = NotificationDetails(android: android, iOS: iOS); - - await flutterLocalNotificationsPlugin.show( - 0, // notification id - title, - message, - platform, - payload: link, - ); - } - - Future _onSelectNotification(String? link) async { - if (link != null) { - await LaunchUrl.launch(link); - } - } - - Future cancelAllNotifications() async { - await flutterLocalNotificationsPlugin.cancelAll(); - } -} diff --git a/lib/app/shared/shared.dart b/lib/app/shared/shared.dart index 342c58ab6..c43fa398d 100644 --- a/lib/app/shared/shared.dart +++ b/lib/app/shared/shared.dart @@ -9,7 +9,6 @@ export 'issuer/issuer.dart'; export 'launch_url/launch_url.dart'; export 'loading/loading.dart'; export 'local_auth/local_auth_api.dart'; -export 'local_notification/local_notification.dart'; export 'm_web3_client/m_web3_client.dart'; export 'message_handler/message_handler.dart'; export 'models/model.dart'; diff --git a/lib/app/shared/translation/translation.dart b/lib/app/shared/translation/translation.dart index 81859b5f2..2a10e3731 100644 --- a/lib/app/shared/translation/translation.dart +++ b/lib/app/shared/translation/translation.dart @@ -9,6 +9,7 @@ class GetTranslation { String translation; final translated = translations.where((element) => element.language == l10n.localeName); + if (translated.isEmpty) { final List translationList = translations.where((element) => element.language == 'en').toList(); diff --git a/lib/app/shared/validators/wallet_address_validator.dart b/lib/app/shared/validators/wallet_address_validator.dart index d6ccf967c..97e99232f 100644 --- a/lib/app/shared/validators/wallet_address_validator.dart +++ b/lib/app/shared/validators/wallet_address_validator.dart @@ -16,7 +16,6 @@ mixin WalletAddressValidator { } else if (address.startsWith('0x')) { return _validEtherumAddress(address); } else if (address.startsWith('tz')) { - // TODO(all): validate tezos address return address.length > 8; } else { // The wallet not support other blockchains diff --git a/lib/app/shared/widget/add_account/add_account_button.dart b/lib/app/shared/widget/add_account/add_account_button.dart index 72c46772a..51add3812 100644 --- a/lib/app/shared/widget/add_account/add_account_button.dart +++ b/lib/app/shared/widget/add_account/add_account_button.dart @@ -1,6 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class AddAccountButton extends StatelessWidget { @@ -31,14 +31,14 @@ class AddAccountButton extends StatelessWidget { ), Text( l10n.cryptoAddAccount, - style: Theme.of(context).textTheme.title, + style: Theme.of(context).textTheme.headlineSmall, ), ], ), const SizedBox(height: 3), Text( l10n.createOrImportNewAccount, - style: Theme.of(context).textTheme.listSubtitle, + style: Theme.of(context).textTheme.titleSmall, ), ], ), diff --git a/lib/app/shared/widget/app_version_drawer.dart b/lib/app/shared/widget/app_version_drawer.dart index de408a656..137fa5431 100644 --- a/lib/app/shared/widget/app_version_drawer.dart +++ b/lib/app/shared/widget/app_version_drawer.dart @@ -1,4 +1,3 @@ -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; import 'package:package_info_plus/package_info_plus.dart'; @@ -23,10 +22,7 @@ class AppVersionDrawer extends StatelessWidget { return Text( isShortForm ? 'V $version' : 'Version $version ($buildNumber)', - style: Theme.of(context) - .textTheme - .bodySmall2 - .copyWith(fontWeight: FontWeight.w800), + style: Theme.of(context).textTheme.bodyMedium, ); case ConnectionState.waiting: case ConnectionState.none: diff --git a/lib/app/shared/widget/base/background_card.dart b/lib/app/shared/widget/base/background_card.dart index f2ccdd398..6ab3b67ef 100644 --- a/lib/app/shared/widget/base/background_card.dart +++ b/lib/app/shared/widget/base/background_card.dart @@ -26,7 +26,7 @@ class BackgroundCard extends StatelessWidget { height: height, width: width, decoration: BoxDecoration( - color: color ?? Theme.of(context).colorScheme.surface, + color: color ?? Theme.of(context).colorScheme.surfaceBright, borderRadius: const BorderRadius.all(Radius.circular(15)), ), child: child, diff --git a/lib/app/shared/widget/base/box_decoration.dart b/lib/app/shared/widget/base/box_decoration.dart index 3e441fa34..8d9da92ac 100644 --- a/lib/app/shared/widget/base/box_decoration.dart +++ b/lib/app/shared/widget/base/box_decoration.dart @@ -116,7 +116,7 @@ class BaseBoxDecoration extends Decoration { color, shapeColor, borderRadius, - Object.hashAll(boxShadow!), + boxShadow != null ? Object.hashAll(boxShadow!) : null, gradient, ); } diff --git a/lib/app/shared/widget/base/credential_field.dart b/lib/app/shared/widget/base/credential_field.dart index 80b2909a9..9d6787448 100644 --- a/lib/app/shared/widget/base/credential_field.dart +++ b/lib/app/shared/widget/base/credential_field.dart @@ -1,4 +1,3 @@ -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; class CredentialField extends StatelessWidget { @@ -66,7 +65,7 @@ class DisplayCredentialField extends StatelessWidget { if (title != null) ...[ TextSpan( text: showVertically ? title : '$title: ', - style: textTheme.credentialFieldTitle.copyWith( + style: textTheme.bodyMedium!.copyWith( color: titleColor, ), ), @@ -74,8 +73,7 @@ class DisplayCredentialField extends StatelessWidget { ], TextSpan( text: value, - style: textTheme.credentialFieldDescription - .copyWith(color: valueColor), + style: textTheme.bodyMedium!.copyWith(color: valueColor), ), ], ), diff --git a/lib/app/shared/widget/base/illustration_page.dart b/lib/app/shared/widget/base/illustration_page.dart index e3244bd5e..c3e698e25 100644 --- a/lib/app/shared/widget/base/illustration_page.dart +++ b/lib/app/shared/widget/base/illustration_page.dart @@ -1,5 +1,5 @@ import 'package:altme/app/app.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; @@ -41,8 +41,7 @@ class BaseIllustrationPage extends StatelessWidget { Text( description!, textAlign: TextAlign.center, - style: - Theme.of(context).textTheme.illustrationPageDescription, + style: Theme.of(context).textTheme.bodyMedium, ), Expanded(child: Container()), MyOutlinedButton( diff --git a/lib/app/shared/widget/base/markdown_page.dart b/lib/app/shared/widget/base/markdown_page.dart index c8c7306f2..39e4ffcf2 100644 --- a/lib/app/shared/widget/base/markdown_page.dart +++ b/lib/app/shared/widget/base/markdown_page.dart @@ -1,5 +1,4 @@ import 'package:altme/app/app.dart'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_markdown/flutter_markdown.dart'; @@ -33,13 +32,17 @@ class MarkdownPage extends StatelessWidget { data: snapshot.data!, styleSheet: MarkdownStyleSheet( h1: TextStyle( - color: Theme.of(context).colorScheme.markDownH1, + color: Theme.of(context).colorScheme.onSurface, ), h2: TextStyle( - color: Theme.of(context).colorScheme.markDownH2, + color: Theme.of(context).colorScheme.onSurface, ), - a: TextStyle(color: Theme.of(context).colorScheme.markDownA), - p: TextStyle(color: Theme.of(context).colorScheme.markDownP), + a: TextStyle(color: Theme.of(context).colorScheme.primary), + p: TextStyle( + color: Theme.of(context) + .colorScheme + .onSurface + .withOpacity(0.6),), ), onTapLink: (text, href, title) => _onTapLink(href), ); diff --git a/lib/app/shared/widget/base/otp_textfield.dart b/lib/app/shared/widget/base/otp_textfield.dart index 08ea5d5fc..67962c99e 100644 --- a/lib/app/shared/widget/base/otp_textfield.dart +++ b/lib/app/shared/widget/base/otp_textfield.dart @@ -1,4 +1,3 @@ -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; class OtpTextField extends StatelessWidget { @@ -27,26 +26,26 @@ class OtpTextField extends StatelessWidget { style: Theme.of(context).textTheme.labelLarge, cursorColor: Theme.of(context).colorScheme.onPrimary, decoration: InputDecoration( - fillColor: Theme.of(context).colorScheme.lightPurple, + fillColor: Theme.of(context).colorScheme.secondary, filled: true, border: OutlineInputBorder( borderRadius: BorderRadius.circular(10), borderSide: BorderSide( - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, width: 1, ), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10), borderSide: BorderSide( - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, width: 1, ), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(10), borderSide: BorderSide( - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, width: 1.5, ), ), @@ -67,7 +66,7 @@ class OtpTextField extends StatelessWidget { } }, onTap: () { - // Move the cursor to the end of the text when the TextField gains focus + // Move the cursor to the end of the text when TextField get focus controllers[index].selection = TextSelection.fromPosition( TextPosition(offset: controllers[index].text.length), ); diff --git a/lib/app/shared/widget/base/page.dart b/lib/app/shared/widget/base/page.dart index 4f7e1ba16..f65dee2a6 100644 --- a/lib/app/shared/widget/base/page.dart +++ b/lib/app/shared/widget/base/page.dart @@ -129,7 +129,7 @@ class _BasePageState extends State with WidgetsBindingObserver { widget.floatingActionButtonLocation, extendBody: widget.extendBelow ?? false, backgroundColor: widget.backgroundColor ?? - Theme.of(context).colorScheme.background, + Theme.of(context).colorScheme.surface, appBar: (widget.title == null && widget.titleLeading == null && widget.titleTrailing == null) @@ -172,7 +172,7 @@ class _BasePageState extends State with WidgetsBindingObserver { floatingActionButtonLocation: widget.floatingActionButtonLocation, extendBody: widget.extendBelow ?? false, backgroundColor: widget.backgroundColor ?? - Theme.of(context).colorScheme.background, + Theme.of(context).colorScheme.surface, appBar: (widget.title == null && widget.titleLeading == null && widget.titleTrailing == null) diff --git a/lib/app/shared/widget/base/text_field.dart b/lib/app/shared/widget/base/text_field.dart index 78d94aee9..179c409b7 100644 --- a/lib/app/shared/widget/base/text_field.dart +++ b/lib/app/shared/widget/base/text_field.dart @@ -1,4 +1,3 @@ -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; class BaseTextField extends StatelessWidget { @@ -46,7 +45,8 @@ class BaseTextField extends StatelessWidget { @override Widget build(BuildContext context) { - final border = borderColor ?? Theme.of(context).colorScheme.tertiary; + final border = + borderColor ?? Theme.of(context).colorScheme.onPrimaryContainer; return SizedBox( height: height, child: TextFormField( @@ -57,7 +57,7 @@ class BaseTextField extends StatelessWidget { maxLines: maxLines, enableInteractiveSelection: true, textCapitalization: textCapitalization, - style: Theme.of(context).textTheme.normal, + style: Theme.of(context).textTheme.bodyMedium, validator: validator, obscureText: obscureText, decoration: InputDecoration( @@ -98,11 +98,11 @@ class BaseTextField extends StatelessWidget { width: 1.5, ), ), - fillColor: fillColor ?? Theme.of(context).colorScheme.background, + fillColor: fillColor ?? Theme.of(context).colorScheme.surface, filled: true, errorText: error, labelText: label, - labelStyle: Theme.of(context).textTheme.normal, + labelStyle: Theme.of(context).textTheme.bodyMedium, prefixIcon: prefixIcon, prefixIconConstraints: const BoxConstraints(minWidth: 60), suffixIcon: suffixIcon, diff --git a/lib/app/shared/widget/button/my_elevated_button.dart b/lib/app/shared/widget/button/my_elevated_button.dart index d561973c9..129c136d1 100644 --- a/lib/app/shared/widget/button/my_elevated_button.dart +++ b/lib/app/shared/widget/button/my_elevated_button.dart @@ -1,4 +1,3 @@ -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; @@ -10,7 +9,7 @@ class MyElevatedButton extends StatelessWidget { this.icon, this.backgroundColor, this.textColor, - this.borderRadius = 18, + this.borderRadius = 8, this.verticalSpacing = 15, this.elevation = 2, this.fontSize = 18, @@ -61,7 +60,7 @@ class MyElevatedButton extends StatelessWidget { ), ) : ElevatedButton.icon( - icon: icon!, + icon: icon, style: elevatedStyleFrom( borderRadius: borderRadius, context: context, @@ -90,16 +89,16 @@ ButtonStyle elevatedStyleFrom({ GestureTapCallback? onPressed, }) { return ButtonStyle( - elevation: MaterialStateProperty.all(elevation), - padding: MaterialStateProperty.all( + elevation: WidgetStateProperty.all(elevation), + padding: WidgetStateProperty.all( EdgeInsets.symmetric(vertical: verticalSpacing), ), - backgroundColor: MaterialStateProperty.all( + backgroundColor: WidgetStateProperty.all( onPressed == null - ? Theme.of(context).colorScheme.disabledBgColor + ? Theme.of(context).colorScheme.onSurface.withOpacity(0.12) : backgroundColor ?? Theme.of(context).colorScheme.primary, ), - shape: MaterialStateProperty.all( + shape: WidgetStateProperty.all( RoundedRectangleBorder( borderRadius: BorderRadius.circular(borderRadius), ), @@ -124,7 +123,7 @@ class ElevatedButtonText extends StatelessWidget { return Text( text.toUpperCase(), style: GoogleFonts.poppins( - color: textColor ?? Theme.of(context).colorScheme.onElevatedButton, + color: textColor ?? Theme.of(context).colorScheme.onPrimary, fontSize: fontSize, fontWeight: FontWeight.w700, ), diff --git a/lib/app/shared/widget/button/my_gradient_button.dart b/lib/app/shared/widget/button/my_gradient_button.dart index e8d035e44..a5e9c0ac1 100644 --- a/lib/app/shared/widget/button/my_gradient_button.dart +++ b/lib/app/shared/widget/button/my_gradient_button.dart @@ -1,5 +1,4 @@ import 'package:altme/app/app.dart'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; @@ -8,7 +7,7 @@ class MyGradientButton extends StatelessWidget { super.key, required this.text, this.icon, - this.borderRadius = 16, + this.borderRadius = 8, this.verticalSpacing = 20, this.elevation = 2, this.fontSize = 18, @@ -47,8 +46,8 @@ class MyGradientButton extends StatelessWidget { begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ - Theme.of(context).colorScheme.startButtonColorA, - Theme.of(context).colorScheme.startButtonColorB, + Theme.of(context).colorScheme.tertiary, + Theme.of(context).colorScheme.primary, ], ); return SizedBox( @@ -58,7 +57,7 @@ class MyGradientButton extends StatelessWidget { borderRadius: BorderRadius.circular(borderRadius), gradient: onPressed == null ? null : gradientValue, color: onPressed == null - ? Theme.of(context).colorScheme.disabledBgColor + ? Theme.of(context).colorScheme.onSurface.withOpacity(0.12) : null, ), child: icon == null @@ -78,7 +77,7 @@ class MyGradientButton extends StatelessWidget { ), ) : ElevatedButton.icon( - icon: icon!, + icon: icon, style: gradientStyleFrom( elevation: elevation, verticalSpacing: verticalSpacing, @@ -107,8 +106,8 @@ ButtonStyle gradientStyleFrom({ return ElevatedButton.styleFrom( elevation: elevation, padding: EdgeInsets.symmetric(vertical: verticalSpacing), - backgroundColor: Theme.of(context).colorScheme.transparent, - shadowColor: Theme.of(context).colorScheme.transparent, + backgroundColor: Colors.transparent, + shadowColor: Colors.transparent, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(borderRadius), ), @@ -137,8 +136,8 @@ class GradientButtonText extends StatelessWidget { upperCase ? text.toUpperCase() : text, style: GoogleFonts.poppins( color: onPressed != null - ? Theme.of(context).colorScheme.onElevatedButton - : Theme.of(context).colorScheme.disabledTextColor, + ? Theme.of(context).colorScheme.onPrimary + : Theme.of(context).colorScheme.onSurface.withOpacity(0.38), fontSize: fontSize, fontWeight: FontWeight.w700, ), diff --git a/lib/app/shared/widget/button/my_outlined_button.dart b/lib/app/shared/widget/button/my_outlined_button.dart index a85dc4a39..b032903cb 100644 --- a/lib/app/shared/widget/button/my_outlined_button.dart +++ b/lib/app/shared/widget/button/my_outlined_button.dart @@ -1,4 +1,3 @@ -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; class MyOutlinedButton extends StatelessWidget { @@ -10,7 +9,7 @@ class MyOutlinedButton extends StatelessWidget { this.backgroundColor, this.textColor, this.borderColor, - this.borderRadius = 18, + this.borderRadius = 8, this.verticalSpacing = 15, this.elevation = 2, this.fontSize = 18, @@ -65,7 +64,7 @@ class MyOutlinedButton extends StatelessWidget { ), ) : OutlinedButton.icon( - icon: icon!, + icon: icon, style: outlinedStyleFrom( borderRadius: borderRadius, context: context, @@ -99,11 +98,10 @@ ButtonStyle outlinedStyleFrom({ return OutlinedButton.styleFrom( padding: EdgeInsets.symmetric(vertical: verticalSpacing), elevation: elevation, - backgroundColor: - backgroundColor ?? Theme.of(context).colorScheme.background, + backgroundColor: backgroundColor ?? Theme.of(context).colorScheme.surface, side: BorderSide( color: onPressed == null - ? Theme.of(context).colorScheme.disabledBgColor + ? Theme.of(context).colorScheme.onSurface.withOpacity(0.12) : borderColor ?? Theme.of(context).colorScheme.primary, width: 2, ), @@ -133,8 +131,8 @@ class OutlinedButtonText extends StatelessWidget { text.toUpperCase(), style: TextStyle( color: onPressed == null - ? Theme.of(context).colorScheme.disabledBgColor - : textColor ?? Theme.of(context).colorScheme.onOutlineButton, + ? Theme.of(context).colorScheme.onSurface.withOpacity(0.12) + : textColor ?? Theme.of(context).colorScheme.primary, fontSize: fontSize, fontWeight: FontWeight.w700, ), diff --git a/lib/app/shared/widget/cached_image_from_network.dart b/lib/app/shared/widget/cached_image_from_network.dart index 7c2dec106..b9fe4eac9 100644 --- a/lib/app/shared/widget/cached_image_from_network.dart +++ b/lib/app/shared/widget/cached_image_from_network.dart @@ -1,6 +1,5 @@ import 'dart:convert'; -import 'package:altme/theme/theme.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; @@ -49,7 +48,8 @@ class CachedImageFromNetwork extends StatelessWidget { placeholderBuilder: (_) => Container( width: width, height: height, - color: Theme.of(context).colorScheme.lightGrey, + color: + Theme.of(context).colorScheme.onSurface.withOpacity(0.6), ), ) : url.startsWith('http') @@ -76,19 +76,25 @@ class CachedImageFromNetwork extends StatelessWidget { ) : Container( color: bgColor ?? - Theme.of(context).colorScheme.lightGrey, + Theme.of(context) + .colorScheme + .onSurface + .withOpacity(0.6), ); }, - errorWidget: (context, error, dynamic _) => - errorMessage == null - ? ColoredBox( - color: Theme.of(context).colorScheme.lightGrey, - child: Icon( - Icons.error, - color: Theme.of(context).colorScheme.darkGrey, - ), - ) - : ErrorWidget(errorMessage: errorMessage), + errorWidget: (context, error, dynamic _) => errorMessage == + null + ? ColoredBox( + color: Theme.of(context) + .colorScheme + .onSurface + .withOpacity(0.6), + child: Icon( + Icons.error, + color: Theme.of(context).colorScheme.onSurface, + ), + ) + : ErrorWidget(errorMessage: errorMessage), ) : Image.memory( base64Decode(url), @@ -133,7 +139,7 @@ class ErrorWidget extends StatelessWidget { padding: const EdgeInsets.all(25), child: Text( errorMessage!, - style: Theme.of(context).textTheme.cacheErrorMessage, + style: Theme.of(context).textTheme.bodyMedium, textAlign: TextAlign.center, ), ), diff --git a/lib/app/shared/widget/checkbox_item.dart b/lib/app/shared/widget/checkbox_item.dart index d9cf52f36..52f263264 100644 --- a/lib/app/shared/widget/checkbox_item.dart +++ b/lib/app/shared/widget/checkbox_item.dart @@ -26,7 +26,7 @@ class CheckboxItem extends StatelessWidget { children: [ Checkbox( value: value, - fillColor: MaterialStateProperty.all( + fillColor: WidgetStateProperty.all( Theme.of(context).colorScheme.primary, ), shape: const RoundedRectangleBorder( diff --git a/lib/app/shared/widget/copy_button.dart b/lib/app/shared/widget/copy_button.dart index 384a9f855..a353a76cc 100644 --- a/lib/app/shared/widget/copy_button.dart +++ b/lib/app/shared/widget/copy_button.dart @@ -1,6 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class CopyButton extends StatelessWidget { @@ -27,7 +27,7 @@ class CopyButton extends StatelessWidget { ), Text( l10n.copy, - style: Theme.of(context).textTheme.title, + style: Theme.of(context).textTheme.headlineSmall, ), ], ), diff --git a/lib/app/shared/widget/custom_app_bar.dart b/lib/app/shared/widget/custom_app_bar.dart index bd444804f..d402238bc 100644 --- a/lib/app/shared/widget/custom_app_bar.dart +++ b/lib/app/shared/widget/custom_app_bar.dart @@ -1,5 +1,5 @@ import 'package:altme/app/app.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class CustomAppBar extends PreferredSize { @@ -23,7 +23,7 @@ class CustomAppBar extends PreferredSize { @override Widget build(BuildContext context) => ColoredBox( - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, child: SafeArea( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 12), @@ -50,7 +50,7 @@ class CustomAppBar extends PreferredSize { title ?? '', maxLines: 2, textAlign: TextAlign.center, - style: Theme.of(context).textTheme.appBar, + style: Theme.of(context).textTheme.headlineMedium, ), ), ], diff --git a/lib/app/shared/widget/custom_listtile_card.dart b/lib/app/shared/widget/custom_listtile_card.dart index 111930abb..f6fa29cb3 100644 --- a/lib/app/shared/widget/custom_listtile_card.dart +++ b/lib/app/shared/widget/custom_listtile_card.dart @@ -1,5 +1,5 @@ import 'package:altme/app/app.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class CustomListTileCard extends StatelessWidget { @@ -34,7 +34,7 @@ class CustomListTileCard extends StatelessWidget { Expanded( child: MyText( title, - style: Theme.of(context).textTheme.customListTileTitleStyle, + style: Theme.of(context).textTheme.titleMedium, ), ), const SizedBox(width: 10), @@ -50,7 +50,7 @@ class CustomListTileCard extends StatelessWidget { ), subtitle: Text( subTitle, - style: Theme.of(context).textTheme.customListTileSubTitleStyle, + style: Theme.of(context).textTheme.labelMedium, ), minVerticalPadding: 0, trailing: Image.asset( diff --git a/lib/app/shared/widget/default_dialog.dart b/lib/app/shared/widget/default_dialog.dart index f60e08e43..cb27f8a65 100644 --- a/lib/app/shared/widget/default_dialog.dart +++ b/lib/app/shared/widget/default_dialog.dart @@ -1,5 +1,5 @@ import 'package:altme/app/app.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class DefaultDialog extends StatelessWidget { @@ -19,8 +19,8 @@ class DefaultDialog extends StatelessWidget { @override Widget build(BuildContext context) { return AlertDialog( - backgroundColor: Theme.of(context).colorScheme.popupBackground, - surfaceTintColor: Colors.transparent, + backgroundColor: Theme.of(context).colorScheme.surface, + surfaceTintColor: Colors.transparent, contentPadding: const EdgeInsets.symmetric( horizontal: Sizes.spaceNormal, vertical: Sizes.spaceSmall, @@ -36,13 +36,13 @@ class DefaultDialog extends StatelessWidget { const SizedBox(height: Sizes.spaceSmall), Text( title, - style: Theme.of(context).textTheme.defaultDialogTitle, + style: Theme.of(context).textTheme.headlineMedium, textAlign: TextAlign.center, ), const SizedBox(height: Sizes.spaceSmall), Text( description, - style: Theme.of(context).textTheme.defaultDialogBody, + style: Theme.of(context).textTheme.bodyMedium, textAlign: TextAlign.center, ), const SizedBox(height: Sizes.spaceSmall), diff --git a/lib/app/shared/widget/dialog/becareful_dialog.dart b/lib/app/shared/widget/dialog/becareful_dialog.dart index d4644e895..7dc3b83dd 100644 --- a/lib/app/shared/widget/dialog/becareful_dialog.dart +++ b/lib/app/shared/widget/dialog/becareful_dialog.dart @@ -1,6 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class BeCarefulDialog extends StatelessWidget { @@ -43,7 +43,7 @@ class BeCarefulDialog extends StatelessWidget { Widget build(BuildContext context) { final l10n = context.l10n; return AlertDialog( - backgroundColor: Theme.of(context).colorScheme.popupBackground, + backgroundColor: Theme.of(context).colorScheme.surface, surfaceTintColor: Colors.transparent, contentPadding: const EdgeInsets.symmetric( horizontal: Sizes.spaceNormal, @@ -57,7 +57,7 @@ class BeCarefulDialog extends StatelessWidget { children: [ DialogCloseButton( showText: false, - color: Theme.of(context).colorScheme.defaultDialogDark, + color: Theme.of(context).colorScheme.surface, ), Image.asset( IconStrings.alert, @@ -65,19 +65,19 @@ class BeCarefulDialog extends StatelessWidget { ), Text( l10n.beCareful, - style: Theme.of(context).textTheme.bodySmall2, + style: Theme.of(context).textTheme.bodyMedium, ), const SizedBox(height: Sizes.spaceSmall), Text( title, - style: Theme.of(context).textTheme.defaultDialogTitle, + style: Theme.of(context).textTheme.headlineMedium, textAlign: TextAlign.center, ), if (subtitle != null) ...[ const SizedBox(height: Sizes.spaceSmall), Text( subtitle!, - style: Theme.of(context).textTheme.defaultDialogBody, + style: Theme.of(context).textTheme.bodyMedium, textAlign: TextAlign.center, ), ], @@ -94,12 +94,9 @@ class BeCarefulDialog extends StatelessWidget { fontSize: 15, borderColor: Theme.of(context) .colorScheme - .defualtDialogCancelButtonBorderColor, + .onSurface + .withOpacity(0.2), borderRadius: 12, - backgroundColor: Colors.white, - textColor: Theme.of(context) - .colorScheme - .defualtDialogCancelButtonTextColor, onPressed: () { Navigator.of(context).pop(); }, diff --git a/lib/app/shared/widget/dialog/confirm_dialog.dart b/lib/app/shared/widget/dialog/confirm_dialog.dart index cff63737d..64a5727ab 100644 --- a/lib/app/shared/widget/dialog/confirm_dialog.dart +++ b/lib/app/shared/widget/dialog/confirm_dialog.dart @@ -1,6 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class ConfirmDialog extends StatelessWidget { @@ -30,9 +30,8 @@ class ConfirmDialog extends StatelessWidget { @override Widget build(BuildContext context) { final color = dialogColor ?? Theme.of(context).colorScheme.primary; - final background = bgColor ?? Theme.of(context).colorScheme.popupBackground; - final textColor = - this.textColor ?? Theme.of(context).colorScheme.dialogText; + final background = bgColor ?? Theme.of(context).colorScheme.surfaceDim; + final textColor = this.textColor ?? Theme.of(context).colorScheme.onSurface; final l10n = context.l10n; return AlertDialog( @@ -49,7 +48,7 @@ class ConfirmDialog extends StatelessWidget { icon, width: 50, height: 50, - color: textColor, + color: icon == IconStrings.cardReceive ? textColor : null, fit: BoxFit.fill, ), const SizedBox(height: 10), @@ -57,7 +56,7 @@ class ConfirmDialog extends StatelessWidget { title, style: Theme.of(context) .textTheme - .defaultDialogTitle + .headlineMedium! .copyWith(color: textColor), textAlign: TextAlign.center, ), @@ -67,7 +66,7 @@ class ConfirmDialog extends StatelessWidget { subtitle!, style: Theme.of(context) .textTheme - .defaultDialogSubtitle + .titleLarge! .copyWith(color: textColor), textAlign: TextAlign.center, ), @@ -84,13 +83,12 @@ class ConfirmDialog extends StatelessWidget { fontSize: 15, borderColor: Theme.of(context) .colorScheme - .defualtDialogCancelButtonBorderColor, + .onSurface + .withOpacity(0.2), backgroundColor: background, borderRadius: Sizes.smallRadius, elevation: 0, - textColor: Theme.of(context) - .colorScheme - .defualtDialogCancelButtonTextColor, + textColor: textColor, onPressed: () { Navigator.of(context).pop(false); }, diff --git a/lib/app/shared/widget/dialog/error_details_dialog.dart b/lib/app/shared/widget/dialog/error_details_dialog.dart index 3609545bb..e165777c4 100644 --- a/lib/app/shared/widget/dialog/error_details_dialog.dart +++ b/lib/app/shared/widget/dialog/error_details_dialog.dart @@ -1,6 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -23,9 +23,8 @@ class ErrorDetailsDialog extends StatelessWidget { @override Widget build(BuildContext context) { final color = dialogColor ?? Theme.of(context).colorScheme.primary; - final background = bgColor ?? Theme.of(context).colorScheme.popupBackground; - final textColor = - this.textColor ?? Theme.of(context).colorScheme.dialogText; + final background = bgColor ?? Theme.of(context).colorScheme.surface; + final textColor = this.textColor ?? Theme.of(context).colorScheme.onSurface; final l10n = context.l10n; return AlertDialog( @@ -45,7 +44,7 @@ class ErrorDetailsDialog extends StatelessWidget { erroDescription!, style: Theme.of(context) .textTheme - .defaultDialogSubtitle + .titleLarge! .copyWith(color: textColor), textAlign: TextAlign.center, ), @@ -62,10 +61,9 @@ class ErrorDetailsDialog extends StatelessWidget { child: Text( l10n.moreDetails, style: Theme.of(context).textTheme.bodyMedium!.copyWith( - color: Theme.of(context).colorScheme.markDownA, + color: Theme.of(context).colorScheme.primary, decoration: TextDecoration.underline, - decorationColor: - Theme.of(context).colorScheme.markDownA, + decorationColor: Theme.of(context).colorScheme.primary, ), textAlign: TextAlign.center, ), diff --git a/lib/app/shared/widget/dialog/error_dialog.dart b/lib/app/shared/widget/dialog/error_dialog.dart index 2c7639713..0c239a396 100644 --- a/lib/app/shared/widget/dialog/error_dialog.dart +++ b/lib/app/shared/widget/dialog/error_dialog.dart @@ -1,6 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class ErrorDialog extends StatelessWidget { @@ -24,9 +24,9 @@ class ErrorDialog extends StatelessWidget { @override Widget build(BuildContext context) { final color = dialogColor ?? Theme.of(context).colorScheme.primary; - final background = bgColor ?? Theme.of(context).colorScheme.popupBackground; - final textColor = - this.textColor ?? Theme.of(context).colorScheme.dialogText; + final background = + bgColor ?? Theme.of(context).colorScheme.primaryContainer; + final textColor = this.textColor ?? Theme.of(context).colorScheme.onSurface; final l10n = context.l10n; return AlertDialog( @@ -49,7 +49,7 @@ class ErrorDialog extends StatelessWidget { title, style: Theme.of(context) .textTheme - .defaultDialogTitle + .headlineMedium! .copyWith(color: textColor), textAlign: TextAlign.center, ), @@ -59,9 +59,8 @@ class ErrorDialog extends StatelessWidget { text: l10n.moreDetails, verticalSpacing: 14, fontSize: 15, - borderColor: Theme.of(context) - .colorScheme - .defualtDialogCancelButtonBorderColor, + borderColor: + Theme.of(context).colorScheme.onSurface.withOpacity(0.2), backgroundColor: background, textColor: textColor, borderRadius: Sizes.smallRadius, diff --git a/lib/app/shared/widget/dialog/text_field_dialog.dart b/lib/app/shared/widget/dialog/text_field_dialog.dart index 9d7699bbb..c715f9e87 100644 --- a/lib/app/shared/widget/dialog/text_field_dialog.dart +++ b/lib/app/shared/widget/dialog/text_field_dialog.dart @@ -1,6 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class TextFieldDialog extends StatefulWidget { @@ -53,9 +53,8 @@ class _TextFieldDialogState extends State { final no = widget.no ?? l10n.no; final color = widget.dialogColor ?? Theme.of(context).colorScheme.primary; - final background = - widget.bgColor ?? Theme.of(context).colorScheme.popupBackground; - final text = widget.textColor ?? Theme.of(context).colorScheme.label; + final background = widget.bgColor ?? Theme.of(context).colorScheme.surface; + final text = widget.textColor ?? Theme.of(context).colorScheme.onSurface; return AlertDialog( backgroundColor: background, @@ -75,17 +74,15 @@ class _TextFieldDialogState extends State { widget.title, style: Theme.of(context) .textTheme - .defaultDialogTitle + .headlineMedium! .copyWith(color: text), textAlign: TextAlign.center, ), if (widget.subtitle != null) Text( widget.subtitle!, - style: Theme.of(context) - .textTheme - .defaultDialogSubtitle - .copyWith(color: text), + style: + Theme.of(context).textTheme.titleLarge!.copyWith(color: text), textAlign: TextAlign.center, ), const SizedBox(height: 24), @@ -108,7 +105,7 @@ class _TextFieldDialogState extends State { elevation: 10, borderRadius: Sizes.smallRadius, backgroundColor: color, - textColor: Theme.of(context).colorScheme.label, + textColor: Theme.of(context).colorScheme.onSurface, fontSize: 15, onPressed: () { Navigator.of(context).pop(controller.text); @@ -121,13 +118,10 @@ class _TextFieldDialogState extends State { fontSize: 12, elevation: 0, borderRadius: Sizes.smallRadius, - borderColor: Theme.of(context) - .colorScheme - .defualtDialogCancelButtonBorderColor, + borderColor: + Theme.of(context).colorScheme.onSurface.withOpacity(0.2), backgroundColor: background, - textColor: Theme.of(context) - .colorScheme - .defualtDialogCancelButtonTextColor, + textColor: Theme.of(context).colorScheme.onSurface, onPressed: () { Navigator.of(context).pop(''); }, diff --git a/lib/app/shared/widget/dialog_close_button.dart b/lib/app/shared/widget/dialog_close_button.dart index e2c192df6..849b37393 100644 --- a/lib/app/shared/widget/dialog_close_button.dart +++ b/lib/app/shared/widget/dialog_close_button.dart @@ -1,6 +1,5 @@ import 'package:altme/app/app.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/app_theme/app_theme.dart'; import 'package:flutter/material.dart'; class DialogCloseButton extends StatelessWidget { @@ -22,7 +21,7 @@ class DialogCloseButton extends StatelessWidget { if (showText) Text( l10n.close, - style: Theme.of(context).textTheme.dialogClose.copyWith( + style: Theme.of(context).textTheme.bodyMedium!.copyWith( color: color, ), ), @@ -30,7 +29,8 @@ class DialogCloseButton extends StatelessWidget { Container( padding: const EdgeInsets.all(0.5), decoration: BoxDecoration( - color: color ?? Theme.of(context).colorScheme.closeIconColor, + color: color ?? + Theme.of(context).colorScheme.onSurface.withOpacity(0.6), shape: BoxShape.circle, ), child: const Icon( diff --git a/lib/app/shared/widget/display_terms.dart b/lib/app/shared/widget/display_terms.dart index 8cb53e93b..6bab855c4 100644 --- a/lib/app/shared/widget/display_terms.dart +++ b/lib/app/shared/widget/display_terms.dart @@ -1,6 +1,5 @@ import 'package:altme/app/app.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -154,11 +153,12 @@ class MarkdownBody extends StatelessWidget { data: data, styleSheet: MarkdownStyleSheet( h1: TextStyle( - color: Theme.of(context).colorScheme.markDownH1, + color: Theme.of(context).colorScheme.onSurface, ), - h2: TextStyle(color: Theme.of(context).colorScheme.markDownH2), - a: TextStyle(color: Theme.of(context).colorScheme.markDownA), - p: TextStyle(color: Theme.of(context).colorScheme.markDownP), + h2: TextStyle(color: Theme.of(context).colorScheme.onSurface), + a: TextStyle(color: Theme.of(context).colorScheme.primary), + p: TextStyle( + color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6),), //onTapLink: (text, href, title) => _onTapLink(href), ), diff --git a/lib/app/shared/widget/error_view.dart b/lib/app/shared/widget/error_view.dart index e4f227cae..0c302126f 100644 --- a/lib/app/shared/widget/error_view.dart +++ b/lib/app/shared/widget/error_view.dart @@ -1,6 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class ErrorView extends StatelessWidget { @@ -21,7 +21,7 @@ class ErrorView extends StatelessWidget { Center( child: Text( message, - style: Theme.of(context).textTheme.errorMessage, + style: Theme.of(context).textTheme.bodyMedium, ), ), const SizedBox(height: Sizes.spaceSmall), @@ -29,7 +29,7 @@ class ErrorView extends StatelessWidget { width: MediaQuery.of(context).size.width / 2.5, child: MyOutlinedButton( text: l10n.tryAgain, - backgroundColor: Theme.of(context).colorScheme.transparent, + backgroundColor: Colors.transparent, onPressed: onTap, fontSize: 14, verticalSpacing: 10, diff --git a/lib/app/shared/widget/grouped_section.dart b/lib/app/shared/widget/grouped_section.dart index 1b0adb90a..393e097aa 100644 --- a/lib/app/shared/widget/grouped_section.dart +++ b/lib/app/shared/widget/grouped_section.dart @@ -1,5 +1,4 @@ import 'package:altme/app/app.dart'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; class GroupedSection extends StatelessWidget { @@ -16,7 +15,7 @@ class GroupedSection extends StatelessWidget { padding: const EdgeInsets.all(Sizes.spaceSmall), margin: const EdgeInsets.all(Sizes.spaceXSmall), decoration: BoxDecoration( - color: Theme.of(context).colorScheme.drawerSurface, + color: Theme.of(context).colorScheme.surface, borderRadius: const BorderRadius.all( Radius.circular(Sizes.largeRadius), ), diff --git a/lib/app/shared/widget/image_card_text.dart b/lib/app/shared/widget/image_card_text.dart index 33fe7b766..345e3d2b1 100644 --- a/lib/app/shared/widget/image_card_text.dart +++ b/lib/app/shared/widget/image_card_text.dart @@ -1,4 +1,3 @@ -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; /// This widget is used to adapt text size on image card when user change @@ -15,7 +14,7 @@ class ImageCardText extends StatelessWidget { @override Widget build(BuildContext context) { - final textStyle = this.textStyle ?? Theme.of(context).textTheme.imageCard; + final textStyle = this.textStyle ?? Theme.of(context).textTheme.bodyMedium!; return Text( text, style: MediaQuery.of(context).orientation == Orientation.landscape diff --git a/lib/app/shared/widget/imported_tag.dart b/lib/app/shared/widget/imported_tag.dart index ab12b0cc2..2e4dd7349 100644 --- a/lib/app/shared/widget/imported_tag.dart +++ b/lib/app/shared/widget/imported_tag.dart @@ -1,6 +1,5 @@ import 'package:altme/app/app.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; class ImportedTag extends StatelessWidget { @@ -14,7 +13,7 @@ class ImportedTag extends StatelessWidget { decoration: BoxDecoration( color: Theme.of(context).highlightColor, border: Border.all( - color: Theme.of(context).colorScheme.borderColor, + color: Theme.of(context).colorScheme.onSurface.withOpacity(0.12), width: 0.35, ), borderRadius: diff --git a/lib/app/shared/widget/numeric_keyboard.dart b/lib/app/shared/widget/numeric_keyboard.dart index f709aa60e..28b9fe396 100644 --- a/lib/app/shared/widget/numeric_keyboard.dart +++ b/lib/app/shared/widget/numeric_keyboard.dart @@ -1,6 +1,5 @@ import 'dart:math'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -161,10 +160,10 @@ class KeyboardButton extends StatelessWidget { child: InkWell( highlightColor: allowAction ? Theme.of(context).colorScheme.primary - : Theme.of(context).colorScheme.background, + : Theme.of(context).colorScheme.surface, splashColor: allowAction ? Theme.of(context).colorScheme.primary - : Theme.of(context).colorScheme.background, + : Theme.of(context).colorScheme.surface, onLongPress: () { if (!allowAction) return; onLongPress?.call(semanticsLabel); @@ -180,12 +179,10 @@ class KeyboardButton extends StatelessWidget { border: digitBorderWidth > 0.0 ? Border.all( color: allowAction - ? Theme.of(context) - .colorScheme - .digitPrimaryColor + ? Theme.of(context).colorScheme.onSurface : Theme.of(context) .colorScheme - .digitPrimaryColor + .onSurface .withOpacity(0.1), width: digitBorderWidth, ) @@ -193,9 +190,9 @@ class KeyboardButton extends StatelessWidget { ), child: Container( alignment: Alignment.center, - decoration: BoxDecoration( + decoration: const BoxDecoration( shape: BoxShape.circle, - color: Theme.of(context).colorScheme.digitFillColor, + color: Colors.transparent, ), child: label != null ? Text( @@ -206,20 +203,20 @@ class KeyboardButton extends StatelessWidget { : digitTextStyle!.copyWith( color: Theme.of(context) .colorScheme - .digitPrimaryColor + .onSurface .withOpacity(0.1), ) : allowAction ? Theme.of(context) .textTheme - .keyboardDigitTextStyle + .headlineMedium : Theme.of(context) .textTheme - .keyboardDigitTextStyle + .headlineMedium! .copyWith( color: Theme.of(context) .colorScheme - .digitPrimaryColor + .onSurface .withOpacity(0.1), ), semanticsLabel: semanticsLabel, @@ -244,26 +241,22 @@ class KeyboardButton extends StatelessWidget { color: Colors.transparent, border: digitBorderWidth > 0.0 ? Border.all( - color: Theme.of(context) - .colorScheme - .digitPrimaryColor, + color: Theme.of(context).colorScheme.onSurface, width: digitBorderWidth, ) : null, ), child: Container( alignment: Alignment.center, - decoration: BoxDecoration( + decoration: const BoxDecoration( shape: BoxShape.circle, - color: Theme.of(context).colorScheme.digitFillColor, + color: Colors.transparent, ), child: label != null ? Text( label!, style: digitTextStyle ?? - Theme.of(context) - .textTheme - .keyboardDigitTextStyle, + Theme.of(context).textTheme.headlineMedium, semanticsLabel: semanticsLabel, ) : icon, diff --git a/lib/app/shared/widget/phrase_word.dart b/lib/app/shared/widget/phrase_word.dart index efc373003..4780b9d97 100644 --- a/lib/app/shared/widget/phrase_word.dart +++ b/lib/app/shared/widget/phrase_word.dart @@ -1,5 +1,5 @@ import 'package:altme/app/app.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class PhraseWord extends StatelessWidget { @@ -29,7 +29,7 @@ class PhraseWord extends StatelessWidget { vertical: Sizes.spaceSmall, ), decoration: BoxDecoration( - color: Theme.of(context).colorScheme.transparent, + color: Colors.transparent, border: Border.all( width: 1.5, color: color, @@ -42,7 +42,6 @@ class PhraseWord extends StatelessWidget { child: MyText( showOrder ? '$order. $word' : word, textAlign: TextAlign.center, - style: Theme.of(context).textTheme.passPhraseText, ), ), ), diff --git a/lib/app/shared/widget/share_button.dart b/lib/app/shared/widget/share_button.dart index d9babbffd..7bcfe1b7a 100644 --- a/lib/app/shared/widget/share_button.dart +++ b/lib/app/shared/widget/share_button.dart @@ -1,6 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class ShareButton extends StatelessWidget { @@ -27,7 +27,7 @@ class ShareButton extends StatelessWidget { ), Text( l10n.share, - style: Theme.of(context).textTheme.title, + style: Theme.of(context).textTheme.headlineSmall, ), ], ), diff --git a/lib/app/view/app.dart b/lib/app/view/app.dart index 86a6c1748..01ea7cf21 100644 --- a/lib/app/view/app.dart +++ b/lib/app/view/app.dart @@ -23,10 +23,10 @@ import 'package:altme/query_by_example/query_by_example.dart'; import 'package:altme/route/route.dart'; import 'package:altme/scan/scan.dart'; import 'package:altme/splash/splash.dart'; -import 'package:altme/theme/theme.dart'; +import 'package:altme/theme/app_theme/app_theme.dart'; + import 'package:altme/wallet/wallet.dart'; import 'package:beacon_flutter/beacon_flutter.dart'; -import 'package:device_preview/device_preview.dart'; import 'package:did_kit/did_kit.dart'; import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; @@ -281,23 +281,14 @@ class MaterialAppDefinition extends StatelessWidget { @override Widget build(BuildContext context) { - final bool isStaging = - context.read().flavorMode == FlavorMode.staging; return BlocBuilder( builder: (context, state) { - if (isStaging) { - final locale = DevicePreview.locale(context); - if (locale != null) { - context.read().setLocale(locale); - } - } if (state.locale == const Locale('en')) { context.read().checkLocale(); } return MaterialApp( - builder: isStaging ? DevicePreview.appBuilder : null, - locale: isStaging ? DevicePreview.locale(context) : state.locale, + locale: state.locale, title: 'Talao Wallet', darkTheme: AppTheme.darkThemeData, navigatorObservers: [MyRouteObserver(context)], diff --git a/lib/bootstrap.dart b/lib/bootstrap.dart index 7298438c6..e77bae269 100644 --- a/lib/bootstrap.dart +++ b/lib/bootstrap.dart @@ -8,7 +8,6 @@ import 'dart:async'; import 'dart:developer'; -import 'package:altme/app/app.dart'; import 'package:bloc/bloc.dart'; import 'package:dartez/dartez.dart'; import 'package:flutter/widgets.dart'; @@ -37,7 +36,6 @@ Future bootstrap(FutureOr Function() builder) async { () async { WidgetsFlutterBinding.ensureInitialized(); - await LocalNotification().init(); await initSecureStorage; /// Disable Http google font diff --git a/lib/chat_room/view/chat_room_view.dart b/lib/chat_room/view/chat_room_view.dart index 7215c4d9e..34eba381c 100644 --- a/lib/chat_room/view/chat_room_view.dart +++ b/lib/chat_room/view/chat_room_view.dart @@ -2,7 +2,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/chat_room/chat_room.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_chat_types/flutter_chat_types.dart'; @@ -161,7 +161,14 @@ class _ChatRoomViewState extends State { l10n.e2eEncyptedChat, maxLines: 1, minFontSize: 8, - style: Theme.of(context).textTheme.subtitle4, + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith( + color: Theme.of(context) + .colorScheme + .tertiary, + ), ), ), ], diff --git a/lib/connection_bridge/beacon/cubit/beacon_cubit.dart b/lib/connection_bridge/beacon/cubit/beacon_cubit.dart index 19c25f6d7..18c371832 100644 --- a/lib/connection_bridge/beacon/cubit/beacon_cubit.dart +++ b/lib/connection_bridge/beacon/cubit/beacon_cubit.dart @@ -25,7 +25,7 @@ class BeaconCubit extends Cubit { } Future peerFromDeepLink(String beaconData) async { - final isInternetAvailable = await isConnected(); + final isInternetAvailable = await isConnectedToInternet(); if (isInternetAvailable) { await beacon.pair(pairingRequest: beaconData); } diff --git a/lib/dashboard/add_account/choose_method/view/choose_add_account_method_page.dart b/lib/dashboard/add_account/choose_method/view/choose_add_account_method_page.dart index 35a00f72a..be62ca398 100644 --- a/lib/dashboard/add_account/choose_method/view/choose_add_account_method_page.dart +++ b/lib/dashboard/add_account/choose_method/view/choose_add_account_method_page.dart @@ -3,7 +3,7 @@ import 'package:altme/app/shared/constants/sizes.dart'; import 'package:altme/app/shared/widget/widget.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class ChooseAddAccountMethodPage extends StatelessWidget { @@ -38,7 +38,7 @@ class ChooseAddAccountMethodView extends StatelessWidget { Text( l10n.selectAMethodToAddAccount, textAlign: TextAlign.center, - style: Theme.of(context).textTheme.bodySmall3, + style: Theme.of(context).textTheme.bodyMedium, ), const SizedBox(height: Sizes.spaceXLarge), CustomListTileCard( diff --git a/lib/dashboard/add_account/create_account/view/create_account_step1_page.dart b/lib/dashboard/add_account/create_account/view/create_account_step1_page.dart index f727041de..18f0ff967 100644 --- a/lib/dashboard/add_account/create_account/view/create_account_step1_page.dart +++ b/lib/dashboard/add_account/create_account/view/create_account_step1_page.dart @@ -4,7 +4,7 @@ import 'package:altme/app/shared/widget/widget.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; import 'package:altme/onboarding/onboarding.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; import 'package:key_generator/key_generator.dart'; @@ -48,7 +48,7 @@ class CreateAccountStep1View extends StatelessWidget { Text( l10n.chooseABlockchainForAccountCreation, textAlign: TextAlign.center, - style: Theme.of(context).textTheme.bodySmall3, + style: Theme.of(context).textTheme.bodyMedium, ), const SizedBox(height: Sizes.spaceNormal), CustomListTileCard( diff --git a/lib/dashboard/add_account/create_account/view/create_account_step2_page.dart b/lib/dashboard/add_account/create_account/view/create_account_step2_page.dart index 8a80f7be1..89f541f71 100644 --- a/lib/dashboard/add_account/create_account/view/create_account_step2_page.dart +++ b/lib/dashboard/add_account/create_account/view/create_account_step2_page.dart @@ -2,7 +2,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; import 'package:altme/onboarding/widgets/m_stepper.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:altme/wallet/wallet.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -115,7 +115,7 @@ class _CreateAccountStep2ViewState extends State { Text( l10n.setAccountNameDescription, textAlign: TextAlign.center, - style: Theme.of(context).textTheme.bodySmall3, + style: Theme.of(context).textTheme.bodyMedium, ), const SizedBox( height: Sizes.spaceNormal, diff --git a/lib/dashboard/add_account/import_account/view/import_account_step1_page.dart b/lib/dashboard/add_account/import_account/view/import_account_step1_page.dart index 06dd937ed..6d5a12db9 100644 --- a/lib/dashboard/add_account/import_account/view/import_account_step1_page.dart +++ b/lib/dashboard/add_account/import_account/view/import_account_step1_page.dart @@ -4,7 +4,7 @@ import 'package:altme/app/shared/widget/widget.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; import 'package:altme/onboarding/onboarding.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; import 'package:key_generator/key_generator.dart'; @@ -48,7 +48,7 @@ class ImportAccountStep1View extends StatelessWidget { Text( l10n.importEasilyFrom, textAlign: TextAlign.center, - style: Theme.of(context).textTheme.bodySmall3, + style: Theme.of(context).textTheme.bodyMedium, ), const SizedBox(height: Sizes.spaceNormal), CustomListTileCard( diff --git a/lib/dashboard/add_account/import_account/view/import_account_step2_page.dart b/lib/dashboard/add_account/import_account/view/import_account_step2_page.dart index 86e3dd977..d2e85dcc9 100644 --- a/lib/dashboard/add_account/import_account/view/import_account_step2_page.dart +++ b/lib/dashboard/add_account/import_account/view/import_account_step2_page.dart @@ -4,7 +4,7 @@ import 'package:altme/app/shared/widget/widget.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; import 'package:altme/onboarding/onboarding.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; import 'package:key_generator/key_generator.dart'; @@ -57,7 +57,7 @@ class ImportAccountStep2View extends StatelessWidget { Text( l10n.importEasilyFrom, textAlign: TextAlign.center, - style: Theme.of(context).textTheme.bodySmall3, + style: Theme.of(context).textTheme.bodyMedium, ), const SizedBox( height: Sizes.spaceNormal, diff --git a/lib/dashboard/add_account/import_account/view/import_account_step3_page.dart b/lib/dashboard/add_account/import_account/view/import_account_step3_page.dart index 7935fd66a..6554bf2dd 100644 --- a/lib/dashboard/add_account/import_account/view/import_account_step3_page.dart +++ b/lib/dashboard/add_account/import_account/view/import_account_step3_page.dart @@ -2,7 +2,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; import 'package:altme/onboarding/onboarding.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:altme/wallet/cubit/wallet_cubit.dart'; import 'package:did_kit/did_kit.dart'; import 'package:flutter/material.dart'; @@ -111,7 +111,7 @@ class _ImportAccountStep3ViewState extends State { state.accountType == AccountType.tezos ? 54 : 64, ), fillColor: Colors.transparent, - hintStyle: Theme.of(context).textTheme.hintTextFieldStyle, + hintStyle: Theme.of(context).textTheme.bodyMedium, maxLines: 10, borderRadius: Sizes.normalRadius, controller: mnemonicController, @@ -129,7 +129,7 @@ class _ImportAccountStep3ViewState extends State { margin: const EdgeInsets.all(Sizes.spaceNormal), decoration: BoxDecoration( shape: BoxShape.circle, - color: Theme.of(context).colorScheme.checkMarkColor, + color: Theme.of(context).colorScheme.onTertiary, ), child: const Icon( Icons.check, @@ -144,14 +144,14 @@ class _ImportAccountStep3ViewState extends State { const SizedBox(height: Sizes.spaceSmall), Text( l10n.recoveryPhraseDescriptions, - style: Theme.of(context).textTheme.infoSubtitle.copyWith( + style: Theme.of(context).textTheme.titleMedium!.copyWith( fontSize: 12, ), ), const SizedBox(height: Sizes.spaceLarge), Text( l10n.privateKeyDescriptions, - style: Theme.of(context).textTheme.infoSubtitle.copyWith( + style: Theme.of(context).textTheme.titleMedium!.copyWith( fontSize: 12, ), ), diff --git a/lib/dashboard/add_account/import_account/view/import_account_step4_page.dart b/lib/dashboard/add_account/import_account/view/import_account_step4_page.dart index c29b62b72..a9c8098ee 100644 --- a/lib/dashboard/add_account/import_account/view/import_account_step4_page.dart +++ b/lib/dashboard/add_account/import_account/view/import_account_step4_page.dart @@ -2,7 +2,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; import 'package:altme/onboarding/onboarding.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:altme/wallet/wallet.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -107,7 +107,7 @@ class _ImportAccountStep4ViewState extends State { Text( l10n.setAccountNameDescription, textAlign: TextAlign.center, - style: Theme.of(context).textTheme.bodySmall3, + style: Theme.of(context).textTheme.bodyMedium, ), const SizedBox( height: Sizes.spaceNormal, diff --git a/lib/dashboard/add_account/import_account/widgets/account_item.dart b/lib/dashboard/add_account/import_account/widgets/account_item.dart index 020176c74..2ee9b1e40 100644 --- a/lib/dashboard/add_account/import_account/widgets/account_item.dart +++ b/lib/dashboard/add_account/import_account/widgets/account_item.dart @@ -1,5 +1,5 @@ import 'package:altme/app/shared/constants/sizes.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; @@ -24,7 +24,7 @@ class AccountItem extends StatelessWidget { vertical: Sizes.spaceSmall, ), decoration: BoxDecoration( - color: Theme.of(context).colorScheme.cardHighlighted, + color: Theme.of(context).colorScheme.surface, borderRadius: const BorderRadius.all( Radius.circular( Sizes.smallRadius, @@ -39,7 +39,7 @@ class AccountItem extends StatelessWidget { children: [ Text( title, - style: Theme.of(context).textTheme.title, + style: Theme.of(context).textTheme.headlineSmall, ), Container( width: Sizes.icon3x, diff --git a/lib/dashboard/ai_age_verification/choose_verification_method/view/choose_verification_method_page.dart b/lib/dashboard/ai_age_verification/choose_verification_method/view/choose_verification_method_page.dart index 28a44daea..5e490f710 100644 --- a/lib/dashboard/ai_age_verification/choose_verification_method/view/choose_verification_method_page.dart +++ b/lib/dashboard/ai_age_verification/choose_verification_method/view/choose_verification_method_page.dart @@ -2,7 +2,7 @@ import 'package:altme/app/shared/constants/image_strings.dart'; import 'package:altme/app/shared/enum/type/type.dart'; import 'package:altme/app/shared/widget/widget.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class ChooseVerificationMethodPage extends StatelessWidget { @@ -65,7 +65,7 @@ class ChooseVerificationMethodPage extends StatelessWidget { children: [ Text( l10n.chooseMethodPageSubtitle, - style: Theme.of(context).textTheme.customListTileSubTitleStyle, + style: Theme.of(context).textTheme.labelMedium, textAlign: TextAlign.justify, ), const SizedBox(height: 20), diff --git a/lib/dashboard/ai_age_verification/verify_age/view/ai_age_result_page.dart b/lib/dashboard/ai_age_verification/verify_age/view/ai_age_result_page.dart index 82a6b7d60..3050a3b27 100644 --- a/lib/dashboard/ai_age_verification/verify_age/view/ai_age_result_page.dart +++ b/lib/dashboard/ai_age_verification/verify_age/view/ai_age_result_page.dart @@ -2,7 +2,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/kyc_verification/kyc_verification.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/app_theme/app_theme.dart'; import 'package:confetti/confetti.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -250,7 +249,7 @@ class FailureWidget extends StatelessWidget { text: l10n.decline, verticalSpacing: 16, borderRadius: Sizes.largeRadius, - backgroundColor: Theme.of(context).colorScheme.cardHighlighted, + backgroundColor: Theme.of(context).colorScheme.surface, onPressed: () { Navigator.pushAndRemoveUntil( context, diff --git a/lib/dashboard/ai_age_verification/verify_age/view/verify_age_page.dart b/lib/dashboard/ai_age_verification/verify_age/view/verify_age_page.dart index 53b148b2f..f01bc8f59 100644 --- a/lib/dashboard/ai_age_verification/verify_age/view/verify_age_page.dart +++ b/lib/dashboard/ai_age_verification/verify_age/view/verify_age_page.dart @@ -1,7 +1,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class VerifyAgePage extends StatelessWidget { @@ -59,7 +59,7 @@ class _VerifyAgeViewState extends State { child: Text( l10n.verifyYourAgeSubtitle, textAlign: TextAlign.center, - style: Theme.of(context).textTheme.subtitle3, + style: Theme.of(context).textTheme.labelMedium, ), ), const Spacer(), @@ -71,7 +71,7 @@ class _VerifyAgeViewState extends State { Text( l10n.verifyYourAgeDescription, textAlign: TextAlign.center, - style: Theme.of(context).textTheme.subtitle3, + style: Theme.of(context).textTheme.labelMedium, ), const Spacer(), MyElevatedButton( @@ -99,7 +99,7 @@ class _VerifyAgeViewState extends State { MyElevatedButton( text: l10n.decline, verticalSpacing: 16, - backgroundColor: Theme.of(context).colorScheme.cardHighlighted, + backgroundColor: Theme.of(context).colorScheme.surface, borderRadius: Sizes.largeRadius, onPressed: () { Navigator.of(context).pop(); diff --git a/lib/dashboard/connection/confirm_connection/cubit/confirm_connection_cubit.dart b/lib/dashboard/connection/confirm_connection/cubit/confirm_connection_cubit.dart index f4dfdc60f..75e4ad14a 100644 --- a/lib/dashboard/connection/confirm_connection/cubit/confirm_connection_cubit.dart +++ b/lib/dashboard/connection/confirm_connection/cubit/confirm_connection_cubit.dart @@ -37,7 +37,7 @@ class ConfirmConnectionCubit extends Cubit { try { emit(state.loading()); - final isInternetAvailable = await isConnected(); + final isInternetAvailable = await isConnectedToInternet(); if (!isInternetAvailable) { throw NetworkException( message: NetworkError.NETWORK_ERROR_NO_INTERNET_CONNECTION, diff --git a/lib/dashboard/connection/confirm_connection/widgets/permissions.dart b/lib/dashboard/connection/confirm_connection/widgets/permissions.dart index a23d26d23..8129c51c4 100644 --- a/lib/dashboard/connection/confirm_connection/widgets/permissions.dart +++ b/lib/dashboard/connection/confirm_connection/widgets/permissions.dart @@ -1,6 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class Permissions extends StatelessWidget { @@ -12,7 +12,7 @@ class Permissions extends StatelessWidget { return Container( decoration: BoxDecoration( border: Border.all( - color: Theme.of(context).colorScheme.beaconBorder, + color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6), width: 1.5, ), borderRadius: BorderRadius.circular(15), @@ -23,7 +23,7 @@ class Permissions extends StatelessWidget { children: [ Text( l10n.requestPersmissionTo, - style: Theme.of(context).textTheme.beaconRequestPermission, + style: Theme.of(context).textTheme.bodyMedium, ), const SizedBox(height: 5), PermissionText(text: l10n.viewAccountBalanceAndNFTs), @@ -54,7 +54,7 @@ class PermissionText extends StatelessWidget { Expanded( child: MyText( text, - style: Theme.of(context).textTheme.beaconPermissions, + style: Theme.of(context).textTheme.bodyMedium, ), ), ], diff --git a/lib/dashboard/connection/confirm_connection/widgets/select_account.dart b/lib/dashboard/connection/confirm_connection/widgets/select_account.dart index f34377b9b..2052dba1a 100644 --- a/lib/dashboard/connection/confirm_connection/widgets/select_account.dart +++ b/lib/dashboard/connection/confirm_connection/widgets/select_account.dart @@ -1,7 +1,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:altme/wallet/wallet.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -22,7 +22,7 @@ class SelectAccount extends StatelessWidget { children: [ Text( l10n.selectAccountToGrantAccess, - style: Theme.of(context).textTheme.beaconSelectAccont, + style: Theme.of(context).textTheme.headlineSmall, ), BlocBuilder( builder: (context, walletState) { @@ -62,7 +62,8 @@ class SelectAccount extends StatelessWidget { ), child: Divider( height: 0.2, - color: Theme.of(context).colorScheme.borderColor, + color: + Theme.of(context).colorScheme.onSurface.withOpacity(0.12), ), ), ); diff --git a/lib/dashboard/connection/connected_dapps/view/connected_dapps_page.dart b/lib/dashboard/connection/connected_dapps/view/connected_dapps_page.dart index 0c947af51..c88c60392 100644 --- a/lib/dashboard/connection/connected_dapps/view/connected_dapps_page.dart +++ b/lib/dashboard/connection/connected_dapps/view/connected_dapps_page.dart @@ -2,7 +2,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/connection_bridge/connection_bridge.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:beacon_flutter/beacon_flutter.dart'; import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; @@ -127,8 +127,7 @@ class _ConnectedDappsViewState extends State { const SizedBox(height: Sizes.spaceXSmall), MyText( widget.walletAddress, - style: - Theme.of(context).textTheme.beaconWalletAddress, + style: Theme.of(context).textTheme.bodyMedium, ), const SizedBox(height: Sizes.spaceXLarge), Text( @@ -166,7 +165,7 @@ class _ConnectedDappsViewState extends State { .peer.metadata.name, style: Theme.of(context) .textTheme - .dappName, + .bodyMedium, ), ), Icon( @@ -192,8 +191,10 @@ class _ConnectedDappsViewState extends State { }, separatorBuilder: (_, __) => Divider( height: 0.1, - color: - Theme.of(context).colorScheme.borderColor, + color: Theme.of(context) + .colorScheme + .onSurface + .withOpacity(0.12), ), ), ], diff --git a/lib/dashboard/connection/operation/cubit/operation_cubit.dart b/lib/dashboard/connection/operation/cubit/operation_cubit.dart index 2f1d4ce5b..5f3aede70 100644 --- a/lib/dashboard/connection/operation/cubit/operation_cubit.dart +++ b/lib/dashboard/connection/operation/cubit/operation_cubit.dart @@ -208,8 +208,10 @@ class OperationCubit extends Cubit { walletConnectCubit.state.transaction!.value!; amount = MWeb3Client.formatEthAmount(amount: ethAmount.getInWei); - final web3RpcURL = - await fetchRpcUrl(manageNetworkCubit.state.network); + final web3RpcURL = await fetchRpcUrl( + blockchainNetwork: manageNetworkCubit.state.network, + dotEnv: dotenv, + ); log.i('web3RpcURL - $web3RpcURL'); final (_, _, feeData) = await MWeb3Client.estimateEVMFee( @@ -324,7 +326,7 @@ class OperationCubit extends Cubit { operationAttemptCount++; log.i('sendOperataion attempt $operationAttemptCount'); - final isInternetAvailable = await isConnected(); + final isInternetAvailable = await isConnectedToInternet(); if (!isInternetAvailable) { throw NetworkException( message: NetworkError.NETWORK_ERROR_NO_INTERNET_CONNECTION, @@ -356,7 +358,10 @@ class OperationCubit extends Cubit { final EtherAmount ethAmount = walletConnectCubit.state.transaction!.value!; - final rpcUrl = await fetchRpcUrl(manageNetworkCubit.state.network); + final rpcUrl = await fetchRpcUrl( + blockchainNetwork: manageNetworkCubit.state.network, + dotEnv: dotenv, + ); log.i('rpcUrl - $rpcUrl'); diff --git a/lib/dashboard/connection/operation/widgets/fee_details.dart b/lib/dashboard/connection/operation/widgets/fee_details.dart index 6d51aa750..718f9dbae 100644 --- a/lib/dashboard/connection/operation/widgets/fee_details.dart +++ b/lib/dashboard/connection/operation/widgets/fee_details.dart @@ -1,6 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class FeeDetails extends StatelessWidget { @@ -23,7 +23,7 @@ class FeeDetails extends StatelessWidget { final double grandTotal = amount + fee; return BackgroundCard( - color: Theme.of(context).colorScheme.cardBackground, + color: Theme.of(context).colorScheme.surface, child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, @@ -78,7 +78,7 @@ class FeeDetails extends StatelessWidget { (grandTotal * tokenUSDRate) .decimalNumber(6) .formatNumber, - style: Theme.of(context).textTheme.bodySmall2, + style: Theme.of(context).textTheme.bodyMedium, ), ], ), @@ -96,7 +96,7 @@ class FeeDetails extends StatelessWidget { ), child: Divider( height: 0.1, - color: Theme.of(context).colorScheme.borderColor, + color: Theme.of(context).colorScheme.onSurface.withOpacity(0.12), ), ); } diff --git a/lib/dashboard/connection/operation/widgets/sender_reciever.dart b/lib/dashboard/connection/operation/widgets/sender_reciever.dart index a11b3c110..bbbee0c94 100644 --- a/lib/dashboard/connection/operation/widgets/sender_reciever.dart +++ b/lib/dashboard/connection/operation/widgets/sender_reciever.dart @@ -1,6 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class SenderReceiver extends StatelessWidget { @@ -41,7 +41,7 @@ class SenderReceiverCard extends StatelessWidget { @override Widget build(BuildContext context) { return BackgroundCard( - color: Theme.of(context).colorScheme.cardBackground, + color: Theme.of(context).colorScheme.surface, child: SizedBox( width: double.infinity, child: Column( @@ -53,7 +53,7 @@ class SenderReceiverCard extends StatelessWidget { ), MyText( value, - style: Theme.of(context).textTheme.walletAddress, + style: Theme.of(context).textTheme.bodyMedium, ), ], ), diff --git a/lib/dashboard/connection/rights/cubit/rights_cubit.dart b/lib/dashboard/connection/rights/cubit/rights_cubit.dart index ff0accc5e..f1489ade2 100644 --- a/lib/dashboard/connection/rights/cubit/rights_cubit.dart +++ b/lib/dashboard/connection/rights/cubit/rights_cubit.dart @@ -29,7 +29,7 @@ class RightsCubit extends Cubit { log.i('Started disconnecting'); emit(state.loading()); - final isInternetAvailable = await isConnected(); + final isInternetAvailable = await isConnectedToInternet(); if (!isInternetAvailable) { throw NetworkException( message: NetworkError.NETWORK_ERROR_NO_INTERNET_CONNECTION, diff --git a/lib/dashboard/connection/sign_payload/cubit/sign_payload_cubit.dart b/lib/dashboard/connection/sign_payload/cubit/sign_payload_cubit.dart index f99c9d073..a3aee14de 100644 --- a/lib/dashboard/connection/sign_payload/cubit/sign_payload_cubit.dart +++ b/lib/dashboard/connection/sign_payload/cubit/sign_payload_cubit.dart @@ -155,7 +155,7 @@ class SignPayloadCubit extends Cubit { log.i('Started signing'); emit(state.loading()); - final isInternetAvailable = await isConnected(); + final isInternetAvailable = await isConnectedToInternet(); if (!isInternetAvailable) { throw NetworkException( message: NetworkError.NETWORK_ERROR_NO_INTERNET_CONNECTION, diff --git a/lib/dashboard/connection/sign_payload/view/sign_payload_page.dart b/lib/dashboard/connection/sign_payload/view/sign_payload_page.dart index f618ebebc..5b523cae0 100644 --- a/lib/dashboard/connection/sign_payload/view/sign_payload_page.dart +++ b/lib/dashboard/connection/sign_payload/view/sign_payload_page.dart @@ -5,7 +5,7 @@ import 'package:altme/connection_bridge/connection_bridge.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; import 'package:altme/route/route.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:altme/wallet/cubit/wallet_cubit.dart'; import 'package:beacon_flutter/beacon_flutter.dart'; import 'package:flutter/material.dart'; @@ -178,7 +178,7 @@ class _SignPayloadViewState extends State { MyText( beaconRequest!.request!.sourceAddress!, textAlign: TextAlign.center, - style: Theme.of(context).textTheme.beaconPayload, + style: Theme.of(context).textTheme.bodyMedium, ), const SizedBox(height: Sizes.spaceXLarge), ], @@ -191,7 +191,7 @@ class _SignPayloadViewState extends State { Text( message, textAlign: TextAlign.left, - style: Theme.of(context).textTheme.beaconPayload, + style: Theme.of(context).textTheme.bodyMedium, ), const SizedBox(height: Sizes.spaceNormal), ], diff --git a/lib/dashboard/crypto_account_switcher/crypto_bottom_sheet/view/crypto_bottom_sheet_view.dart b/lib/dashboard/crypto_account_switcher/crypto_bottom_sheet/view/crypto_bottom_sheet_view.dart index f028c9ebe..2eeb75a88 100644 --- a/lib/dashboard/crypto_account_switcher/crypto_bottom_sheet/view/crypto_bottom_sheet_view.dart +++ b/lib/dashboard/crypto_account_switcher/crypto_bottom_sheet/view/crypto_bottom_sheet_view.dart @@ -2,7 +2,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/credentials/credentials.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:altme/wallet/wallet.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -95,7 +95,7 @@ class _CryptoBottomSheetPageState extends State { backgroundColor: Colors.transparent, body: DecoratedBox( decoration: BoxDecoration( - color: Theme.of(context).colorScheme.surface, + color: Theme.of(context).colorScheme.surfaceDim, boxShadow: [ BoxShadow( color: Theme.of(context).colorScheme.inversePrimary, @@ -125,7 +125,7 @@ class _CryptoBottomSheetPageState extends State { const SizedBox(width: Sizes.spaceXSmall), Text( l10n.selectAccount, - style: Theme.of(context).textTheme.accountsText, + style: Theme.of(context).textTheme.headlineMedium, ), ], ), @@ -140,7 +140,8 @@ class _CryptoBottomSheetPageState extends State { border: Border.all( color: Theme.of(context) .colorScheme - .accountBottomSheetBorderColor, + .onSurface + .withOpacity(0.12), width: 0.2, ), borderRadius: const BorderRadius.all( @@ -173,7 +174,8 @@ class _CryptoBottomSheetPageState extends State { height: 0.2, color: Theme.of(context) .colorScheme - .borderColor, + .onSurface + .withOpacity(0.12), ), ), ), diff --git a/lib/dashboard/crypto_account_switcher/crypto_bottom_sheet/widgets/crypto_accont_item.dart b/lib/dashboard/crypto_account_switcher/crypto_bottom_sheet/widgets/crypto_accont_item.dart index c1d1cbf9a..4d4a865aa 100644 --- a/lib/dashboard/crypto_account_switcher/crypto_bottom_sheet/widgets/crypto_accont_item.dart +++ b/lib/dashboard/crypto_account_switcher/crypto_bottom_sheet/widgets/crypto_accont_item.dart @@ -1,6 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:altme/wallet/model/model.dart'; import 'package:flutter/material.dart'; @@ -34,7 +34,7 @@ class CryptoAccountItem extends StatelessWidget { horizontalTitleGap: 0, leading: Checkbox( value: isSelected, - fillColor: MaterialStateProperty.all( + fillColor: WidgetStateProperty.all( Theme.of(context).colorScheme.inversePrimary, ), checkColor: Theme.of(context).colorScheme.primary, @@ -57,7 +57,7 @@ class CryptoAccountItem extends StatelessWidget { maxLines: 1, minFontSize: 14, overflow: TextOverflow.ellipsis, - style: Theme.of(context).textTheme.title, + style: Theme.of(context).textTheme.headlineSmall, ), ), const SizedBox(width: Sizes.spaceXSmall), @@ -77,7 +77,7 @@ class CryptoAccountItem extends StatelessWidget { padding: const EdgeInsets.symmetric(vertical: Sizes.spaceXSmall), child: MyText( walletAddressExtracted, - style: Theme.of(context).textTheme.walletAddress, + style: Theme.of(context).textTheme.bodyMedium, ), ), ); diff --git a/lib/dashboard/discover/view/discover_page.dart b/lib/dashboard/discover/view/discover_page.dart index a09d810a0..4693bde01 100644 --- a/lib/dashboard/discover/view/discover_page.dart +++ b/lib/dashboard/discover/view/discover_page.dart @@ -1,7 +1,6 @@ import 'package:altme/app/shared/widget/widget.dart'; import 'package:altme/credentials/credentials.dart'; import 'package:altme/dashboard/dashboard.dart'; -import 'package:altme/theme/theme.dart'; import 'package:altme/wallet/cubit/wallet_cubit.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -31,7 +30,7 @@ class _DiscoverPageState extends State { return BasePage( scrollView: false, padding: EdgeInsets.zero, - backgroundColor: Theme.of(context).colorScheme.transparent, + backgroundColor: Colors.transparent, body: BlocListener( listenWhen: (previous, current) { // if (current.model.profileSetting.selfSovereignIdentityOptions diff --git a/lib/dashboard/discover/view/discover_tab_page.dart b/lib/dashboard/discover/view/discover_tab_page.dart index 373697b7a..8096c730a 100644 --- a/lib/dashboard/discover/view/discover_tab_page.dart +++ b/lib/dashboard/discover/view/discover_tab_page.dart @@ -1,7 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:webview_flutter/webview_flutter.dart'; @@ -44,7 +43,7 @@ class _DiscoverTabPageViewState extends State return BasePage( scrollView: false, padding: EdgeInsets.zero, - backgroundColor: Theme.of(context).colorScheme.transparent, + backgroundColor: Colors.transparent, body: BlocBuilder( builder: (context, tabState) { _tabController.index = tabState; diff --git a/lib/dashboard/discover/widgets/discover_credential_category_item.dart b/lib/dashboard/discover/widgets/discover_credential_category_item.dart index 45265578a..7b7679762 100644 --- a/lib/dashboard/discover/widgets/discover_credential_category_item.dart +++ b/lib/dashboard/discover/widgets/discover_credential_category_item.dart @@ -1,6 +1,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; -import 'package:altme/theme/theme.dart'; +import 'package:altme/l10n/l10n.dart'; + import 'package:flutter/material.dart'; class DiscoverCredentialCategoryItem extends StatelessWidget { @@ -17,7 +18,7 @@ class DiscoverCredentialCategoryItem extends StatelessWidget { @override Widget build(BuildContext context) { - final credentialCategoryConfig = credentialCategory.config(context); + final credentialCategoryConfig = credentialCategory.config(context.l10n); //sort credentials by order dummyCredentials.sort( (a, b) => @@ -38,7 +39,7 @@ class DiscoverCredentialCategoryItem extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 0), child: Text( credentialCategoryConfig.discoverTitle, - style: Theme.of(context).textTheme.credentialCategoryTitle, + style: Theme.of(context).textTheme.headlineSmall, ), ), Padding( @@ -46,7 +47,9 @@ class DiscoverCredentialCategoryItem extends StatelessWidget { child: Text( credentialCategoryConfig.discoverSubTitle, maxLines: 3, - style: Theme.of(context).textTheme.credentialCategorySubTitle, + style: Theme.of(context).textTheme.titleMedium!.copyWith( + color: Theme.of(context).colorScheme.secondaryContainer, + ), ), ), const SizedBox(height: 14), diff --git a/lib/dashboard/discover/widgets/discover_dynamic_detail.dart b/lib/dashboard/discover/widgets/discover_dynamic_detail.dart index b8fff09a5..8a3bb5ea7 100644 --- a/lib/dashboard/discover/widgets/discover_dynamic_detail.dart +++ b/lib/dashboard/discover/widgets/discover_dynamic_detail.dart @@ -1,6 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; @@ -22,8 +22,8 @@ class DiscoverDynamicDetial extends StatelessWidget { Widget build(BuildContext context) { final textTheme = Theme.of(context).textTheme; - final titleTheme = textTheme.discoverFieldTitle; - final valueTheme = textTheme.discoverFieldDescription; + final titleTheme = textTheme.titleLarge!; + final valueTheme = textTheme.bodyMedium!; return Padding( padding: padding, @@ -54,7 +54,7 @@ class DiscoverDynamicDetial extends StatelessWidget { }, child: ImageIcon( const AssetImage(IconStrings.link), - color: Theme.of(context).colorScheme.markDownA, + color: Theme.of(context).colorScheme.primary, size: 17, ), ), diff --git a/lib/dashboard/drawer/about_altme/about_altme/view/about_altme_menu.dart b/lib/dashboard/drawer/about_altme/about_altme/view/about_altme_menu.dart index bceccf02d..ca5839bad 100644 --- a/lib/dashboard/drawer/about_altme/about_altme/view/about_altme_menu.dart +++ b/lib/dashboard/drawer/about_altme/about_altme/view/about_altme_menu.dart @@ -1,7 +1,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:package_info_plus/package_info_plus.dart'; @@ -35,7 +35,7 @@ class AboutAltmeView extends StatelessWidget { final profileSetting = profileModel.profileSetting; return BasePage( - backgroundColor: Theme.of(context).colorScheme.drawerBackground, + backgroundColor: Theme.of(context).colorScheme.surface, useSafeArea: true, scrollView: true, titleAlignment: Alignment.topCenter, @@ -57,7 +57,7 @@ class AboutAltmeView extends StatelessWidget { ), child: Text( profileSetting.generalOptions.companyName, - style: Theme.of(context).textTheme.drawerItemTitle.copyWith( + style: Theme.of(context).textTheme.titleLarge!.copyWith( fontSize: 18, ), ), @@ -82,7 +82,7 @@ class AboutAltmeView extends StatelessWidget { ), child: Text( l10n.about, - style: Theme.of(context).textTheme.drawerItemTitle.copyWith( + style: Theme.of(context).textTheme.titleLarge!.copyWith( fontSize: 18, ), ), diff --git a/lib/dashboard/drawer/about_altme/sofware_licenses/view/software_license_view.dart b/lib/dashboard/drawer/about_altme/sofware_licenses/view/software_license_view.dart index 9929cd90c..5b7759ace 100644 --- a/lib/dashboard/drawer/about_altme/sofware_licenses/view/software_license_view.dart +++ b/lib/dashboard/drawer/about_altme/sofware_licenses/view/software_license_view.dart @@ -1,7 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -62,7 +61,7 @@ class _SoftwareLicenseViewState extends State { physics: const ScrollPhysics(), itemBuilder: (context, index) { return BackgroundCard( - color: Theme.of(context).colorScheme.surfaceContainer, + color: Theme.of(context).colorScheme.surface.withOpacity(0.07), padding: const EdgeInsets.symmetric(horizontal: 8), margin: const EdgeInsets.only(bottom: 8), child: TransparentInkWell( @@ -99,7 +98,7 @@ class _SoftwareLicenseViewState extends State { const SizedBox(width: 10), Icon( Icons.chevron_right, - color: Theme.of(context).colorScheme.onBackground, + color: Theme.of(context).colorScheme.onSurface, ), ], ), diff --git a/lib/dashboard/drawer/about_altme/terms/view/terms_page.dart b/lib/dashboard/drawer/about_altme/terms/view/terms_page.dart index dedd6538e..aeb2276ce 100644 --- a/lib/dashboard/drawer/about_altme/terms/view/terms_page.dart +++ b/lib/dashboard/drawer/about_altme/terms/view/terms_page.dart @@ -14,7 +14,7 @@ class TermsPage extends StatelessWidget { Widget build(BuildContext context) { final l10n = context.l10n; return BasePage( - backgroundColor: Theme.of(context).colorScheme.background, + backgroundColor: Theme.of(context).colorScheme.surface, title: l10n.termsOfUse, titleLeading: const BackLeadingButton(), scrollView: false, diff --git a/lib/dashboard/drawer/advance_settings/widgets/advance_settings_radio_item.dart b/lib/dashboard/drawer/advance_settings/widgets/advance_settings_radio_item.dart index bafa0c81b..1a39adf54 100644 --- a/lib/dashboard/drawer/advance_settings/widgets/advance_settings_radio_item.dart +++ b/lib/dashboard/drawer/advance_settings/widgets/advance_settings_radio_item.dart @@ -1,5 +1,5 @@ import 'package:altme/app/app.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class AdvanceSettingsRadioItem extends StatelessWidget { @@ -22,7 +22,7 @@ class AdvanceSettingsRadioItem extends StatelessWidget { horizontalTitleGap: 0, leading: Checkbox( value: isSelected, - fillColor: MaterialStateProperty.all( + fillColor: WidgetStateProperty.all( Theme.of(context).colorScheme.inversePrimary, ), checkColor: Theme.of(context).colorScheme.primary, @@ -34,7 +34,7 @@ class AdvanceSettingsRadioItem extends StatelessWidget { maxLines: 1, minFontSize: 12, overflow: TextOverflow.ellipsis, - style: Theme.of(context).textTheme.title, + style: Theme.of(context).textTheme.headlineSmall, ), ); } diff --git a/lib/dashboard/drawer/blockchain_settings/blockchain_settings/view/blockchain_settings_menu.dart b/lib/dashboard/drawer/blockchain_settings/blockchain_settings/view/blockchain_settings_menu.dart index 64f80d6ec..0aca6d109 100644 --- a/lib/dashboard/drawer/blockchain_settings/blockchain_settings/view/blockchain_settings_menu.dart +++ b/lib/dashboard/drawer/blockchain_settings/blockchain_settings/view/blockchain_settings_menu.dart @@ -1,7 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; import 'package:altme/wallet/wallet.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -29,7 +28,7 @@ class BlockchainSettingsView extends StatelessWidget { Widget build(BuildContext context) { final l10n = context.l10n; return BasePage( - backgroundColor: Theme.of(context).colorScheme.drawerBackground, + backgroundColor: Theme.of(context).colorScheme.surface, useSafeArea: true, scrollView: true, titleAlignment: Alignment.topCenter, diff --git a/lib/dashboard/drawer/blockchain_settings/manage_accounts/view/account_private_key_page.dart b/lib/dashboard/drawer/blockchain_settings/manage_accounts/view/account_private_key_page.dart index 56ed1807a..64b063942 100644 --- a/lib/dashboard/drawer/blockchain_settings/manage_accounts/view/account_private_key_page.dart +++ b/lib/dashboard/drawer/blockchain_settings/manage_accounts/view/account_private_key_page.dart @@ -76,7 +76,7 @@ class _AccountPrivateKeyPageState extends State }, icon: Icon( Icons.qr_code, - color: Theme.of(context).colorScheme.onBackground, + color: Theme.of(context).colorScheme.onSurface, ), ), body: BackgroundCard( diff --git a/lib/dashboard/drawer/blockchain_settings/manage_accounts/view/private_key_qr_page.dart b/lib/dashboard/drawer/blockchain_settings/manage_accounts/view/private_key_qr_page.dart index 18c54cfca..688a20ae8 100644 --- a/lib/dashboard/drawer/blockchain_settings/manage_accounts/view/private_key_qr_page.dart +++ b/lib/dashboard/drawer/blockchain_settings/manage_accounts/view/private_key_qr_page.dart @@ -81,7 +81,7 @@ class _PrivateKeyQrPageState extends State child: QrImageView( data: widget.data, size: 200, - foregroundColor: Theme.of(context).colorScheme.onBackground, + foregroundColor: Theme.of(context).colorScheme.onSurface, ), ), const SizedBox(height: 10), diff --git a/lib/dashboard/drawer/blockchain_settings/manage_accounts/widgets/manage_accounts_item.dart b/lib/dashboard/drawer/blockchain_settings/manage_accounts/widgets/manage_accounts_item.dart index 793c94c9c..bf701a753 100644 --- a/lib/dashboard/drawer/blockchain_settings/manage_accounts/widgets/manage_accounts_item.dart +++ b/lib/dashboard/drawer/blockchain_settings/manage_accounts/widgets/manage_accounts_item.dart @@ -1,7 +1,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:altme/wallet/model/model.dart'; import 'package:flutter/material.dart'; @@ -35,12 +35,12 @@ class ManageAccountsItem extends StatelessWidget { margin: const EdgeInsets.only(bottom: Sizes.spaceSmall), padding: const EdgeInsets.symmetric(horizontal: Sizes.spaceSmall), decoration: BoxDecoration( - color: Theme.of(context).colorScheme.cardHighlighted, + color: Theme.of(context).colorScheme.surface, borderRadius: const BorderRadius.all( Radius.circular(Sizes.normalRadius), ), border: Border.all( - color: Theme.of(context).colorScheme.borderColor, + color: Theme.of(context).colorScheme.onSurface.withOpacity(0.12), width: 0.25, ), ), @@ -65,7 +65,7 @@ class ManageAccountsItem extends StatelessWidget { maxLines: 1, minFontSize: 12, overflow: TextOverflow.ellipsis, - style: Theme.of(context).textTheme.title, + style: Theme.of(context).textTheme.headlineSmall, ), ), const SizedBox(width: Sizes.spaceXSmall), @@ -85,7 +85,7 @@ class ManageAccountsItem extends StatelessWidget { padding: const EdgeInsets.symmetric(vertical: Sizes.spaceXSmall), child: MyText( walletAddressExtracted, - style: Theme.of(context).textTheme.walletAddress, + style: Theme.of(context).textTheme.bodyMedium, ), ), ), diff --git a/lib/dashboard/drawer/blockchain_settings/manage_accounts/widgets/reveal_private_key_button.dart b/lib/dashboard/drawer/blockchain_settings/manage_accounts/widgets/reveal_private_key_button.dart index e02f85e52..934e9dfe9 100644 --- a/lib/dashboard/drawer/blockchain_settings/manage_accounts/widgets/reveal_private_key_button.dart +++ b/lib/dashboard/drawer/blockchain_settings/manage_accounts/widgets/reveal_private_key_button.dart @@ -1,6 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class RevealPrivateKeyButton extends StatelessWidget { @@ -33,7 +33,7 @@ class RevealPrivateKeyButton extends StatelessWidget { ), Text( l10n.revealPrivateKey.toUpperCase(), - style: Theme.of(context).textTheme.miniButton, + style: Theme.of(context).textTheme.bodyMedium, ), ], ), diff --git a/lib/dashboard/drawer/blockchain_settings/manage_accounts/widgets/see_address_button.dart b/lib/dashboard/drawer/blockchain_settings/manage_accounts/widgets/see_address_button.dart index e8c01f780..be8be0a42 100644 --- a/lib/dashboard/drawer/blockchain_settings/manage_accounts/widgets/see_address_button.dart +++ b/lib/dashboard/drawer/blockchain_settings/manage_accounts/widgets/see_address_button.dart @@ -1,6 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class SeeAddressButton extends StatelessWidget { @@ -36,7 +36,7 @@ class SeeAddressButton extends StatelessWidget { ), Text( l10n.seeAddress.toUpperCase(), - style: Theme.of(context).textTheme.miniButton, + style: Theme.of(context).textTheme.bodyMedium, ), ], ), diff --git a/lib/dashboard/drawer/blockchain_settings/manage_network/view/manage_network_page.dart b/lib/dashboard/drawer/blockchain_settings/manage_network/view/manage_network_page.dart index 9f326e49e..2fd3ae4d9 100644 --- a/lib/dashboard/drawer/blockchain_settings/manage_network/view/manage_network_page.dart +++ b/lib/dashboard/drawer/blockchain_settings/manage_network/view/manage_network_page.dart @@ -1,7 +1,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:altme/wallet/cubit/wallet_cubit.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -37,7 +37,7 @@ class ManageNetworkPage extends StatelessWidget { Center( child: Text( l10n.chooseNetWork, - style: Theme.of(context).textTheme.radioTitle, + style: Theme.of(context).textTheme.headlineSmall, ), ), const SizedBox(height: 15), @@ -58,7 +58,10 @@ class ManageNetworkPage extends StatelessWidget { ), child: Divider( height: 0.2, - color: Theme.of(context).colorScheme.borderColor, + color: Theme.of(context) + .colorScheme + .onSurface + .withOpacity(0.12), ), ), ], diff --git a/lib/dashboard/drawer/blockchain_settings/manage_network/widgets/network_selector.dart b/lib/dashboard/drawer/blockchain_settings/manage_network/widgets/network_selector.dart index 2a37052e3..fbe348bcb 100644 --- a/lib/dashboard/drawer/blockchain_settings/manage_network/widgets/network_selector.dart +++ b/lib/dashboard/drawer/blockchain_settings/manage_network/widgets/network_selector.dart @@ -1,6 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -27,11 +27,11 @@ class NetworkSelector extends StatelessWidget { visualDensity: VisualDensity.compact, title: Text( network.title!, - style: Theme.of(context).textTheme.radioOption, + style: Theme.of(context).textTheme.titleMedium, ), subtitle: Text( network.subTitle!, - style: Theme.of(context).textTheme.bodySmall2, + style: Theme.of(context).textTheme.bodyMedium, ), onChanged: (BlockchainNetwork? value) async { if (value != null) { diff --git a/lib/dashboard/drawer/help_center/faqs/view/faqs_page.dart b/lib/dashboard/drawer/help_center/faqs/view/faqs_page.dart index a0b4d9e05..5733965a3 100644 --- a/lib/dashboard/drawer/help_center/faqs/view/faqs_page.dart +++ b/lib/dashboard/drawer/help_center/faqs/view/faqs_page.dart @@ -1,7 +1,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -49,7 +49,7 @@ class FAQsView extends StatelessWidget { tilePadding: const EdgeInsets.symmetric(horizontal: 8), title: Text( faqElement.que, - style: Theme.of(context).textTheme.faqQue, + style: Theme.of(context).textTheme.bodyMedium, ), children: [ Padding( @@ -61,13 +61,15 @@ class FAQsView extends StatelessWidget { child: Text( faqElement.ans, style: faqElement.href != null - ? Theme.of(context).textTheme.faqAns.copyWith( + ? Theme.of(context) + .textTheme + .bodyMedium! + .copyWith( decoration: TextDecoration.underline, - color: Theme.of(context) - .colorScheme - .markDownA, + color: + Theme.of(context).colorScheme.primary, ) - : Theme.of(context).textTheme.faqAns, + : Theme.of(context).textTheme.bodyMedium!, textAlign: TextAlign.justify, ), ), diff --git a/lib/dashboard/drawer/help_center/help_center/view/help_center_menu.dart b/lib/dashboard/drawer/help_center/help_center/view/help_center_menu.dart index 17e681773..f35e473d3 100644 --- a/lib/dashboard/drawer/help_center/help_center/view/help_center_menu.dart +++ b/lib/dashboard/drawer/help_center/help_center/view/help_center_menu.dart @@ -1,7 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -52,7 +51,7 @@ class HelpCenterView extends StatelessWidget { } return BasePage( - backgroundColor: Theme.of(context).colorScheme.drawerBackground, + backgroundColor: Theme.of(context).colorScheme.surface, useSafeArea: true, scrollView: true, titleAlignment: Alignment.topCenter, diff --git a/lib/dashboard/drawer/import_talao_community_card/view/import_talao_community_card_page.dart b/lib/dashboard/drawer/import_talao_community_card/view/import_talao_community_card_page.dart index c93ac0626..a58655d9a 100644 --- a/lib/dashboard/drawer/import_talao_community_card/view/import_talao_community_card_page.dart +++ b/lib/dashboard/drawer/import_talao_community_card/view/import_talao_community_card_page.dart @@ -1,7 +1,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:altme/wallet/cubit/wallet_cubit.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -88,7 +88,7 @@ class _ImportTalaoCommunityCardViewState Text( l10n.drawerTalaoCommunityCardTitle, textAlign: TextAlign.center, - style: Theme.of(context).textTheme.infoTitle, + style: Theme.of(context).textTheme.titleLarge, ), const SizedBox(height: 15), Text( @@ -96,7 +96,7 @@ class _ImportTalaoCommunityCardViewState textAlign: TextAlign.center, style: Theme.of(context) .textTheme - .infoSubtitle + .titleMedium! .copyWith(fontSize: 13), ), ], @@ -109,9 +109,7 @@ class _ImportTalaoCommunityCardViewState BaseTextField( hint: l10n.drawerTalaoCommunityCardTextBoxMessage, fillColor: Colors.transparent, - hintStyle: Theme.of(context) - .textTheme - .hintTextFieldStyle, + hintStyle: Theme.of(context).textTheme.bodyMedium, controller: privateKeyController, error: state.isTextFieldEdited && !state.isPrivateKeyValid @@ -130,9 +128,8 @@ class _ImportTalaoCommunityCardViewState margin: const EdgeInsets.all(Sizes.spaceNormal), decoration: BoxDecoration( shape: BoxShape.circle, - color: Theme.of(context) - .colorScheme - .checkMarkColor, + color: + Theme.of(context).colorScheme.onTertiary, ), child: const Icon( Icons.check, @@ -148,7 +145,7 @@ class _ImportTalaoCommunityCardViewState textAlign: TextAlign.center, style: Theme.of(context) .textTheme - .infoSubtitle + .titleMedium! .copyWith(fontSize: 13), ), ], diff --git a/lib/dashboard/drawer/profile/view/pick_profile_menu.dart b/lib/dashboard/drawer/profile/view/pick_profile_menu.dart index 4488347c9..dd49a8857 100644 --- a/lib/dashboard/drawer/profile/view/pick_profile_menu.dart +++ b/lib/dashboard/drawer/profile/view/pick_profile_menu.dart @@ -2,7 +2,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/dashboard/drawer/profile/widget/profile_selector_widget.dart'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; class PickProfileMenu extends StatelessWidget { @@ -27,7 +26,7 @@ class PickProfileMenuView extends StatelessWidget { @override Widget build(BuildContext context) { return BasePage( - backgroundColor: Theme.of(context).colorScheme.drawerBackground, + backgroundColor: Theme.of(context).colorScheme.surface, useSafeArea: true, scrollView: true, titleAlignment: Alignment.topCenter, diff --git a/lib/dashboard/drawer/profile/widget/profile_selector_widget.dart b/lib/dashboard/drawer/profile/widget/profile_selector_widget.dart index 0ae8121fd..cdb21a765 100644 --- a/lib/dashboard/drawer/profile/widget/profile_selector_widget.dart +++ b/lib/dashboard/drawer/profile/widget/profile_selector_widget.dart @@ -2,7 +2,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/credentials/cubit/credentials_cubit.dart'; import 'package:altme/dashboard/profile/profile.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:altme/wallet/wallet.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -29,7 +29,7 @@ class ProfileSelectorWidget extends StatelessWidget { padding: const EdgeInsets.all(Sizes.spaceSmall), margin: const EdgeInsets.all(Sizes.spaceXSmall), decoration: BoxDecoration( - color: Theme.of(context).colorScheme.drawerSurface, + color: Theme.of(context).colorScheme.surface, borderRadius: const BorderRadius.all( Radius.circular(Sizes.largeRadius), ), @@ -45,7 +45,7 @@ class ProfileSelectorWidget extends StatelessWidget { children: [ Text( l10n.walletProfilesDescription, - style: Theme.of(context).textTheme.drawerItemSubtitle, + style: Theme.of(context).textTheme.titleMedium, ), ], ), @@ -72,8 +72,10 @@ class ProfileSelectorWidget extends StatelessWidget { const EdgeInsets.symmetric(horizontal: 8), child: Divider( height: 0, - color: - Theme.of(context).colorScheme.borderColor, + color: Theme.of(context) + .colorScheme + .onSurface + .withOpacity(0.12), ), ), ListTile( diff --git a/lib/dashboard/drawer/reset_wallet/helper_functions/reset_wallet.dart b/lib/dashboard/drawer/reset_wallet/helper_functions/reset_wallet.dart new file mode 100644 index 000000000..409e74664 --- /dev/null +++ b/lib/dashboard/drawer/reset_wallet/helper_functions/reset_wallet.dart @@ -0,0 +1,14 @@ +import 'package:altme/credentials/cubit/credentials_cubit.dart'; +import 'package:altme/dashboard/drawer/help_center/altme_support_chat/cubit/altme_support_chat_cubit.dart'; +import 'package:altme/dashboard/profile/cubit/profile_cubit.dart'; +import 'package:altme/wallet/cubit/wallet_cubit.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +Future resetWallet(BuildContext context) async { + await context.read().resetProfile(); + await context + .read() + .resetWallet(context.read()); + await context.read().dispose(); +} diff --git a/lib/dashboard/drawer/reset_wallet/view/reset_wallet_menu.dart b/lib/dashboard/drawer/reset_wallet/view/reset_wallet_menu.dart index f8f84326d..dace17438 100644 --- a/lib/dashboard/drawer/reset_wallet/view/reset_wallet_menu.dart +++ b/lib/dashboard/drawer/reset_wallet/view/reset_wallet_menu.dart @@ -1,9 +1,8 @@ import 'package:altme/app/app.dart'; -import 'package:altme/credentials/credentials.dart'; import 'package:altme/dashboard/dashboard.dart'; +import 'package:altme/dashboard/drawer/reset_wallet/helper_functions/reset_wallet.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; -import 'package:altme/wallet/wallet.dart'; + import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -39,7 +38,7 @@ class ResetWalletView extends StatelessWidget { titleAlignment: Alignment.topCenter, padding: const EdgeInsets.symmetric(horizontal: Sizes.spaceSmall), titleLeading: const BackLeadingButton(), - backgroundColor: Theme.of(context).colorScheme.drawerBackground, + backgroundColor: Theme.of(context).colorScheme.surface, body: BlocBuilder( builder: (context, state) { return Column( @@ -49,7 +48,7 @@ class ResetWalletView extends StatelessWidget { Text( l10n.resetWalletTitle, textAlign: TextAlign.center, - style: Theme.of(context).textTheme.resetWalletTitle, + style: Theme.of(context).textTheme.titleLarge, ), const SizedBox( height: Sizes.spaceNormal, @@ -57,7 +56,7 @@ class ResetWalletView extends StatelessWidget { Text( l10n.resetWalletSubtitle, textAlign: TextAlign.center, - style: Theme.of(context).textTheme.resetWalletSubtitle, + style: Theme.of(context).textTheme.titleMedium, ), const SizedBox( height: Sizes.spaceLarge, @@ -65,13 +64,13 @@ class ResetWalletView extends StatelessWidget { Text( l10n.resetWalletSubtitle2, textAlign: TextAlign.center, - style: Theme.of(context).textTheme.subtitle4, + style: Theme.of(context).textTheme.titleMedium, ), const Spacer(), CheckboxItem( value: state.isRecoveryPhraseWritten, text: l10n.resetWalletCheckBox1, - textStyle: Theme.of(context).textTheme.resetWalletTitle, + textStyle: Theme.of(context).textTheme.titleLarge, onChange: (_) { context .read() @@ -84,7 +83,7 @@ class ResetWalletView extends StatelessWidget { CheckboxItem( value: state.isBackupCredentialSaved, text: l10n.resetWalletCheckBox2, - textStyle: Theme.of(context).textTheme.resetWalletTitle, + textStyle: Theme.of(context).textTheme.titleLarge, onChange: (_) { context .read() @@ -101,22 +100,18 @@ class ResetWalletView extends StatelessWidget { padding: const EdgeInsets.all(Sizes.spaceSmall), child: MyElevatedButton( text: l10n.resetWallet, - onPressed: state.isBackupCredentialSaved && - state.isRecoveryPhraseWritten - ? () async { - await securityCheck( - context: context, - localAuthApi: LocalAuthApi(), - onSuccess: () async { - await context.read().resetProfile(); - await context - .read() - .resetWallet(context.read()); - await context.read().dispose(); - }, - ); - } - : null, + onPressed: + state.isBackupCredentialSaved && state.isRecoveryPhraseWritten + ? () async { + await securityCheck( + context: context, + localAuthApi: LocalAuthApi(), + onSuccess: () async { + await resetWallet(context); + }, + ); + } + : null, ), ); }, diff --git a/lib/dashboard/drawer/src/view/check_linkedin_profile.dart b/lib/dashboard/drawer/src/view/check_linkedin_profile.dart index 9c08ee72e..841c0439d 100644 --- a/lib/dashboard/drawer/src/view/check_linkedin_profile.dart +++ b/lib/dashboard/drawer/src/view/check_linkedin_profile.dart @@ -3,7 +3,6 @@ import 'dart:convert'; import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -30,7 +29,7 @@ class CheckForLinkedInProfileView extends StatelessWidget { Widget build(BuildContext context) { final l10n = context.l10n; return Drawer( - backgroundColor: Theme.of(context).colorScheme.drawerBackground, + backgroundColor: Theme.of(context).colorScheme.surface, child: SafeArea( child: SingleChildScrollView( child: Padding( diff --git a/lib/dashboard/drawer/src/view/drawer_page.dart b/lib/dashboard/drawer/src/view/drawer_page.dart index df9c74d38..1517fdaed 100644 --- a/lib/dashboard/drawer/src/view/drawer_page.dart +++ b/lib/dashboard/drawer/src/view/drawer_page.dart @@ -3,7 +3,6 @@ import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/dashboard/drawer/profile/view/pick_profile_menu.dart'; import 'package:altme/enterprise/cubit/enterprise_cubit.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -29,7 +28,7 @@ class DrawerView extends StatelessWidget { return SizedBox( width: MediaQuery.of(context).size.width, child: Drawer( - backgroundColor: Theme.of(context).colorScheme.drawerBackground, + backgroundColor: Theme.of(context).colorScheme.surface, child: SafeArea( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 15), diff --git a/lib/dashboard/drawer/src/widgets/drawer_app_version.dart b/lib/dashboard/drawer/src/widgets/drawer_app_version.dart index 7373c4b51..f8b77e6fd 100644 --- a/lib/dashboard/drawer/src/widgets/drawer_app_version.dart +++ b/lib/dashboard/drawer/src/widgets/drawer_app_version.dart @@ -1,5 +1,5 @@ import 'package:altme/splash/splash.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -12,7 +12,7 @@ class DrawerAppVersion extends StatelessWidget { builder: (context, state) { return Text( 'Talao: V${state.versionNumber} (${state.buildNumber})', - style: Theme.of(context).textTheme.drawerMenu, + style: Theme.of(context).textTheme.bodyLarge, ); }, ); diff --git a/lib/dashboard/drawer/src/widgets/drawer_category_item.dart b/lib/dashboard/drawer/src/widgets/drawer_category_item.dart index 2e2d4debe..9e1a8c28d 100644 --- a/lib/dashboard/drawer/src/widgets/drawer_category_item.dart +++ b/lib/dashboard/drawer/src/widgets/drawer_category_item.dart @@ -1,5 +1,5 @@ import 'package:altme/app/shared/constants/sizes.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class DrawerCategoryItem extends StatelessWidget { @@ -25,7 +25,7 @@ class DrawerCategoryItem extends StatelessWidget { child: Container( padding: padding, decoration: BoxDecoration( - color: Theme.of(context).colorScheme.drawerSurface, + color: Theme.of(context).colorScheme.surfaceBright, borderRadius: const BorderRadius.all( Radius.circular(Sizes.normalRadius), ), @@ -39,13 +39,13 @@ class DrawerCategoryItem extends StatelessWidget { children: [ Text( title, - style: Theme.of(context).textTheme.drawerCategoryTitle, + style: Theme.of(context).textTheme.headlineSmall, ), if (subTitle != null) ...[ const SizedBox(height: Sizes.space2XSmall), Text( subTitle!, - style: Theme.of(context).textTheme.drawerCategorySubTitle, + style: Theme.of(context).textTheme.titleMedium, ), ], ], @@ -58,7 +58,7 @@ class DrawerCategoryItem extends StatelessWidget { Icon( Icons.chevron_right, size: Sizes.icon2x, - color: Theme.of(context).colorScheme.unSelectedLabel, + color: Theme.of(context).colorScheme.onSurface.withOpacity(0.6), ), ], ], diff --git a/lib/dashboard/drawer/src/widgets/drawer_item.dart b/lib/dashboard/drawer/src/widgets/drawer_item.dart index 89b93bd04..2dc081ca7 100644 --- a/lib/dashboard/drawer/src/widgets/drawer_item.dart +++ b/lib/dashboard/drawer/src/widgets/drawer_item.dart @@ -1,5 +1,5 @@ import 'package:altme/app/app.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class DrawerItem extends StatelessWidget { @@ -26,7 +26,7 @@ class DrawerItem extends StatelessWidget { padding: const EdgeInsets.all(Sizes.spaceNormal), margin: const EdgeInsets.all(Sizes.spaceXSmall), decoration: BoxDecoration( - color: Theme.of(context).colorScheme.drawerSurface, + color: Theme.of(context).colorScheme.surfaceBright, borderRadius: const BorderRadius.all( Radius.circular( Sizes.normalRadius, @@ -45,25 +45,28 @@ class DrawerItem extends StatelessWidget { children: [ Text( title, - style: - Theme.of(context).textTheme.drawerItemTitle.copyWith( - color: isDisabled - ? Theme.of(context).colorScheme.lightGrey - : null, - ), + style: Theme.of(context).textTheme.titleLarge!.copyWith( + color: isDisabled + ? Theme.of(context) + .colorScheme + .onSurface + .withOpacity(0.6) + : null, + ), ), if (subtitle != null) ...[ const SizedBox(height: 10), Text( subtitle!, - style: Theme.of(context) - .textTheme - .drawerItemSubtitle - .copyWith( - color: isDisabled - ? Theme.of(context).colorScheme.lightGrey - : null, - ), + style: + Theme.of(context).textTheme.titleMedium!.copyWith( + color: isDisabled + ? Theme.of(context) + .colorScheme + .onSurface + .withOpacity(0.6) + : null, + ), ), const SizedBox(height: 10), ], @@ -78,8 +81,11 @@ class DrawerItem extends StatelessWidget { Icons.chevron_right, size: Sizes.icon2x, color: isDisabled - ? Theme.of(context).colorScheme.lightGrey - : Theme.of(context).colorScheme.unSelectedLabel, + ? Theme.of(context).colorScheme.onSurface.withOpacity(0.6) + : Theme.of(context) + .colorScheme + .onSurface + .withOpacity(0.6), ), ], ], diff --git a/lib/dashboard/drawer/src/widgets/drawer_item2.dart b/lib/dashboard/drawer/src/widgets/drawer_item2.dart index 1429f401d..fab3c4900 100644 --- a/lib/dashboard/drawer/src/widgets/drawer_item2.dart +++ b/lib/dashboard/drawer/src/widgets/drawer_item2.dart @@ -1,5 +1,5 @@ import 'package:altme/app/app.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class DrawerItem2 extends StatelessWidget { @@ -26,7 +26,7 @@ class DrawerItem2 extends StatelessWidget { padding: const EdgeInsets.all(Sizes.spaceNormal), margin: const EdgeInsets.all(Sizes.spaceXSmall), decoration: BoxDecoration( - color: Theme.of(context).colorScheme.drawerSurface, + color: Theme.of(context).colorScheme.surface, borderRadius: const BorderRadius.all( Radius.circular( Sizes.normalRadius, @@ -44,25 +44,28 @@ class DrawerItem2 extends StatelessWidget { children: [ Text( title, - style: - Theme.of(context).textTheme.drawerItemTitle.copyWith( - color: isDisabled - ? Theme.of(context).colorScheme.lightGrey - : null, - ), + style: Theme.of(context).textTheme.titleLarge!.copyWith( + color: isDisabled + ? Theme.of(context) + .colorScheme + .onSurface + .withOpacity(0.6) + : null, + ), ), if (subtitle != null) ...[ const SizedBox(height: 10), Text( subtitle!, - style: Theme.of(context) - .textTheme - .drawerItemSubtitle - .copyWith( - color: isDisabled - ? Theme.of(context).colorScheme.lightGrey - : null, - ), + style: + Theme.of(context).textTheme.titleMedium!.copyWith( + color: isDisabled + ? Theme.of(context) + .colorScheme + .onSurface + .withOpacity(0.6) + : null, + ), ), const SizedBox(height: 20), ], @@ -77,8 +80,11 @@ class DrawerItem2 extends StatelessWidget { Icons.chevron_right, size: 26, color: isDisabled - ? Theme.of(context).colorScheme.lightGrey - : Theme.of(context).colorScheme.unSelectedLabel, + ? Theme.of(context).colorScheme.onSurface.withOpacity(0.6) + : Theme.of(context) + .colorScheme + .onSurface + .withOpacity(0.6), ), ], ], diff --git a/lib/dashboard/drawer/src/widgets/drawer_item_divider.dart b/lib/dashboard/drawer/src/widgets/drawer_item_divider.dart index d3f4d4b2b..a81e9f445 100644 --- a/lib/dashboard/drawer/src/widgets/drawer_item_divider.dart +++ b/lib/dashboard/drawer/src/widgets/drawer_item_divider.dart @@ -1,4 +1,3 @@ -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; class DrawerItemDivider extends StatelessWidget { @@ -11,7 +10,7 @@ class DrawerItemDivider extends StatelessWidget { decoration: BoxDecoration( border: Border( bottom: BorderSide( - color: Theme.of(context).colorScheme.borderColor, + color: Theme.of(context).colorScheme.onSurface.withOpacity(0.12), width: 0.2, ), ), diff --git a/lib/dashboard/drawer/src/widgets/enterprise_data.dart b/lib/dashboard/drawer/src/widgets/enterprise_data.dart index ad2e4e0de..62246a683 100644 --- a/lib/dashboard/drawer/src/widgets/enterprise_data.dart +++ b/lib/dashboard/drawer/src/widgets/enterprise_data.dart @@ -1,5 +1,5 @@ import 'package:altme/app/app.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class EnterpriseData extends StatelessWidget { @@ -14,12 +14,12 @@ class EnterpriseData extends StatelessWidget { @override Widget build(BuildContext context) { - final textStyle = Theme.of(context).textTheme.drawerItemTitle; + final textStyle = Theme.of(context).textTheme.titleLarge!; return Container( padding: const EdgeInsets.all(Sizes.spaceNormal), margin: const EdgeInsets.all(Sizes.spaceXSmall), decoration: BoxDecoration( - color: Theme.of(context).colorScheme.drawerSurface, + color: Theme.of(context).colorScheme.surface, borderRadius: const BorderRadius.all( Radius.circular(Sizes.normalRadius), ), diff --git a/lib/dashboard/drawer/ssi/backup/backup_credential/view/backup_credential_page.dart b/lib/dashboard/drawer/ssi/backup/backup_credential/view/backup_credential_page.dart index a617aa3d5..fb100333c 100644 --- a/lib/dashboard/drawer/ssi/backup/backup_credential/view/backup_credential_page.dart +++ b/lib/dashboard/drawer/ssi/backup/backup_credential/view/backup_credential_page.dart @@ -4,7 +4,7 @@ import 'package:altme/dashboard/drawer/drawer.dart'; import 'package:altme/l10n/l10n.dart'; import 'package:altme/onboarding/onboarding.dart'; import 'package:altme/polygon_id/polygon_id.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:cryptocurrency_keys/cryptocurrency_keys.dart'; import 'package:file_saver/file_saver.dart'; import 'package:flutter/material.dart'; @@ -96,13 +96,13 @@ class BackupCredentialView extends StatelessWidget { Text( l10n.saveBackupCredentialTitle, textAlign: TextAlign.center, - style: Theme.of(context).textTheme.messageTitle, + style: Theme.of(context).textTheme.titleLarge, ), const SizedBox(height: Sizes.spaceXLarge), Text( l10n.saveBackupCredentialSubtitle, textAlign: TextAlign.center, - style: Theme.of(context).textTheme.subtitle4, + style: Theme.of(context).textTheme.titleMedium, ), const SizedBox(height: Sizes.spaceXLarge), Image.asset( diff --git a/lib/dashboard/drawer/ssi/backup/backup_mnemonic/view/backup_mnemonic_page.dart b/lib/dashboard/drawer/ssi/backup/backup_mnemonic/view/backup_mnemonic_page.dart index 49fbf53d4..5669c29a8 100644 --- a/lib/dashboard/drawer/ssi/backup/backup_mnemonic/view/backup_mnemonic_page.dart +++ b/lib/dashboard/drawer/ssi/backup/backup_mnemonic/view/backup_mnemonic_page.dart @@ -1,7 +1,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/l10n/l10n.dart'; import 'package:altme/onboarding/onboarding.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; import 'package:secure_storage/secure_storage.dart'; @@ -74,7 +74,7 @@ class BackupMnemonicView extends StatelessWidget { Text( l10n.backupCredentialPhraseExplanation, textAlign: TextAlign.center, - style: Theme.of(context).textTheme.messageTitle, + style: Theme.of(context).textTheme.titleLarge, ), ], ), diff --git a/lib/dashboard/drawer/ssi/backup/backup_polygon_identity/view/backup_polygon_identity_page.dart b/lib/dashboard/drawer/ssi/backup/backup_polygon_identity/view/backup_polygon_identity_page.dart index aea8026e7..5eac0222d 100644 --- a/lib/dashboard/drawer/ssi/backup/backup_polygon_identity/view/backup_polygon_identity_page.dart +++ b/lib/dashboard/drawer/ssi/backup/backup_polygon_identity/view/backup_polygon_identity_page.dart @@ -3,7 +3,7 @@ import 'package:altme/dashboard/drawer/drawer.dart'; import 'package:altme/l10n/l10n.dart'; import 'package:altme/onboarding/onboarding.dart'; import 'package:altme/polygon_id/cubit/polygon_id_cubit.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:altme/wallet/wallet.dart'; import 'package:cryptocurrency_keys/cryptocurrency_keys.dart'; import 'package:file_saver/file_saver.dart'; @@ -98,13 +98,13 @@ class BackupPolygonIdIdentityView extends StatelessWidget { Text( l10n.saveBackupCredentialTitle, textAlign: TextAlign.center, - style: Theme.of(context).textTheme.messageTitle, + style: Theme.of(context).textTheme.titleLarge, ), const SizedBox(height: Sizes.spaceXLarge), Text( l10n.saveBackupPolygonCredentialSubtitle, textAlign: TextAlign.center, - style: Theme.of(context).textTheme.subtitle4, + style: Theme.of(context).textTheme.titleMedium, ), const SizedBox(height: Sizes.spaceXLarge), Image.asset( diff --git a/lib/dashboard/drawer/ssi/backup/src/view/backup_menu.dart b/lib/dashboard/drawer/ssi/backup/src/view/backup_menu.dart index 9a465d69d..5b144597f 100644 --- a/lib/dashboard/drawer/ssi/backup/src/view/backup_menu.dart +++ b/lib/dashboard/drawer/ssi/backup/src/view/backup_menu.dart @@ -1,7 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -28,7 +27,7 @@ class BackupView extends StatelessWidget { Widget build(BuildContext context) { final l10n = context.l10n; return Drawer( - backgroundColor: Theme.of(context).colorScheme.drawerBackground, + backgroundColor: Theme.of(context).colorScheme.surface, child: SafeArea( child: SingleChildScrollView( child: Padding( diff --git a/lib/dashboard/drawer/ssi/manage_did/view/did/did_private_key_page.dart b/lib/dashboard/drawer/ssi/manage_did/view/did/did_private_key_page.dart index ae4d8a371..400c20dde 100644 --- a/lib/dashboard/drawer/ssi/manage_did/view/did/did_private_key_page.dart +++ b/lib/dashboard/drawer/ssi/manage_did/view/did/did_private_key_page.dart @@ -1,7 +1,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -85,7 +85,7 @@ class _DidPrivateKeyPageState extends State children: [ Text( l10n.didPrivateKey, - style: Theme.of(context).textTheme.title, + style: Theme.of(context).textTheme.headlineSmall, ), const SizedBox( height: Sizes.spaceNormal, diff --git a/lib/dashboard/drawer/ssi/manage_did/view/did_menu.dart b/lib/dashboard/drawer/ssi/manage_did/view/did_menu.dart index 2739c9f75..20865d7bf 100644 --- a/lib/dashboard/drawer/ssi/manage_did/view/did_menu.dart +++ b/lib/dashboard/drawer/ssi/manage_did/view/did_menu.dart @@ -2,7 +2,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; import 'package:altme/polygon_id/polygon_id.dart'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -29,7 +28,7 @@ class DidView extends StatelessWidget { Widget build(BuildContext context) { final l10n = context.l10n; return Drawer( - backgroundColor: Theme.of(context).colorScheme.drawerBackground, + backgroundColor: Theme.of(context).colorScheme.surface, child: SafeArea( child: SingleChildScrollView( child: Padding( diff --git a/lib/dashboard/drawer/ssi/manage_did/view/jwk_thumbprint_p256_key/jwk_thumbprint_p256_key_page.dart b/lib/dashboard/drawer/ssi/manage_did/view/jwk_thumbprint_p256_key/jwk_thumbprint_p256_key_page.dart index ff1440130..ba7aafe19 100644 --- a/lib/dashboard/drawer/ssi/manage_did/view/jwk_thumbprint_p256_key/jwk_thumbprint_p256_key_page.dart +++ b/lib/dashboard/drawer/ssi/manage_did/view/jwk_thumbprint_p256_key/jwk_thumbprint_p256_key_page.dart @@ -1,7 +1,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -78,7 +78,7 @@ class _JWKThumbprintP256KeyPageState extends State children: [ Text( l10n.jwkThumbprintP256Key, - style: Theme.of(context).textTheme.title, + style: Theme.of(context).textTheme.headlineSmall, ), const SizedBox( height: Sizes.spaceNormal, diff --git a/lib/dashboard/drawer/ssi/manage_did/widgets/did.dart b/lib/dashboard/drawer/ssi/manage_did/widgets/did.dart index 670344e44..081f3348a 100644 --- a/lib/dashboard/drawer/ssi/manage_did/widgets/did.dart +++ b/lib/dashboard/drawer/ssi/manage_did/widgets/did.dart @@ -1,6 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -16,7 +16,7 @@ class Did extends StatelessWidget { children: [ Text( l10n.did, - style: Theme.of(context).textTheme.title, + style: Theme.of(context).textTheme.headlineSmall, ), const SizedBox( height: Sizes.spaceNormal, diff --git a/lib/dashboard/drawer/ssi/manage_did/widgets/did_private_key.dart b/lib/dashboard/drawer/ssi/manage_did/widgets/did_private_key.dart index 43cce6178..2a158807c 100644 --- a/lib/dashboard/drawer/ssi/manage_did/widgets/did_private_key.dart +++ b/lib/dashboard/drawer/ssi/manage_did/widgets/did_private_key.dart @@ -1,7 +1,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/drawer/drawer.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class DidPrivateKey extends StatelessWidget { @@ -22,7 +22,7 @@ class DidPrivateKey extends StatelessWidget { ), Text( l10n.didPrivateKey, - style: Theme.of(context).textTheme.title, + style: Theme.of(context).textTheme.headlineSmall, ), const SizedBox( height: Sizes.spaceNormal, diff --git a/lib/dashboard/drawer/ssi/manage_did/widgets/export_button.dart b/lib/dashboard/drawer/ssi/manage_did/widgets/export_button.dart index 24c04b16a..8cff7340f 100644 --- a/lib/dashboard/drawer/ssi/manage_did/widgets/export_button.dart +++ b/lib/dashboard/drawer/ssi/manage_did/widgets/export_button.dart @@ -1,6 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class ExportButton extends StatelessWidget { @@ -26,7 +26,7 @@ class ExportButton extends StatelessWidget { ), Text( l10n.export, - style: Theme.of(context).textTheme.title, + style: Theme.of(context).textTheme.headlineSmall, ), ], ), diff --git a/lib/dashboard/drawer/ssi/manage_did/widgets/reveal_button.dart b/lib/dashboard/drawer/ssi/manage_did/widgets/reveal_button.dart index 3b55473c7..aa3b761d4 100644 --- a/lib/dashboard/drawer/ssi/manage_did/widgets/reveal_button.dart +++ b/lib/dashboard/drawer/ssi/manage_did/widgets/reveal_button.dart @@ -1,6 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class RevealButton extends StatelessWidget { @@ -38,7 +38,7 @@ class RevealButton extends StatelessWidget { ), Text( l10n.reveal.toUpperCase(), - style: Theme.of(context).textTheme.miniButton, + style: Theme.of(context).textTheme.bodyMedium, ), ], ), diff --git a/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/client_authentication_widget.dart b/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/client_authentication_widget.dart index 42d20fa54..8fb078117 100644 --- a/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/client_authentication_widget.dart +++ b/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/client_authentication_widget.dart @@ -1,7 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:oidc4vc/oidc4vc.dart'; @@ -36,7 +35,10 @@ class ClientAuthenticationWidget extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: 8), child: Divider( height: 0, - color: Theme.of(context).colorScheme.borderColor, + color: Theme.of(context) + .colorScheme + .onSurface + .withOpacity(0.12), ), ), ListTile( diff --git a/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/client_type_widget.dart b/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/client_type_widget.dart index 7618189c3..302cbbfa9 100644 --- a/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/client_type_widget.dart +++ b/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/client_type_widget.dart @@ -1,7 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:oidc4vc/oidc4vc.dart'; @@ -31,7 +30,10 @@ class ClientTypeWidget extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: 8), child: Divider( height: 0, - color: Theme.of(context).colorScheme.borderColor, + color: Theme.of(context) + .colorScheme + .onSurface + .withOpacity(0.12), ), ), ListTile( diff --git a/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/confidential_client_widget.dart b/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/confidential_client_widget.dart index 11430ac91..50d68c5e1 100644 --- a/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/confidential_client_widget.dart +++ b/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/confidential_client_widget.dart @@ -1,7 +1,7 @@ import 'package:altme/app/shared/shared.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -12,8 +12,8 @@ class ConfidentialClientWidget extends StatelessWidget { Widget build(BuildContext context) { final l10n = context.l10n; final color = Theme.of(context).colorScheme.primary; - final background = Theme.of(context).colorScheme.popupBackground; - final textColor = Theme.of(context).colorScheme.dialogText; + final background = Theme.of(context).colorScheme.surface; + final textColor = Theme.of(context).colorScheme.onSurface; return BlocBuilder( builder: (context, state) { final clientIdController = TextEditingController( @@ -25,7 +25,10 @@ class ConfidentialClientWidget extends StatelessWidget { text: state.model.profileSetting.selfSovereignIdentityOptions .customOidc4vcProfile.clientSecret, ); - + final clientId = state.model.profileSetting.selfSovereignIdentityOptions + .customOidc4vcProfile.clientId; + final clientSecret = state.model.profileSetting + .selfSovereignIdentityOptions.customOidc4vcProfile.clientSecret; return OptionContainer( title: l10n.confidentialClient, body: Padding( @@ -115,7 +118,7 @@ class ConfidentialClientWidget extends StatelessWidget { ), decoration: BoxDecoration( border: Border.all( - color: Theme.of(context).colorScheme.onBackground, + color: Theme.of(context).colorScheme.onSurface, ), borderRadius: const BorderRadius.all( Radius.circular( @@ -128,9 +131,8 @@ class ConfidentialClientWidget extends StatelessWidget { children: [ Expanded( child: Text( - '${l10n.clientId}: ${state.model.profileSetting.selfSovereignIdentityOptions.customOidc4vcProfile.clientId}', - style: - Theme.of(context).textTheme.drawerItemSubtitle, + '${l10n.clientId}: $clientId', + style: Theme.of(context).textTheme.titleMedium, ), ), const SizedBox(width: 8), @@ -226,7 +228,7 @@ class ConfidentialClientWidget extends StatelessWidget { ), decoration: BoxDecoration( border: Border.all( - color: Theme.of(context).colorScheme.onBackground, + color: Theme.of(context).colorScheme.onSurface, ), borderRadius: const BorderRadius.all( Radius.circular( @@ -240,9 +242,8 @@ class ConfidentialClientWidget extends StatelessWidget { Expanded( child: Text( '${l10n.clientSecret}: ' - '${state.model.profileSetting.selfSovereignIdentityOptions.customOidc4vcProfile.clientSecret}', - style: - Theme.of(context).textTheme.drawerItemSubtitle, + '$clientSecret', + style: Theme.of(context).textTheme.titleMedium, ), ), const SizedBox(width: 8), diff --git a/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/did_key_type_widget.dart b/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/did_key_type_widget.dart index 040c2d27d..f5e513d2e 100644 --- a/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/did_key_type_widget.dart +++ b/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/did_key_type_widget.dart @@ -1,7 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:oidc4vc/oidc4vc.dart'; @@ -35,7 +34,10 @@ class DidKeyTypeWidget extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: 8), child: Divider( height: 0, - color: Theme.of(context).colorScheme.borderColor, + color: Theme.of(context) + .colorScheme + .onSurface + .withOpacity(0.12), ), ), ListTile( diff --git a/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/draft_type_widget.dart b/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/draft_type_widget.dart index 5384465d1..d76ad74f6 100644 --- a/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/draft_type_widget.dart +++ b/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/draft_type_widget.dart @@ -1,7 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:oidc4vc/oidc4vc.dart'; @@ -31,7 +30,10 @@ class DraftTypeWidget extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: 8), child: Divider( height: 0, - color: Theme.of(context).colorScheme.borderColor, + color: Theme.of(context) + .colorScheme + .onSurface + .withOpacity(0.12), ), ), ListTile( diff --git a/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/option_container.dart b/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/option_container.dart index 1c46885b5..f6e8c3563 100644 --- a/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/option_container.dart +++ b/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/option_container.dart @@ -1,5 +1,5 @@ import 'package:altme/app/app.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class OptionContainer extends StatelessWidget { @@ -21,7 +21,7 @@ class OptionContainer extends StatelessWidget { padding: const EdgeInsets.all(Sizes.spaceSmall), margin: const EdgeInsets.all(Sizes.spaceXSmall), decoration: BoxDecoration( - color: Theme.of(context).colorScheme.drawerSurface, + color: Theme.of(context).colorScheme.surface, borderRadius: const BorderRadius.all( Radius.circular(Sizes.largeRadius), ), @@ -37,13 +37,13 @@ class OptionContainer extends StatelessWidget { children: [ Text( title, - style: Theme.of(context).textTheme.drawerItemTitle, + style: Theme.of(context).textTheme.titleLarge, ), if (subtitle != null) ...[ const SizedBox(height: 10), Text( subtitle!, - style: Theme.of(context).textTheme.drawerItemSubtitle, + style: Theme.of(context).textTheme.titleMedium, ), ], ], diff --git a/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/proof_header_widget.dart b/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/proof_header_widget.dart index 121f67bab..a1e62c379 100644 --- a/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/proof_header_widget.dart +++ b/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/proof_header_widget.dart @@ -1,7 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:oidc4vc/oidc4vc.dart'; @@ -31,7 +30,10 @@ class ProofHeaderWidget extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: 8), child: Divider( height: 0, - color: Theme.of(context).colorScheme.borderColor, + color: Theme.of(context) + .colorScheme + .onSurface + .withOpacity(0.12), ), ), ListTile( diff --git a/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/proof_type_widget.dart b/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/proof_type_widget.dart index fc509e13d..98af2898d 100644 --- a/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/proof_type_widget.dart +++ b/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/proof_type_widget.dart @@ -1,7 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:oidc4vc/oidc4vc.dart'; @@ -31,7 +30,10 @@ class ProofTypeWidget extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: 8), child: Divider( height: 0, - color: Theme.of(context).colorScheme.borderColor, + color: Theme.of(context) + .colorScheme + .onSurface + .withOpacity(0.12), ), ), ListTile( diff --git a/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/vc_format_widget.dart b/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/vc_format_widget.dart index ee02be9be..868cc7e7c 100644 --- a/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/vc_format_widget.dart +++ b/lib/dashboard/drawer/ssi/oidc4vc_settngs/widget/vc_format_widget.dart @@ -1,7 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:oidc4vc/oidc4vc.dart'; @@ -31,7 +30,10 @@ class VCFormatWidget extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: 8), child: Divider( height: 0, - color: Theme.of(context).colorScheme.borderColor, + color: Theme.of(context) + .colorScheme + .onSurface + .withOpacity(0.12), ), ), ListTile( diff --git a/lib/dashboard/drawer/ssi/restore/restore_credential/view/restore_credential_page.dart b/lib/dashboard/drawer/ssi/restore/restore_credential/view/restore_credential_page.dart index f3bb161ab..57c306ff1 100644 --- a/lib/dashboard/drawer/ssi/restore/restore_credential/view/restore_credential_page.dart +++ b/lib/dashboard/drawer/ssi/restore/restore_credential/view/restore_credential_page.dart @@ -5,7 +5,7 @@ import 'package:altme/credentials/credentials.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; import 'package:altme/onboarding/onboarding.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:altme/wallet/wallet.dart'; import 'package:cryptocurrency_keys/cryptocurrency_keys.dart'; import 'package:device_info_plus/device_info_plus.dart'; @@ -119,7 +119,7 @@ class _RestoreCredentialViewState extends State { Text( l10n.restoreCredentialStep2Title, textAlign: TextAlign.center, - style: Theme.of(context).textTheme.subtitle3, + style: Theme.of(context).textTheme.labelMedium, ), const SizedBox( height: Sizes.spaceNormal, @@ -158,8 +158,8 @@ class _RestoreCredentialViewState extends State { Future _pickRestoreFile() async { final l10n = context.l10n; - DeviceInfoPlugin deviceInfo = DeviceInfoPlugin(); - AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo; + final DeviceInfoPlugin deviceInfo = DeviceInfoPlugin(); + final AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo; /// storage permission has changed with android 13 late final PermissionStatus storagePermission; diff --git a/lib/dashboard/drawer/ssi/restore/restore_credential/view/restore_polygonid_credential_page.dart b/lib/dashboard/drawer/ssi/restore/restore_credential/view/restore_polygonid_credential_page.dart index ae93490ed..449c2e9da 100644 --- a/lib/dashboard/drawer/ssi/restore/restore_credential/view/restore_polygonid_credential_page.dart +++ b/lib/dashboard/drawer/ssi/restore/restore_credential/view/restore_polygonid_credential_page.dart @@ -5,7 +5,7 @@ import 'package:altme/credentials/credentials.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; import 'package:altme/onboarding/onboarding.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:altme/wallet/wallet.dart'; import 'package:cryptocurrency_keys/cryptocurrency_keys.dart'; import 'package:file_picker/file_picker.dart'; @@ -120,7 +120,7 @@ class _RestorePolygonIdCredentialViewState Text( l10n.restoreCredentialStep2Title, textAlign: TextAlign.center, - style: Theme.of(context).textTheme.subtitle3, + style: Theme.of(context).textTheme.labelMedium, ), const SizedBox( height: Sizes.spaceNormal, diff --git a/lib/dashboard/drawer/ssi/restore/restore_credential/widgets/upload_file.dart b/lib/dashboard/drawer/ssi/restore/restore_credential/widgets/upload_file.dart index 92d21670c..f2e4ad094 100644 --- a/lib/dashboard/drawer/ssi/restore/restore_credential/widgets/upload_file.dart +++ b/lib/dashboard/drawer/ssi/restore/restore_credential/widgets/upload_file.dart @@ -2,7 +2,7 @@ import 'package:altme/app/shared/constants/icon_strings.dart'; import 'package:altme/app/shared/constants/sizes.dart'; import 'package:altme/app/shared/widget/widget.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:dotted_border/dotted_border.dart'; import 'package:flutter/material.dart'; @@ -22,16 +22,15 @@ class UploadFile extends StatelessWidget { return DottedBorder( borderType: BorderType.RRect, radius: const Radius.circular(Sizes.smallRadius), - color: Theme.of(context).colorScheme.cardHighlighted, + color: Theme.of(context).colorScheme.surface, child: InkWell( onTap: onTap, child: Container( alignment: Alignment.center, height: 85, decoration: BoxDecoration( - color: filePath != null - ? Theme.of(context).colorScheme.cardHighlighted - : null, + color: + filePath != null ? Theme.of(context).colorScheme.surface : null, borderRadius: const BorderRadius.all( Radius.circular(Sizes.smallRadius), ), @@ -57,7 +56,7 @@ class UploadFile extends StatelessWidget { MyText( filePath == null ? l10n.uploadFile : filePath!.split('/').last, minFontSize: 12, - style: Theme.of(context).textTheme.uploadFileTitle, + style: Theme.of(context).textTheme.headlineSmall, ), ], ), diff --git a/lib/dashboard/drawer/ssi/restore/restore_credential_mnemonics/view/restore_credential_mnemonic_page.dart b/lib/dashboard/drawer/ssi/restore/restore_credential_mnemonics/view/restore_credential_mnemonic_page.dart index c7c6809ba..6569e3a6e 100644 --- a/lib/dashboard/drawer/ssi/restore/restore_credential_mnemonics/view/restore_credential_mnemonic_page.dart +++ b/lib/dashboard/drawer/ssi/restore/restore_credential_mnemonics/view/restore_credential_mnemonic_page.dart @@ -2,7 +2,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/drawer/drawer.dart'; import 'package:altme/l10n/l10n.dart'; import 'package:altme/onboarding/onboarding.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:secure_storage/secure_storage.dart'; @@ -115,13 +115,13 @@ class _RestoreCredentialMnemonicViewState Text( l10n.restoreCredentialStep1Title, textAlign: TextAlign.left, - style: Theme.of(context).textTheme.subtitle3, + style: Theme.of(context).textTheme.labelMedium, ), const SizedBox(height: 32), BaseTextField( hint: l10n.restorePhraseTextFieldHint, fillColor: Colors.transparent, - hintStyle: Theme.of(context).textTheme.hintTextFieldStyle, + hintStyle: Theme.of(context).textTheme.bodyMedium, controller: mnemonicController, error: state.isTextFieldEdited && !state.isMnemonicValid ? l10n.recoveryMnemonicError diff --git a/lib/dashboard/drawer/ssi/restore/src/view/restore_menu.dart b/lib/dashboard/drawer/ssi/restore/src/view/restore_menu.dart index 083222eb1..50d34bc43 100644 --- a/lib/dashboard/drawer/ssi/restore/src/view/restore_menu.dart +++ b/lib/dashboard/drawer/ssi/restore/src/view/restore_menu.dart @@ -1,7 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -28,7 +27,7 @@ class RestoreView extends StatelessWidget { Widget build(BuildContext context) { final l10n = context.l10n; return Drawer( - backgroundColor: Theme.of(context).colorScheme.drawerBackground, + backgroundColor: Theme.of(context).colorScheme.surface, child: SafeArea( child: SingleChildScrollView( child: Padding( diff --git a/lib/dashboard/drawer/ssi/src/view/ssi_menu.dart b/lib/dashboard/drawer/ssi/src/view/ssi_menu.dart index 7766cc1f1..76f95e041 100644 --- a/lib/dashboard/drawer/ssi/src/view/ssi_menu.dart +++ b/lib/dashboard/drawer/ssi/src/view/ssi_menu.dart @@ -2,7 +2,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/dashboard/drawer/ssi/manage_did/view/did_menu.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -37,7 +36,7 @@ class SSIView extends StatelessWidget { .selfSovereignIdentityOptions .displayManageDecentralizedId; return BasePage( - backgroundColor: Theme.of(context).colorScheme.drawerBackground, + backgroundColor: Theme.of(context).colorScheme.surface, useSafeArea: true, scrollView: true, titleAlignment: Alignment.topCenter, diff --git a/lib/dashboard/drawer/ssi/trust_framework/view/trust_framework_page.dart b/lib/dashboard/drawer/ssi/trust_framework/view/trust_framework_page.dart index 061ae178a..4bd2a6878 100644 --- a/lib/dashboard/drawer/ssi/trust_framework/view/trust_framework_page.dart +++ b/lib/dashboard/drawer/ssi/trust_framework/view/trust_framework_page.dart @@ -2,7 +2,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; import 'package:altme/polygon_id/cubit/polygon_id_cubit.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -47,7 +47,7 @@ class TrustFrameworkPage extends StatelessWidget { child: Text( l10n.trustFrameworkDescription, textAlign: TextAlign.center, - style: Theme.of(context).textTheme.bodySmall3, + style: Theme.of(context).textTheme.bodyMedium, ), ), const SizedBox(height: 15), @@ -55,7 +55,7 @@ class TrustFrameworkPage extends StatelessWidget { padding: const EdgeInsets.only(left: Sizes.spaceSmall), child: Text( l10n.polygon, - style: Theme.of(context).textTheme.subtitle3, + style: Theme.of(context).textTheme.labelMedium, ), ), GroupedSection( @@ -76,7 +76,10 @@ class TrustFrameworkPage extends StatelessWidget { horizontal: Sizes.spaceSmall, ), child: Divider( - color: Theme.of(context).colorScheme.borderColor, + color: Theme.of(context) + .colorScheme + .onSurface + .withOpacity(0.12), ), ), IssuerVerifierSelector( @@ -96,7 +99,7 @@ class TrustFrameworkPage extends StatelessWidget { padding: const EdgeInsets.only(left: Sizes.spaceSmall), child: Text( l10n.ebsi, - style: Theme.of(context).textTheme.subtitle3, + style: Theme.of(context).textTheme.labelMedium, ), ), const GroupedSection( diff --git a/lib/dashboard/drawer/ssi/trust_framework/widgets/issuer_verifier_selector.dart b/lib/dashboard/drawer/ssi/trust_framework/widgets/issuer_verifier_selector.dart index 83fbffaeb..2a67b7236 100644 --- a/lib/dashboard/drawer/ssi/trust_framework/widgets/issuer_verifier_selector.dart +++ b/lib/dashboard/drawer/ssi/trust_framework/widgets/issuer_verifier_selector.dart @@ -1,6 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -27,7 +27,7 @@ class IssuerVerifierSelector extends StatelessWidget { onTap: onTap, title: Text( title, - style: Theme.of(context).textTheme.radioOption, + style: Theme.of(context).textTheme.titleMedium, ), trailing: Icon( isChecked ? Icons.check_circle : Icons.circle_outlined, diff --git a/lib/dashboard/drawer/wallet_security/advanced_security_settings/view/advanced_security_settings_menu.dart b/lib/dashboard/drawer/wallet_security/advanced_security_settings/view/advanced_security_settings_menu.dart index d1106c6b1..f53bbd2c6 100644 --- a/lib/dashboard/drawer/wallet_security/advanced_security_settings/view/advanced_security_settings_menu.dart +++ b/lib/dashboard/drawer/wallet_security/advanced_security_settings/view/advanced_security_settings_menu.dart @@ -1,7 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -30,7 +29,7 @@ class AdvancedSecuritySettingsView extends StatelessWidget { return BlocBuilder( builder: (context, state) { return Drawer( - backgroundColor: Theme.of(context).colorScheme.drawerBackground, + backgroundColor: Theme.of(context).colorScheme.surface, child: SafeArea( child: SingleChildScrollView( child: Padding( diff --git a/lib/dashboard/drawer/wallet_security/recovery_key/view/key_verified_page.dart b/lib/dashboard/drawer/wallet_security/recovery_key/view/key_verified_page.dart index 9872361f9..a105eff5b 100644 --- a/lib/dashboard/drawer/wallet_security/recovery_key/view/key_verified_page.dart +++ b/lib/dashboard/drawer/wallet_security/recovery_key/view/key_verified_page.dart @@ -17,12 +17,12 @@ class KeyVerifiedPage extends StatelessWidget { @override Widget build(BuildContext context) { - return const WalletReadyView(); + return const KeyVerifiedView(); } } -class WalletReadyView extends StatelessWidget { - const WalletReadyView({super.key}); +class KeyVerifiedView extends StatelessWidget { + const KeyVerifiedView({super.key}); @override Widget build(BuildContext context) { diff --git a/lib/dashboard/drawer/wallet_security/recovery_key/view/recovery_key_page.dart b/lib/dashboard/drawer/wallet_security/recovery_key/view/recovery_key_page.dart index 5e8b9daf0..58a1e03e6 100644 --- a/lib/dashboard/drawer/wallet_security/recovery_key/view/recovery_key_page.dart +++ b/lib/dashboard/drawer/wallet_security/recovery_key/view/recovery_key_page.dart @@ -2,7 +2,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; import 'package:altme/onboarding/onboarding.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:altme/wallet/cubit/wallet_cubit.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -117,13 +117,13 @@ class _RecoveryKeyViewState extends State Text( l10n.genPhraseInstruction, textAlign: TextAlign.center, - style: Theme.of(context).textTheme.messageTitle, + style: Theme.of(context).textTheme.titleLarge, ), const SizedBox(height: 20), Text( l10n.genPhraseExplanation, textAlign: TextAlign.center, - style: Theme.of(context).textTheme.messageSubtitle, + style: Theme.of(context).textTheme.titleLarge, ), ], ), diff --git a/lib/dashboard/drawer/wallet_security/src/view/wallet_security_menu.dart b/lib/dashboard/drawer/wallet_security/src/view/wallet_security_menu.dart index da236fc3e..df5275e3d 100644 --- a/lib/dashboard/drawer/wallet_security/src/view/wallet_security_menu.dart +++ b/lib/dashboard/drawer/wallet_security/src/view/wallet_security_menu.dart @@ -3,7 +3,6 @@ import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; import 'package:altme/onboarding/protect_wallet/view/protect_wallet_page.dart'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -32,7 +31,7 @@ class WalletSecurityView extends StatelessWidget { return BlocBuilder( builder: (context, state) { return BasePage( - backgroundColor: Theme.of(context).colorScheme.drawerBackground, + backgroundColor: Theme.of(context).colorScheme.surface, useSafeArea: true, scrollView: true, titleAlignment: Alignment.topCenter, diff --git a/lib/dashboard/drawer/wallet_settings/view/wallet_settings_menu.dart b/lib/dashboard/drawer/wallet_settings/view/wallet_settings_menu.dart index 5bcd9834a..d119899a3 100644 --- a/lib/dashboard/drawer/wallet_settings/view/wallet_settings_menu.dart +++ b/lib/dashboard/drawer/wallet_settings/view/wallet_settings_menu.dart @@ -1,6 +1,5 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; class WalletSettingsMenu extends StatelessWidget { @@ -25,7 +24,7 @@ class WalletSettingsMenuView extends StatelessWidget { @override Widget build(BuildContext context) { return BasePage( - backgroundColor: Theme.of(context).colorScheme.drawerBackground, + backgroundColor: Theme.of(context).colorScheme.surface, useSafeArea: true, scrollView: true, titleAlignment: Alignment.topCenter, diff --git a/lib/dashboard/drawer/wallet_settings/widget/language_selector_widget.dart b/lib/dashboard/drawer/wallet_settings/widget/language_selector_widget.dart index f4048b3a2..06cda769c 100644 --- a/lib/dashboard/drawer/wallet_settings/widget/language_selector_widget.dart +++ b/lib/dashboard/drawer/wallet_settings/widget/language_selector_widget.dart @@ -3,7 +3,7 @@ import 'package:altme/app/shared/enum/type/language_type.dart'; import 'package:altme/l10n/l10n.dart'; import 'package:altme/lang/cubit/lang_cubit.dart'; import 'package:altme/lang/cubit/lang_state.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -23,7 +23,7 @@ class LanguageSelectorWidget extends StatelessWidget { padding: const EdgeInsets.all(Sizes.spaceSmall), margin: const EdgeInsets.all(Sizes.spaceXSmall), decoration: BoxDecoration( - color: Theme.of(context).colorScheme.drawerSurface, + color: Theme.of(context).colorScheme.surface, borderRadius: const BorderRadius.all( Radius.circular(Sizes.largeRadius), ), @@ -39,7 +39,7 @@ class LanguageSelectorWidget extends StatelessWidget { children: [ Text( l10n.walletSettingsDescription, - style: Theme.of(context).textTheme.drawerItemSubtitle, + style: Theme.of(context).textTheme.titleMedium, ), ], ), @@ -61,8 +61,10 @@ class LanguageSelectorWidget extends StatelessWidget { const EdgeInsets.symmetric(horizontal: 8), child: Divider( height: 0, - color: - Theme.of(context).colorScheme.borderColor, + color: Theme.of(context) + .colorScheme + .onSurface + .withOpacity(0.12), ), ), ListTile( diff --git a/lib/dashboard/home/home/cubit/home_cubit.dart b/lib/dashboard/home/home/cubit/home_cubit.dart index 0f4f8a470..e40e10b81 100644 --- a/lib/dashboard/home/home/cubit/home_cubit.dart +++ b/lib/dashboard/home/home/cubit/home_cubit.dart @@ -271,158 +271,4 @@ class HomeCubit extends Cubit { Future launchUrl({String? link}) async { await LaunchUrl.launch(link ?? state.link!); } - - // Future periodicCheckRewardOnTezosBlockchain() async { - // Timer.periodic(const Duration(minutes: 1), (timer) async { - // List walletAddresses = []; - // final String? savedCryptoAccount = - // await secureStorageProvider.get(SecureStorageKeys.cryptoAccount); - - // if (savedCryptoAccount != null && savedCryptoAccount.isNotEmpty) { - // //load all the content of walletAddress - // final cryptoAccountJson = - // jsonDecode(savedCryptoAccount) as Map; - // final CryptoAccount cryptoAccount = - // CryptoAccount.fromJson(cryptoAccountJson); - - // walletAddresses = - // cryptoAccount.data.map((e) => e.walletAddress).toList(); - // } - // if (walletAddresses.isEmpty) return; - // try { - // final tezosWalletAddresses = - // walletAddresses.where((e) => e.startsWith('tz')).toList(); - // if (tezosWalletAddresses.isEmpty) return; - // await checkRewards(tezosWalletAddresses); - // } catch (e, s) { - // getLogger('HomeCubit') - // .e('error in checking for reward , error: $e, stack: $s'); - // } - // }); - // } - - // Future checkRewards(List walletAddresses) async { - // for (int i = 0; i < walletAddresses.length; i++) { - // await checkUNOReward(walletAddresses[i]); - // await checkXTZReward(walletAddresses[i]); - // } - // } - - // Future checkUNOReward(String walletAddress) async { - // getLogger('HomeCubit').i('check for UNO reward'); - // final response = await client.get( - // '${Urls.tzktMainnetUrl}/v1/tokens/transfers', - // queryParameters: { - // 'from': 'tz1YtKsJMx5FqhULTDzNxs9r9QYHBGsmz58o', // tezotopia - // 'to': walletAddress, - // 'token.contract.eq': 'KT1ErKVqEhG9jxXgUG2KGLW3bNM7zXHX8SDF', // UNO - // 'sort.desc': 'timestamp', - // }, - // ) as List; - - // if (response.isEmpty) { - // return; - // } - - // final operations = response - // .map( - // (dynamic e) => OperationModel.fromFa2Json(e as Map), - // ) - // .toList(); - - // final String? lastNotifiedRewardId = await secureStorageProvider.get( - // SecureStorageKeys.lastNotifiedUNORewardId + walletAddress, - // ); - - // final lastOperation = operations.first; //operations sorted by time in api - // if (lastOperation.id.toString() == lastNotifiedRewardId) { - // return; - // } else { - // // save the operation id to storage - // await secureStorageProvider.set( - // SecureStorageKeys.lastNotifiedUNORewardId + walletAddress, - // lastOperation.id.toString(), - // ); - - // emit( - // state.copyWith( - // status: AppStatus.gotTokenReward, - // tokenReward: TokenReward( - // amount: lastOperation.calcAmount( - // decimal: 9, //UNO - // value: lastOperation.amount.toString(), - // ), - // txId: lastOperation.hash, - // counter: lastOperation.counter, - // account: walletAddress, - // origin: - // 'Tezotopia Membership Card', // TODO(all): dynamic text later - // symbol: 'UNO', - // name: 'Unobtanium', - // ), - // ), - // ); - // } - // } - - // Future checkXTZReward(String walletAddress) async { - // getLogger('HomeCubit').i('check for XTZ reward'); - - // final result = await client.get( - // '${Urls.tzktMainnetUrl}/v1/operations/transactions', - // queryParameters: { - // 'sender': 'tz1YtKsJMx5FqhULTDzNxs9r9QYHBGsmz58o', // tezotopia - // 'target': walletAddress, - // 'amount.gt': 0, - // }, - // ) as List; - - // if (result.isEmpty) { - // return; - // } - - // final operations = result - // .map( - // (dynamic e) => OperationModel.fromJson(e as Map), - // ) - // .toList(); - // //sort for last transaction at first - // operations.sort( - // (a, b) => b.dateTime.compareTo(a.dateTime), - // ); - - // final String? lastNotifiedRewardId = await secureStorageProvider.get( - // SecureStorageKeys.lastNotifiedXTZRewardId + walletAddress, - // ); - - // final lastOperation = operations.first; //operations sorted by time in api - // if (lastOperation.id.toString() == lastNotifiedRewardId) { - // return; - // } else { - // // save the operation id to storage - // await secureStorageProvider.set( - // SecureStorageKeys.lastNotifiedXTZRewardId + walletAddress, - // lastOperation.id.toString(), - // ); - - // emit( - // state.copyWith( - // status: AppStatus.gotTokenReward, - // tokenReward: TokenReward( - // amount: lastOperation.calcAmount( - // decimal: 6, //XTZ - // value: lastOperation.amount.toString(), - // ), - // account: walletAddress, - // txId: lastOperation.hash, - // counter: lastOperation.counter, - // origin: - // 'Tezotopia Membership Card', // TODO(all): dynamic text later - // symbol: 'XTZ', - // name: 'Tezos', - // ), - // ), - // ); - // } - // } } diff --git a/lib/dashboard/home/home/widgets/get_cards_widget.dart b/lib/dashboard/home/home/widgets/get_cards_widget.dart index f8a12486a..eebcffc5f 100644 --- a/lib/dashboard/home/home/widgets/get_cards_widget.dart +++ b/lib/dashboard/home/home/widgets/get_cards_widget.dart @@ -2,7 +2,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/issuer_websites_page/issuer_websites.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -39,7 +39,7 @@ class GetCardsWidget extends StatelessWidget { children: [ Text( l10n.getCards.toUpperCase(), - style: Theme.of(context).textTheme.getCardsButton, + style: Theme.of(context).textTheme.bodyMedium, ), const SizedBox(width: 8), Image.asset( diff --git a/lib/dashboard/home/home/widgets/qr_icon.dart b/lib/dashboard/home/home/widgets/qr_icon.dart index 90364fcc1..ee716082a 100644 --- a/lib/dashboard/home/home/widgets/qr_icon.dart +++ b/lib/dashboard/home/home/widgets/qr_icon.dart @@ -1,7 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -46,7 +45,7 @@ class QRIcon extends StatelessWidget { height: 80, width: 80, decoration: BoxDecoration( - color: Theme.of(context).colorScheme.qrScanBackground, + color: Theme.of(context).colorScheme.surface, borderRadius: BorderRadius.circular(50), ), ), @@ -58,11 +57,12 @@ class QRIcon extends StatelessWidget { borderRadius: BorderRadius.circular(50), boxShadow: [ BoxShadow( - color: Theme.of(context).colorScheme.qrScanInnerShadow, + color: + Theme.of(context).colorScheme.shadow.withOpacity(0.16), blurRadius: 25, spreadRadius: 5, offset: const Offset(0, 10), - ) + ), ], ), ), diff --git a/lib/dashboard/home/home/widgets/token_reward_dialog.dart b/lib/dashboard/home/home/widgets/token_reward_dialog.dart index 3a33d3c5b..253b39423 100644 --- a/lib/dashboard/home/home/widgets/token_reward_dialog.dart +++ b/lib/dashboard/home/home/widgets/token_reward_dialog.dart @@ -1,7 +1,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/home/home/home.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; @@ -29,7 +29,7 @@ class TokenRewardDialog extends StatelessWidget { Widget build(BuildContext context) { final l10n = context.l10n; return AlertDialog( - backgroundColor: Theme.of(context).colorScheme.popupBackground, + backgroundColor: Theme.of(context).colorScheme.surface, surfaceTintColor: Colors.transparent, contentPadding: const EdgeInsets.symmetric( horizontal: Sizes.spaceNormal, @@ -46,7 +46,7 @@ class TokenRewardDialog extends StatelessWidget { const SizedBox(height: Sizes.spaceSmall), Text( l10n.rewardDialogTitle, - style: Theme.of(context).textTheme.defaultDialogTitle.copyWith( + style: Theme.of(context).textTheme.headlineMedium!.copyWith( color: Theme.of(context).colorScheme.primary, ), textAlign: TextAlign.center, @@ -55,16 +55,15 @@ class TokenRewardDialog extends StatelessWidget { Text.rich( TextSpan( text: l10n.rewardDialogDescPart1, - style: Theme.of(context).textTheme.defaultDialogBody, + style: Theme.of(context).textTheme.bodyMedium, children: [ TextSpan( text: ''' ${tokenReward.amount.toString().formatNumber} ${tokenReward.symbol} ''', - style: - Theme.of(context).textTheme.defaultDialogBody.copyWith( - fontWeight: FontWeight.w900, - color: Theme.of(context).colorScheme.primary, - ), + style: Theme.of(context).textTheme.bodyMedium!.copyWith( + fontWeight: FontWeight.w900, + color: Theme.of(context).colorScheme.primary, + ), ), TextSpan( text: '${l10n.rewardDialogDescPart2} : ', @@ -72,10 +71,9 @@ class TokenRewardDialog extends StatelessWidget { TextSpan( text: '''${tokenReward.account.substring(0, 6)}...${tokenReward.account.substring(tokenReward.account.length - 6)}''', - style: - Theme.of(context).textTheme.defaultDialogBody.copyWith( - decoration: TextDecoration.underline, - ), + style: Theme.of(context).textTheme.bodyMedium!.copyWith( + decoration: TextDecoration.underline, + ), recognizer: TapGestureRecognizer() ..onTap = () { LaunchUrl.launch( diff --git a/lib/dashboard/home/home/widgets/wallet_dialog.dart b/lib/dashboard/home/home/widgets/wallet_dialog.dart index 63cf03f99..41daf6cb5 100644 --- a/lib/dashboard/home/home/widgets/wallet_dialog.dart +++ b/lib/dashboard/home/home/widgets/wallet_dialog.dart @@ -1,7 +1,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/l10n/l10n.dart'; import 'package:altme/onboarding/onboarding.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class WalletDialog extends StatelessWidget { @@ -13,7 +13,7 @@ class WalletDialog extends StatelessWidget { Widget build(BuildContext context) { final l10n = context.l10n; return AlertDialog( - backgroundColor: Theme.of(context).colorScheme.popupBackground, + backgroundColor: Theme.of(context).colorScheme.surface, surfaceTintColor: Colors.transparent, contentPadding: const EdgeInsets.symmetric(horizontal: 20, vertical: 15), shape: const RoundedRectangleBorder( @@ -29,14 +29,14 @@ class WalletDialog extends StatelessWidget { IconStrings.cardSend, width: 50, height: 50, - color: Theme.of(context).colorScheme.dialogText, + color: Theme.of(context).colorScheme.onSurface, ), const SizedBox(height: 30), Text( l10n.createTitle, - style: Theme.of(context).textTheme.defaultDialogTitle.copyWith( + style: Theme.of(context).textTheme.headlineMedium!.copyWith( fontSize: 18, - color: Theme.of(context).colorScheme.dialogText, + color: Theme.of(context).colorScheme.onSurface, ), textAlign: TextAlign.center, ), @@ -45,8 +45,8 @@ class WalletDialog extends StatelessWidget { l10n.createSubtitle, style: Theme.of(context) .textTheme - .defaultDialogSubtitle - .copyWith(color: Theme.of(context).colorScheme.dialogText), + .titleLarge! + .copyWith(color: Theme.of(context).colorScheme.onSurface), textAlign: TextAlign.center, ), const SizedBox(height: 30), @@ -76,12 +76,12 @@ class WalletDialog extends StatelessWidget { verticalSpacing: 12, fontSize: 13, borderRadius: 8, - textColor: Theme.of(context).colorScheme.lightPurple, + textColor: Theme.of(context).colorScheme.secondary, borderColor: Theme.of(context) .colorScheme - .defualtDialogCancelButtonBorderColor, - backgroundColor: - Theme.of(context).colorScheme.popupBackground, + .onSurface + .withOpacity(0.2), + backgroundColor: Theme.of(context).colorScheme.surface, onPressed: () { Navigator.pop(context); Navigator.of(context).push( diff --git a/lib/dashboard/home/tab_bar/credentials/credential_qr/view/credential_qr_page.dart b/lib/dashboard/home/tab_bar/credentials/credential_qr/view/credential_qr_page.dart index f5ce895ad..2233fa16e 100644 --- a/lib/dashboard/home/tab_bar/credentials/credential_qr/view/credential_qr_page.dart +++ b/lib/dashboard/home/tab_bar/credentials/credential_qr/view/credential_qr_page.dart @@ -3,7 +3,7 @@ import 'dart:convert'; import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:qr_flutter/qr_flutter.dart'; @@ -44,7 +44,7 @@ class CredentialQrPage extends StatelessWidget { child: QrImageView( data: data, size: 200, - foregroundColor: Theme.of(context).colorScheme.onBackground, + foregroundColor: Theme.of(context).colorScheme.onSurface, ), ), const SizedBox(height: 10), @@ -58,7 +58,7 @@ class CredentialQrPage extends StatelessWidget { }, child: Text( l10n.copyToClipboard, - style: Theme.of(context).textTheme.copyToClipBoard, + style: Theme.of(context).textTheme.bodyMedium, ), ), const SizedBox(height: Sizes.spaceSmall), diff --git a/lib/dashboard/home/tab_bar/credentials/detail/view/credentials_details_page.dart b/lib/dashboard/home/tab_bar/credentials/detail/view/credentials_details_page.dart index 5a1618ee3..8777b6a4d 100644 --- a/lib/dashboard/home/tab_bar/credentials/detail/view/credentials_details_page.dart +++ b/lib/dashboard/home/tab_bar/credentials/detail/view/credentials_details_page.dart @@ -9,7 +9,6 @@ import 'package:altme/l10n/l10n.dart'; import 'package:altme/polygon_id/polygon_id.dart'; import 'package:altme/selective_disclosure/selective_disclosure.dart'; import 'package:altme/selective_disclosure/widget/display_selective_disclosure.dart'; -import 'package:altme/theme/theme.dart'; import 'package:altme/wallet/cubit/wallet_cubit.dart'; import 'package:did_kit/did_kit.dart'; import 'package:dio/dio.dart'; diff --git a/lib/dashboard/home/tab_bar/credentials/detail/widgets/credential_active_status.dart b/lib/dashboard/home/tab_bar/credentials/detail/widgets/credential_active_status.dart index 640830178..b6534cd02 100644 --- a/lib/dashboard/home/tab_bar/credentials/detail/widgets/credential_active_status.dart +++ b/lib/dashboard/home/tab_bar/credentials/detail/widgets/credential_active_status.dart @@ -1,5 +1,5 @@ import 'package:altme/app/app.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class CredentialActiveStatus extends StatelessWidget { @@ -19,11 +19,8 @@ class CredentialActiveStatus extends StatelessWidget { children: [ Text( credentialStatus?.message(context) ?? '', - style: Theme.of(context) - .textTheme - .credentialFieldDescription - .copyWith( - color: Theme.of(context).colorScheme.valueColor, + style: Theme.of(context).textTheme.bodyMedium!.copyWith( + color: Theme.of(context).colorScheme.onSurface, ), ), const SizedBox(width: 5), diff --git a/lib/dashboard/home/tab_bar/credentials/detail/widgets/credential_subject_data.dart b/lib/dashboard/home/tab_bar/credentials/detail/widgets/credential_subject_data.dart index e7e50e0ff..4a0de54f2 100644 --- a/lib/dashboard/home/tab_bar/credentials/detail/widgets/credential_subject_data.dart +++ b/lib/dashboard/home/tab_bar/credentials/detail/widgets/credential_subject_data.dart @@ -1,7 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/lang/cubit/lang_cubit.dart'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:json_path/json_path.dart'; @@ -122,8 +121,8 @@ class CredentialSubjectData extends StatelessWidget { padding: const EdgeInsets.only(top: 10), title: title, value: data, - titleColor: Theme.of(context).colorScheme.titleColor, - valueColor: Theme.of(context).colorScheme.valueColor, + titleColor: Theme.of(context).colorScheme.onSurface, + valueColor: Theme.of(context).colorScheme.onSurface, showVertically: showVertically, ); }).toList(), diff --git a/lib/dashboard/home/tab_bar/credentials/detail/widgets/deferred_credential_data.dart b/lib/dashboard/home/tab_bar/credentials/detail/widgets/deferred_credential_data.dart index ec4ad95ca..f0c832520 100644 --- a/lib/dashboard/home/tab_bar/credentials/detail/widgets/deferred_credential_data.dart +++ b/lib/dashboard/home/tab_bar/credentials/detail/widgets/deferred_credential_data.dart @@ -1,7 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; class DeferredCredentialData extends StatelessWidget { @@ -25,8 +24,8 @@ class DeferredCredentialData extends StatelessWidget { padding: const EdgeInsets.only(top: 10), title: l10n.issuer, value: credentialModel.pendingInfo!.issuer ?? '', - titleColor: Theme.of(context).colorScheme.titleColor, - valueColor: Theme.of(context).colorScheme.valueColor, + titleColor: Theme.of(context).colorScheme.onSurface, + valueColor: Theme.of(context).colorScheme.onSurface, showVertically: showVertically, ), CredentialField( @@ -35,8 +34,8 @@ class DeferredCredentialData extends StatelessWidget { value: UiDate.formatDate( credentialModel.pendingInfo!.requestedAt, ), - titleColor: Theme.of(context).colorScheme.titleColor, - valueColor: Theme.of(context).colorScheme.valueColor, + titleColor: Theme.of(context).colorScheme.onSurface, + valueColor: Theme.of(context).colorScheme.onSurface, showVertically: showVertically, ), ], diff --git a/lib/dashboard/home/tab_bar/credentials/detail/widgets/developer_details.dart b/lib/dashboard/home/tab_bar/credentials/detail/widgets/developer_details.dart index 5ef973db8..6c054deb2 100644 --- a/lib/dashboard/home/tab_bar/credentials/detail/widgets/developer_details.dart +++ b/lib/dashboard/home/tab_bar/credentials/detail/widgets/developer_details.dart @@ -1,7 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; class DeveloperDetails extends StatelessWidget { @@ -27,8 +26,8 @@ class DeveloperDetails extends StatelessWidget { credentialModel.credentialPreview.credentialSubjectModel.id ?? ''; final String type = credentialModel.credentialPreview.type.toString(); - final titleColor = Theme.of(context).colorScheme.titleColor; - final valueColor = Theme.of(context).colorScheme.valueColor; + final titleColor = Theme.of(context).colorScheme.onSurface; + final valueColor = Theme.of(context).colorScheme.onSurface; return Column( crossAxisAlignment: CrossAxisAlignment.start, diff --git a/lib/dashboard/home/tab_bar/credentials/helper_functions/get_credential_manifest_from_altme.dart b/lib/dashboard/home/tab_bar/credentials/helper_functions/get_credential_manifest_from_altme.dart index f909ffd4f..12157492c 100644 --- a/lib/dashboard/home/tab_bar/credentials/helper_functions/get_credential_manifest_from_altme.dart +++ b/lib/dashboard/home/tab_bar/credentials/helper_functions/get_credential_manifest_from_altme.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'package:credential_manifest/credential_manifest.dart'; +import 'package:dio/dio.dart'; import 'package:json_path/json_path.dart'; import 'package:oidc4vc/oidc4vc.dart'; @@ -11,6 +12,7 @@ Future getCredentialManifestFromAltMe({ final OpenIdConfiguration openIdConfiguration = await oidc4vc.getOpenIdConfig( baseUrl: 'https://issuer.talao.co', isAuthorizationServer: false, + dio: Dio(), ); final JsonPath credentialManifetPath = JsonPath(r'$..credential_manifest'); final credentialManifest = CredentialManifest.fromJson( diff --git a/lib/dashboard/home/tab_bar/credentials/linkedin_credential/generate_linkedin_qr/view/generate_linkedin_qr_page.dart b/lib/dashboard/home/tab_bar/credentials/linkedin_credential/generate_linkedin_qr/view/generate_linkedin_qr_page.dart index d11284061..0b6dcbb2b 100644 --- a/lib/dashboard/home/tab_bar/credentials/linkedin_credential/generate_linkedin_qr/view/generate_linkedin_qr_page.dart +++ b/lib/dashboard/home/tab_bar/credentials/linkedin_credential/generate_linkedin_qr/view/generate_linkedin_qr_page.dart @@ -133,12 +133,9 @@ class _GenerateLinkedinQrViewState extends State { : FractionallySizedBox( heightFactor: 0.48, widthFactor: 0.12, - child: PrettyQr( - size: 300, + child: PrettyQrView.data( data: state.qrValue!, errorCorrectLevel: QrErrorCorrectLevel.M, - typeNumber: null, - roundEdges: true, ), ), ), diff --git a/lib/dashboard/home/tab_bar/credentials/linkedin_credential/get_linkedin_info/view/get_linkedin_info_page.dart b/lib/dashboard/home/tab_bar/credentials/linkedin_credential/get_linkedin_info/view/get_linkedin_info_page.dart index 71a5a42ac..b9449e69f 100644 --- a/lib/dashboard/home/tab_bar/credentials/linkedin_credential/get_linkedin_info/view/get_linkedin_info_page.dart +++ b/lib/dashboard/home/tab_bar/credentials/linkedin_credential/get_linkedin_info/view/get_linkedin_info_page.dart @@ -1,7 +1,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -95,7 +95,7 @@ class _GetLinkedinInfoViewState extends State { height: 100, hint: 'e.g. https://www.linkedin.com/john.doe', fillColor: Colors.transparent, - hintStyle: Theme.of(context).textTheme.hintTextFieldStyle, + hintStyle: Theme.of(context).textTheme.bodyMedium, maxLines: 3, borderRadius: Sizes.normalRadius, controller: linkedInUrlController, @@ -112,7 +112,7 @@ class _GetLinkedinInfoViewState extends State { margin: const EdgeInsets.all(Sizes.spaceNormal), decoration: BoxDecoration( shape: BoxShape.circle, - color: Theme.of(context).colorScheme.checkMarkColor, + color: Theme.of(context).colorScheme.onTertiary, ), child: const Icon( Icons.check, diff --git a/lib/dashboard/home/tab_bar/credentials/list/view/home_credentials_list_page.dart b/lib/dashboard/home/tab_bar/credentials/list/view/home_credentials_list_page.dart index 7a32e9867..3c321267f 100644 --- a/lib/dashboard/home/tab_bar/credentials/list/view/home_credentials_list_page.dart +++ b/lib/dashboard/home/tab_bar/credentials/list/view/home_credentials_list_page.dart @@ -1,7 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/credentials/cubit/credentials_cubit.dart'; import 'package:altme/dashboard/dashboard.dart'; -import 'package:altme/theme/theme.dart'; import 'package:altme/wallet/wallet.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -43,7 +42,7 @@ class _HomeCredentialsListPageState extends State return BasePage( scrollView: false, padding: EdgeInsets.zero, - backgroundColor: Theme.of(context).colorScheme.transparent, + backgroundColor: Colors.transparent, body: BlocListener( listenWhen: (previous, current) { if (current.model.profileSetting.selfSovereignIdentityOptions diff --git a/lib/dashboard/home/tab_bar/credentials/list/widgets/credential_detail_tab_bar.dart b/lib/dashboard/home/tab_bar/credentials/list/widgets/credential_detail_tab_bar.dart index c7e963a23..ea565e492 100644 --- a/lib/dashboard/home/tab_bar/credentials/list/widgets/credential_detail_tab_bar.dart +++ b/lib/dashboard/home/tab_bar/credentials/list/widgets/credential_detail_tab_bar.dart @@ -1,5 +1,5 @@ import 'package:altme/app/app.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:badges/badges.dart' as badges; import 'package:flutter/material.dart'; @@ -28,10 +28,10 @@ class CredentialDetailTabbar extends StatelessWidget { showBadge: badgeCount > 0, badgeContent: Text( badgeCount.toString(), - style: Theme.of(context).textTheme.badgeStyle, + style: Theme.of(context).textTheme.bodySmall, textAlign: TextAlign.center, ), - position: badges.BadgePosition.topEnd(end: 5,top: 15), + position: badges.BadgePosition.topEnd(end: 5, top: 15), child: Container( width: double.infinity, padding: const EdgeInsets.symmetric( @@ -39,7 +39,8 @@ class CredentialDetailTabbar extends StatelessWidget { ), decoration: isSelected ? BoxDecoration( - color: Theme.of(context).colorScheme.surfaceContainer, + color: + Theme.of(context).colorScheme.surface.withOpacity(0.07), borderRadius: const BorderRadius.only( topLeft: Radius.circular(Sizes.smallRadius), topRight: Radius.circular(Sizes.smallRadius), @@ -50,10 +51,9 @@ class CredentialDetailTabbar extends StatelessWidget { child: Text( title, style: isSelected - ? textTheme.credentialManifestDescription - .copyWith(fontWeight: FontWeight.bold) - : textTheme.credentialManifestDescription - .copyWith(color: colorScheme.onTertiary), + ? textTheme.bodyMedium!.copyWith(fontWeight: FontWeight.bold) + : textTheme.bodyMedium! + .copyWith(color: colorScheme.secondaryContainer), ), ), ), diff --git a/lib/dashboard/home/tab_bar/credentials/list/widgets/credential_item_shimmer.dart b/lib/dashboard/home/tab_bar/credentials/list/widgets/credential_item_shimmer.dart index a7cb7a588..bdef1c8a7 100644 --- a/lib/dashboard/home/tab_bar/credentials/list/widgets/credential_item_shimmer.dart +++ b/lib/dashboard/home/tab_bar/credentials/list/widgets/credential_item_shimmer.dart @@ -1,5 +1,4 @@ import 'package:altme/app/app.dart'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; class CredentialItemShimmer extends StatelessWidget { @@ -8,7 +7,7 @@ class CredentialItemShimmer extends StatelessWidget { @override Widget build(BuildContext context) { return BackgroundCard( - color: Theme.of(context).colorScheme.surfaceContainer, + color: Theme.of(context).colorScheme.surface.withOpacity(0.07), padding: EdgeInsets.zero, child: const Column( crossAxisAlignment: CrossAxisAlignment.start, diff --git a/lib/dashboard/home/tab_bar/credentials/list/widgets/home_credential_category_item.dart b/lib/dashboard/home/tab_bar/credentials/list/widgets/home_credential_category_item.dart index 9589bdbd1..a05c909f4 100644 --- a/lib/dashboard/home/tab_bar/credentials/list/widgets/home_credential_category_item.dart +++ b/lib/dashboard/home/tab_bar/credentials/list/widgets/home_credential_category_item.dart @@ -1,6 +1,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; -import 'package:altme/theme/theme.dart'; +import 'package:altme/l10n/l10n.dart'; + import 'package:flutter/material.dart'; class HomeCredentialCategoryItem extends StatelessWidget { @@ -29,7 +30,7 @@ class HomeCredentialCategoryItem extends StatelessWidget { ? 1 : 0, ); - final credentialCategoryConfig = credentialCategory.config(context); + final credentialCategoryConfig = credentialCategory.config(context.l10n); return Padding( padding: margin, child: Column( @@ -40,7 +41,7 @@ class HomeCredentialCategoryItem extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 0), child: Text( credentialCategoryConfig.homeTitle, - style: Theme.of(context).textTheme.credentialCategoryTitle, + style: Theme.of(context).textTheme.headlineSmall, ), ), Padding( @@ -48,7 +49,9 @@ class HomeCredentialCategoryItem extends StatelessWidget { child: Text( credentialCategoryConfig.homeSubTitle, maxLines: 3, - style: Theme.of(context).textTheme.credentialCategorySubTitle, + style: Theme.of(context).textTheme.titleMedium!.copyWith( + color: Theme.of(context).colorScheme.secondaryContainer, + ), ), ), const SizedBox(height: 14), diff --git a/lib/dashboard/home/tab_bar/credentials/polygon_id/polygon_id_credential_offer/view/polygon_id_credential_offer_view.dart b/lib/dashboard/home/tab_bar/credentials/polygon_id/polygon_id_credential_offer/view/polygon_id_credential_offer_view.dart index 25e04a74b..8ead3e943 100644 --- a/lib/dashboard/home/tab_bar/credentials/polygon_id/polygon_id_credential_offer/view/polygon_id_credential_offer_view.dart +++ b/lib/dashboard/home/tab_bar/credentials/polygon_id/polygon_id_credential_offer/view/polygon_id_credential_offer_view.dart @@ -2,7 +2,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; import 'package:altme/polygon_id/polygon_id.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:credential_manifest/credential_manifest.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -34,8 +34,8 @@ class PolygonIdCredentialOfferPage extends StatelessWidget { Text( l10n.wouldYouLikeToAcceptThisCredentialsFromThisOrganisation, textAlign: TextAlign.center, - style: Theme.of(context).textTheme.credentialSubtitle.copyWith( - color: Theme.of(context).colorScheme.lightPurple, + style: Theme.of(context).textTheme.titleMedium!.copyWith( + color: Theme.of(context).colorScheme.secondary, ), ), const SizedBox(height: 30), diff --git a/lib/dashboard/home/tab_bar/credentials/polygon_id/polygon_id_proof/view/polygon_id_proof_view.dart b/lib/dashboard/home/tab_bar/credentials/polygon_id/polygon_id_proof/view/polygon_id_proof_view.dart index fee4613e2..802f76864 100644 --- a/lib/dashboard/home/tab_bar/credentials/polygon_id/polygon_id_proof/view/polygon_id_proof_view.dart +++ b/lib/dashboard/home/tab_bar/credentials/polygon_id/polygon_id_proof/view/polygon_id_proof_view.dart @@ -1,7 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/app_theme/app_theme.dart'; import 'package:flutter/material.dart'; import 'package:polygonid/polygonid.dart'; @@ -78,7 +77,7 @@ class PolygonIdProofPage extends StatelessWidget { child: Text( l10n.noInformationWillBeSharedFromThisCredentialMessage, textAlign: TextAlign.center, - style: Theme.of(context).textTheme.subMessage, + style: Theme.of(context).textTheme.bodyMedium, ), ), ], diff --git a/lib/dashboard/home/tab_bar/credentials/polygon_id/polygon_id_verification/view/polygon_id_verification_view.dart b/lib/dashboard/home/tab_bar/credentials/polygon_id/polygon_id_verification/view/polygon_id_verification_view.dart index ac852552f..009b2f1aa 100644 --- a/lib/dashboard/home/tab_bar/credentials/polygon_id/polygon_id_verification/view/polygon_id_verification_view.dart +++ b/lib/dashboard/home/tab_bar/credentials/polygon_id/polygon_id_verification/view/polygon_id_verification_view.dart @@ -2,7 +2,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; import 'package:altme/polygon_id/polygon_id.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:polygonid/polygonid.dart'; @@ -68,7 +68,6 @@ class _PolygonIdVerificationViewState extends State { Widget build(BuildContext context) { final l10n = context.l10n; final body = widget.iden3MessageEntity.body as AuthBodyRequest; - // TODO(all): change UI return BlocConsumer( listener: (context, state) { if (state.status == AppStatus.loading) { @@ -96,10 +95,9 @@ class _PolygonIdVerificationViewState extends State { Text( l10n.thisOrganisationRequestsThisInformation, textAlign: TextAlign.center, - style: - Theme.of(context).textTheme.credentialSubtitle.copyWith( - color: Theme.of(context).colorScheme.lightPurple, - ), + style: Theme.of(context).textTheme.titleMedium!.copyWith( + color: Theme.of(context).colorScheme.secondary, + ), ), const SizedBox(height: 10), if (body.scope != null) diff --git a/lib/dashboard/home/tab_bar/credentials/present/pick/credential_manifest/helpers/apply_submission_requirements.dart b/lib/dashboard/home/tab_bar/credentials/present/pick/credential_manifest/helpers/apply_submission_requirements.dart index f6e561a9f..f92c67369 100644 --- a/lib/dashboard/home/tab_bar/credentials/present/pick/credential_manifest/helpers/apply_submission_requirements.dart +++ b/lib/dashboard/home/tab_bar/credentials/present/pick/credential_manifest/helpers/apply_submission_requirements.dart @@ -3,6 +3,8 @@ import 'package:credential_manifest/credential_manifest.dart'; PresentationDefinition applySubmissionRequirements( PresentationDefinition presentationDefinition, ) { + PresentationDefinition newPresentationDefinition = presentationDefinition; + if (presentationDefinition.submissionRequirements != null) { /// https://identity.foundation/presentation-exchange/#presentation-definition-extensions final inputDescriptors = List.of(presentationDefinition.inputDescriptors); @@ -47,10 +49,10 @@ PresentationDefinition applySubmissionRequirements( } } - presentationDefinition = PresentationDefinition.copyWithData( + newPresentationDefinition = PresentationDefinition.copyWithData( oldPresentationDefinition: presentationDefinition, inputDescriptors: newInputDescriptor, ); } - return presentationDefinition; + return newPresentationDefinition; } diff --git a/lib/dashboard/home/tab_bar/credentials/present/pick/credential_manifest/view/credential_manifest_credential_offer_pick_page.dart b/lib/dashboard/home/tab_bar/credentials/present/pick/credential_manifest/view/credential_manifest_credential_offer_pick_page.dart index 133b371cb..3e7c41547 100644 --- a/lib/dashboard/home/tab_bar/credentials/present/pick/credential_manifest/view/credential_manifest_credential_offer_pick_page.dart +++ b/lib/dashboard/home/tab_bar/credentials/present/pick/credential_manifest/view/credential_manifest_credential_offer_pick_page.dart @@ -3,7 +3,7 @@ import 'package:altme/credentials/credentials.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; import 'package:altme/scan/cubit/scan_cubit.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:credential_manifest/credential_manifest.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -147,8 +147,7 @@ class CredentialManifestOfferPickView extends StatelessWidget { children: [ Text( '${inputDescriptorIndex + 1}/${presentationDefinition.inputDescriptors.length}', - style: - Theme.of(context).textTheme.credentialSteps, + style: Theme.of(context).textTheme.titleLarge, ), const SizedBox(height: 10), Padding( @@ -158,9 +157,7 @@ class CredentialManifestOfferPickView extends StatelessWidget { .inputDescriptors[inputDescriptorIndex] .purpose ?? l10n.credentialPickSelect, - style: Theme.of(context) - .textTheme - .credentialSubtitle, + style: Theme.of(context).textTheme.titleMedium, ), ), const SizedBox(height: 12), @@ -203,21 +200,26 @@ class CredentialManifestOfferPickView extends StatelessWidget { onPressed: !credentialManifestState .isButtonEnabled ? null - : () => Navigator.of(context) + : () { + final credentialToBePresented = + credentialManifestState + .filteredCredentialList[ + credentialManifestState + .selected.first]; + + Navigator.of(context) .pushReplacement( SelectiveDisclosurePickPage.route( uri: uri, issuer: issuer, credential: credential, credentialToBePresented: - credentialManifestState - .filteredCredentialList[ - credentialManifestState - .selected.first], + credentialToBePresented, presentationDefinition: presentationDefinition, ), - ), + ); + }, /// next button because we will now choose /// the claims we will present diff --git a/lib/dashboard/home/tab_bar/credentials/present/pick/credential_manifest/widgets/required_credential_not_found.dart b/lib/dashboard/home/tab_bar/credentials/present/pick/credential_manifest/widgets/required_credential_not_found.dart index 69b7e0a62..e9fb8fef3 100644 --- a/lib/dashboard/home/tab_bar/credentials/present/pick/credential_manifest/widgets/required_credential_not_found.dart +++ b/lib/dashboard/home/tab_bar/credentials/present/pick/credential_manifest/widgets/required_credential_not_found.dart @@ -1,6 +1,5 @@ import 'package:altme/app/app.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/app_theme/app_theme.dart'; import 'package:flutter/material.dart'; class RequiredCredentialNotFound extends StatelessWidget { @@ -26,7 +25,7 @@ class RequiredCredentialNotFound extends StatelessWidget { Text( l10n.requiredCredentialNotFoundTitle, textAlign: TextAlign.center, - style: Theme.of(context).textTheme.infoSubtitle, + style: Theme.of(context).textTheme.titleMedium, ), const Spacer( flex: 3, @@ -42,7 +41,7 @@ class RequiredCredentialNotFound extends StatelessWidget { Text( l10n.requiredCredentialNotFoundSubTitle, textAlign: TextAlign.center, - style: Theme.of(context).textTheme.title, + style: Theme.of(context).textTheme.headlineSmall, ), const Spacer( flex: 1, @@ -50,12 +49,12 @@ class RequiredCredentialNotFound extends StatelessWidget { Text( l10n.requiredCredentialNotFoundDescription, textAlign: TextAlign.center, - style: Theme.of(context).textTheme.infoSubtitle, + style: Theme.of(context).textTheme.titleMedium, ), Text( AltMeStrings.appSupportMail, textAlign: TextAlign.center, - style: Theme.of(context).textTheme.title, + style: Theme.of(context).textTheme.headlineSmall, ), const Spacer( flex: 3, diff --git a/lib/dashboard/home/tab_bar/credentials/present/pick/query_by_example/view/query_by_example_credentials_pick_page.dart b/lib/dashboard/home/tab_bar/credentials/present/pick/query_by_example/view/query_by_example_credentials_pick_page.dart index c73490195..6bc2111af 100644 --- a/lib/dashboard/home/tab_bar/credentials/present/pick/query_by_example/view/query_by_example_credentials_pick_page.dart +++ b/lib/dashboard/home/tab_bar/credentials/present/pick/query_by_example/view/query_by_example_credentials_pick_page.dart @@ -4,7 +4,7 @@ import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; import 'package:altme/query_by_example/query_by_example.dart'; import 'package:altme/scan/scan.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:altme/wallet/wallet.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -215,13 +215,13 @@ class QueryByExampleCredentialPickView extends StatelessWidget { if (credentialQueryList.isNotEmpty) ...[ Text( '${credentialQueryIndex + 1}/${queryByExampleCubit.credentialQuery.length}', - style: Theme.of(context).textTheme.credentialSteps, + style: Theme.of(context).textTheme.titleLarge, ), const SizedBox(height: 10), ], Text( reason.trim() == '' ? l10n.credentialPickSelect : reason, - style: Theme.of(context).textTheme.credentialSubtitle, + style: Theme.of(context).textTheme.titleMedium, ), const SizedBox(height: 12), ...List.generate( diff --git a/lib/dashboard/home/tab_bar/credentials/receive/view/credentials_receive_page.dart b/lib/dashboard/home/tab_bar/credentials/receive/view/credentials_receive_page.dart index 28886e0e1..93ca292c4 100644 --- a/lib/dashboard/home/tab_bar/credentials/receive/view/credentials_receive_page.dart +++ b/lib/dashboard/home/tab_bar/credentials/receive/view/credentials_receive_page.dart @@ -2,7 +2,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; import 'package:altme/scan/scan.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -60,7 +60,7 @@ class _CredentialsReceivePageState extends State { final outputDescriptors = credentialModel.credentialManifest?.outputDescriptors; - final textColor = Theme.of(context).colorScheme.valueColor; + final textColor = Theme.of(context).colorScheme.onSurface; final profileSetting = context.read().state.model.profileSetting; @@ -102,8 +102,7 @@ class _CredentialsReceivePageState extends State { tilePadding: const EdgeInsets.symmetric(horizontal: 8), title: Text( l10n.credentialManifestDescription, - style: - Theme.of(context).textTheme.credentialManifestTitle2, + style: Theme.of(context).textTheme.titleMedium, ), children: [ Padding( diff --git a/lib/dashboard/home/tab_bar/credentials/repository/credential_repository.dart b/lib/dashboard/home/tab_bar/credentials/repository/credential_repository.dart index 23d94d95d..bdf135bf2 100644 --- a/lib/dashboard/home/tab_bar/credentials/repository/credential_repository.dart +++ b/lib/dashboard/home/tab_bar/credentials/repository/credential_repository.dart @@ -46,7 +46,6 @@ class CredentialsRepository { } Future deleteAll() async { - print('before getAllValues 2'); final data = await _secureStorageProvider.getAllValues(); diff --git a/lib/dashboard/home/tab_bar/credentials/widgets/activity_widget.dart b/lib/dashboard/home/tab_bar/credentials/widgets/activity_widget.dart index 10e5c3126..d73d9c29e 100644 --- a/lib/dashboard/home/tab_bar/credentials/widgets/activity_widget.dart +++ b/lib/dashboard/home/tab_bar/credentials/widgets/activity_widget.dart @@ -1,7 +1,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/home/tab_bar/credentials/models/activity/activity.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class ActivityWidget extends StatelessWidget { @@ -17,8 +17,8 @@ class ActivityWidget extends StatelessWidget { @override Widget build(BuildContext context) { final l10n = context.l10n; - final titleColor = Theme.of(context).colorScheme.titleColor; - final valueColor = Theme.of(context).colorScheme.valueColor; + final titleColor = Theme.of(context).colorScheme.onSurface; + final valueColor = Theme.of(context).colorScheme.onSurface; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -46,7 +46,7 @@ class ActivityWidget extends StatelessWidget { l10n.credentialDetailsOrganisationDetail, style: Theme.of(context) .textTheme - .credentialFieldTitle + .bodyMedium! .copyWith(color: titleColor), ), const SizedBox(height: 5), @@ -57,7 +57,7 @@ class ActivityWidget extends StatelessWidget { : activity.presentation!.issuer.organizationInfo.website, style: Theme.of(context) .textTheme - .credentialFieldDescription + .bodyMedium! .copyWith(color: valueColor), maxLines: 5, overflow: TextOverflow.ellipsis, @@ -74,7 +74,7 @@ class ActivityWidget extends StatelessWidget { ), const SizedBox(height: 5), ], - Divider(color: Theme.of(context).colorScheme.surfaceContainer), + Divider(color: Theme.of(context).colorScheme.surface.withOpacity(0.07)), ], ); } diff --git a/lib/dashboard/home/tab_bar/credentials/widgets/card_animation.dart b/lib/dashboard/home/tab_bar/credentials/widgets/card_animation.dart index 7212a0d1f..517ce1d8c 100644 --- a/lib/dashboard/home/tab_bar/credentials/widgets/card_animation.dart +++ b/lib/dashboard/home/tab_bar/credentials/widgets/card_animation.dart @@ -54,7 +54,6 @@ class _CardAnimationState extends State onTap: () async { await _animationController.reverse(); - // TODO(all): remove setstate setState(() { if (_card is Recto) { _card = widget.verso; diff --git a/lib/dashboard/home/tab_bar/credentials/widgets/credential_background.dart b/lib/dashboard/home/tab_bar/credentials/widgets/credential_background.dart index b82fa189c..8fe9e2754 100644 --- a/lib/dashboard/home/tab_bar/credentials/widgets/credential_background.dart +++ b/lib/dashboard/home/tab_bar/credentials/widgets/credential_background.dart @@ -1,6 +1,5 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; class CredentialBackground extends StatelessWidget { @@ -28,7 +27,7 @@ class CredentialBackground extends StatelessWidget { credentialModel.credentialPreview.credentialSubjectModel .credentialSubjectType .backgroundColor(credentialModel), - shapeColor: Theme.of(context).colorScheme.documentShape, + shapeColor: Theme.of(context).colorScheme.primary.withOpacity(0.05), value: 0, shapeSize: 256, anchors: showBgDecoration diff --git a/lib/dashboard/home/tab_bar/credentials/widgets/credential_container.dart b/lib/dashboard/home/tab_bar/credentials/widgets/credential_container.dart index eadf15399..b862c09dd 100644 --- a/lib/dashboard/home/tab_bar/credentials/widgets/credential_container.dart +++ b/lib/dashboard/home/tab_bar/credentials/widgets/credential_container.dart @@ -1,5 +1,4 @@ import 'package:altme/app/shared/constants/sizes.dart'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; class CredentialContainer extends StatelessWidget { @@ -14,7 +13,7 @@ class CredentialContainer extends StatelessWidget { decoration: BoxDecoration( boxShadow: [ BoxShadow( - color: Theme.of(context).colorScheme.documentShadow, + color: Theme.of(context).colorScheme.onSurface.withOpacity(0.12), blurRadius: 15, spreadRadius: 1, offset: const Offset(0, 4), diff --git a/lib/dashboard/home/tab_bar/credentials/widgets/credential_dynamic_detail.dart b/lib/dashboard/home/tab_bar/credentials/widgets/credential_dynamic_detail.dart index bafbb9985..dd8fc83c6 100644 --- a/lib/dashboard/home/tab_bar/credentials/widgets/credential_dynamic_detail.dart +++ b/lib/dashboard/home/tab_bar/credentials/widgets/credential_dynamic_detail.dart @@ -1,6 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; @@ -44,14 +44,11 @@ class CredentialDynamicDetial extends StatelessWidget { final textTheme = Theme.of(context).textTheme; final titleTheme = titleColor == null - ? textTheme.credentialFieldTitle - : textTheme.credentialFieldTitle.copyWith(color: titleColor); + ? textTheme.bodyMedium + : textTheme.bodyMedium!.copyWith(color: titleColor); final valueTheme = valueColor == null - ? Theme.of(context).textTheme.credentialFieldDescription - : Theme.of(context) - .textTheme - .credentialFieldDescription - .copyWith(color: valueColor); + ? Theme.of(context).textTheme.bodyMedium + : Theme.of(context).textTheme.bodyMedium!.copyWith(color: valueColor); return Padding( padding: padding, @@ -67,8 +64,8 @@ class CredentialDynamicDetial extends StatelessWidget { style: (format != null && (format == AltMeStrings.uri || format == AltMeStrings.email)) - ? valueTheme.copyWith( - color: Theme.of(context).colorScheme.markDownA, + ? valueTheme!.copyWith( + color: Theme.of(context).colorScheme.primary, ) : valueTheme, recognizer: TapGestureRecognizer() @@ -90,7 +87,7 @@ class CredentialDynamicDetial extends StatelessWidget { }, child: ImageIcon( const AssetImage(IconStrings.link), - color: Theme.of(context).colorScheme.markDownA, + color: Theme.of(context).colorScheme.primary, size: 17, ), ), diff --git a/lib/dashboard/home/tab_bar/credentials/widgets/credential_manifest_widgets/credential_manifest_card.dart b/lib/dashboard/home/tab_bar/credentials/widgets/credential_manifest_widgets/credential_manifest_card.dart index c1b17928b..b2f59bcfe 100644 --- a/lib/dashboard/home/tab_bar/credentials/widgets/credential_manifest_widgets/credential_manifest_card.dart +++ b/lib/dashboard/home/tab_bar/credentials/widgets/credential_manifest_widgets/credential_manifest_card.dart @@ -1,7 +1,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:credential_manifest/credential_manifest.dart'; import 'package:flutter/material.dart'; @@ -47,7 +47,7 @@ class CredentialManifestCard extends StatelessWidget { ], ) : null, - shapeColor: Theme.of(context).colorScheme.documentShape, + shapeColor: Theme.of(context).colorScheme.primary.withOpacity(0.05), value: 1, anchors: showBgDecoration ? const [Alignment.bottomRight] @@ -68,12 +68,10 @@ class CredentialManifestCard extends StatelessWidget { displayMapping: outputDescriptor.display?.title, credentialModel: credentialModel, textStyle: textColor == null - ? Theme.of(context) - .textTheme - .credentialBaseTitleText + ? Theme.of(context).textTheme.displayLarge! : Theme.of(context) .textTheme - .credentialBaseTitleText + .displayLarge! .copyWith(color: textColor), ), ), @@ -94,16 +92,12 @@ class CredentialManifestCard extends StatelessWidget { children: [ TextSpan( text: '${l10n.providedBy} ', - style: Theme.of(context) - .textTheme - .credentialBaseLightText, + style: Theme.of(context).textTheme.bodyMedium, ), TextSpan( text: credentialModel.credentialPreview .credentialSubjectModel.issuedBy?.name, - style: Theme.of(context) - .textTheme - .credentialBaseBoldText, + style: Theme.of(context).textTheme.bodyMedium, ), ], ), @@ -122,10 +116,10 @@ class CredentialManifestCard extends StatelessWidget { displayMapping: outputDescriptor.display?.subtitle, credentialModel: credentialModel, textStyle: textColor == null - ? Theme.of(context).textTheme.credentialBaseBoldText + ? Theme.of(context).textTheme.bodyMedium! : Theme.of(context) .textTheme - .credentialBaseBoldText + .bodyMedium! .copyWith(color: textColor), ), ), @@ -138,7 +132,7 @@ class CredentialManifestCard extends StatelessWidget { widthFactor: 0.4, child: MyText( l10n.issuedOn, - style: Theme.of(context).textTheme.credentialBaseBoldText, + style: Theme.of(context).textTheme.bodyMedium, ), ), ), @@ -151,7 +145,7 @@ class CredentialManifestCard extends StatelessWidget { UiDate.formatDateForCredentialCard( credentialModel.credentialPreview.issuanceDate, ), - style: Theme.of(context).textTheme.credentialBaseLightText, + style: Theme.of(context).textTheme.bodyMedium, ), ), ), @@ -163,7 +157,7 @@ class CredentialManifestCard extends StatelessWidget { widthFactor: 0.4, child: MyText( l10n.expirationDate, - style: Theme.of(context).textTheme.credentialBaseBoldText, + style: Theme.of(context).textTheme.bodyMedium, ), ), ), @@ -177,8 +171,7 @@ class CredentialManifestCard extends StatelessWidget { UiDate.formatDateForCredentialCard( credentialModel.expirationDate!, ), - style: - Theme.of(context).textTheme.credentialBaseLightText, + style: Theme.of(context).textTheme.bodyMedium, ), ), ), diff --git a/lib/dashboard/home/tab_bar/credentials/widgets/credential_manifest_widgets/credential_manifest_details.dart b/lib/dashboard/home/tab_bar/credentials/widgets/credential_manifest_widgets/credential_manifest_details.dart index 3d0c324d6..b36ef5ec4 100644 --- a/lib/dashboard/home/tab_bar/credentials/widgets/credential_manifest_widgets/credential_manifest_details.dart +++ b/lib/dashboard/home/tab_bar/credentials/widgets/credential_manifest_widgets/credential_manifest_details.dart @@ -1,6 +1,5 @@ import 'package:altme/dashboard/dashboard.dart'; -import 'package:altme/theme/theme.dart'; import 'package:credential_manifest/credential_manifest.dart'; import 'package:flutter/material.dart'; @@ -16,8 +15,8 @@ class CredentialManifestDetails extends StatelessWidget { @override Widget build(BuildContext context) { - final titleColor = Theme.of(context).colorScheme.titleColor; - final valueColor = Theme.of(context).colorScheme.valueColor; + final titleColor = Theme.of(context).colorScheme.onSurface; + final valueColor = Theme.of(context).colorScheme.onSurface; return Column( crossAxisAlignment: CrossAxisAlignment.start, diff --git a/lib/dashboard/home/tab_bar/credentials/widgets/credential_manifest_widgets/expansiontile_container.dart b/lib/dashboard/home/tab_bar/credentials/widgets/credential_manifest_widgets/expansiontile_container.dart index 9278d05d8..012e63e25 100644 --- a/lib/dashboard/home/tab_bar/credentials/widgets/credential_manifest_widgets/expansiontile_container.dart +++ b/lib/dashboard/home/tab_bar/credentials/widgets/credential_manifest_widgets/expansiontile_container.dart @@ -1,5 +1,4 @@ import 'package:altme/app/app.dart'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; class ExpansionTileContainer extends StatelessWidget { @@ -13,15 +12,15 @@ class ExpansionTileContainer extends StatelessWidget { @override Widget build(BuildContext context) { return BackgroundCard( - color: Theme.of(context).colorScheme.surfaceContainer, + color: Theme.of(context).colorScheme.surface.withOpacity(0.07), padding: const EdgeInsets.symmetric(horizontal: 8), margin: const EdgeInsets.only(bottom: 8), child: Theme( data: Theme.of(context).copyWith( unselectedWidgetColor: Theme.of(context).colorScheme.onPrimary, - dividerColor: Theme.of(context).colorScheme.surfaceContainer, - splashColor: Theme.of(context).colorScheme.transparent, - highlightColor: Theme.of(context).colorScheme.transparent, + dividerColor: Theme.of(context).colorScheme.surface.withOpacity(0.07), + splashColor: Colors.transparent, + highlightColor: Colors.transparent, colorScheme: ColorScheme.dark( primary: Theme.of(context).colorScheme.onPrimary, ), diff --git a/lib/dashboard/home/tab_bar/credentials/widgets/credential_widget/add_credential_button.dart b/lib/dashboard/home/tab_bar/credentials/widgets/credential_widget/add_credential_button.dart index eda451d46..9cd6c4cd3 100644 --- a/lib/dashboard/home/tab_bar/credentials/widgets/credential_widget/add_credential_button.dart +++ b/lib/dashboard/home/tab_bar/credentials/widgets/credential_widget/add_credential_button.dart @@ -1,7 +1,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -37,7 +37,7 @@ class AddCredentialButton extends StatelessWidget { ), Text( l10n.addCards, - style: Theme.of(context).textTheme.credentialCategoryTitle, + style: Theme.of(context).textTheme.headlineSmall, ), ], ), diff --git a/lib/dashboard/home/tab_bar/credentials/widgets/credential_widget/arago_learning_achievement_widget.dart b/lib/dashboard/home/tab_bar/credentials/widgets/credential_widget/arago_learning_achievement_widget.dart index 4cf538708..e46c3b380 100644 --- a/lib/dashboard/home/tab_bar/credentials/widgets/credential_widget/arago_learning_achievement_widget.dart +++ b/lib/dashboard/home/tab_bar/credentials/widgets/credential_widget/arago_learning_achievement_widget.dart @@ -1,6 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class AragoLearningAchievementWidget extends StatelessWidget { @@ -51,7 +51,7 @@ class AragoLearningAchievementRecto extends Recto { id: 'name', child: DisplayNameCard( credentialModel: credentialModel, - style: Theme.of(context).textTheme.credentialTitleCard, + style: Theme.of(context).textTheme.titleLarge!, ), ), LayoutId( @@ -59,15 +59,14 @@ class AragoLearningAchievementRecto extends Recto { child: MyText( credentialModel .credentialPreview.credentialSubjectModel.issuedBy!.name, - style: Theme.of(context).textTheme.studentCardSchool, + style: Theme.of(context).textTheme.bodyMedium, ), ), LayoutId( id: 'description', child: DisplayDescriptionCard( credentialModel: credentialModel, - style: - Theme.of(context).textTheme.credentialStudentCardTextCard, + style: Theme.of(context).textTheme.bodyMedium!, ), ), ], diff --git a/lib/dashboard/home/tab_bar/credentials/widgets/credential_widget/certificate_of_employment_widget.dart b/lib/dashboard/home/tab_bar/credentials/widgets/credential_widget/certificate_of_employment_widget.dart index 19810a4e8..fa31bf842 100644 --- a/lib/dashboard/home/tab_bar/credentials/widgets/credential_widget/certificate_of_employment_widget.dart +++ b/lib/dashboard/home/tab_bar/credentials/widgets/credential_widget/certificate_of_employment_widget.dart @@ -1,7 +1,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/home/tab_bar/credentials/credential.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class CertificateOfEmploymentWidget extends StatelessWidget { @@ -52,16 +52,14 @@ class CertificateOfEmploymentRecto extends Recto { id: 'name', child: DisplayNameCard( credentialModel: credentialModel, - style: Theme.of(context) - .textTheme - .certificateOfEmploymentTitleCard, + style: Theme.of(context).textTheme.bodyMedium!, ), ), LayoutId( id: 'description', child: DisplayDescriptionCard( credentialModel: credentialModel, - style: Theme.of(context).textTheme.credentialDescription, + style: Theme.of(context).textTheme.bodyMedium!, maxLines: 4, ), ), @@ -102,9 +100,7 @@ class CertificateOfEmploymentVerso extends Verso { widthFactor: 0.7, child: DisplayNameCard( credentialModel: credentialModel, - style: Theme.of(context) - .textTheme - .certificateOfEmploymentTitleCard, + style: Theme.of(context).textTheme.bodyMedium!, ), ), ), @@ -116,13 +112,12 @@ class CertificateOfEmploymentVerso extends Verso { text: '${l10n.personalLastName}: ', textStyle: Theme.of(context) .textTheme - .certificateOfEmploymentData + .bodyMedium! .copyWith(fontWeight: FontWeight.bold), ), ImageCardText( text: certificateOfEmploymentModel.familyName!, - textStyle: - Theme.of(context).textTheme.certificateOfEmploymentData, + textStyle: Theme.of(context).textTheme.bodyMedium, ), ], ), @@ -135,13 +130,12 @@ class CertificateOfEmploymentVerso extends Verso { text: '${l10n.personalFirstName}: ', textStyle: Theme.of(context) .textTheme - .certificateOfEmploymentData + .bodyMedium! .copyWith(fontWeight: FontWeight.bold), ), ImageCardText( text: certificateOfEmploymentModel.givenName!, - textStyle: - Theme.of(context).textTheme.certificateOfEmploymentData, + textStyle: Theme.of(context).textTheme.bodyMedium, ), ], ), @@ -154,13 +148,12 @@ class CertificateOfEmploymentVerso extends Verso { text: '${l10n.workFor}: ', textStyle: Theme.of(context) .textTheme - .certificateOfEmploymentData + .bodyMedium! .copyWith(fontWeight: FontWeight.bold), ), ImageCardText( text: certificateOfEmploymentModel.workFor!.name, - textStyle: - Theme.of(context).textTheme.certificateOfEmploymentData, + textStyle: Theme.of(context).textTheme.bodyMedium, ), const SizedBox(width: 20), if (certificateOfEmploymentModel.workFor!.logo != '') @@ -183,15 +176,14 @@ class CertificateOfEmploymentVerso extends Verso { text: '${l10n.startDate}: ', textStyle: Theme.of(context) .textTheme - .certificateOfEmploymentData + .bodyMedium! .copyWith(fontWeight: FontWeight.bold), ), ImageCardText( text: UiDate.formatStringDate( certificateOfEmploymentModel.startDate!, ), - textStyle: - Theme.of(context).textTheme.certificateOfEmploymentData, + textStyle: Theme.of(context).textTheme.bodyMedium, ), ], ), @@ -204,13 +196,12 @@ class CertificateOfEmploymentVerso extends Verso { text: '${l10n.jobTitle}: ', textStyle: Theme.of(context) .textTheme - .certificateOfEmploymentData + .bodyMedium! .copyWith(fontWeight: FontWeight.bold), ), ImageCardText( text: certificateOfEmploymentModel.jobTitle!, - textStyle: - Theme.of(context).textTheme.certificateOfEmploymentData, + textStyle: Theme.of(context).textTheme.bodyMedium, ), ], ), @@ -223,13 +214,12 @@ class CertificateOfEmploymentVerso extends Verso { text: '${l10n.employmentType}: ', textStyle: Theme.of(context) .textTheme - .certificateOfEmploymentData + .bodyMedium! .copyWith(fontWeight: FontWeight.bold), ), ImageCardText( text: certificateOfEmploymentModel.employmentType!, - textStyle: - Theme.of(context).textTheme.certificateOfEmploymentData, + textStyle: Theme.of(context).textTheme.bodyMedium, ), ], ), @@ -242,13 +232,12 @@ class CertificateOfEmploymentVerso extends Verso { text: '${l10n.baseSalary}: ', textStyle: Theme.of(context) .textTheme - .certificateOfEmploymentData + .bodyMedium! .copyWith(fontWeight: FontWeight.bold), ), ImageCardText( text: certificateOfEmploymentModel.baseSalary!, - textStyle: - Theme.of(context).textTheme.certificateOfEmploymentData, + textStyle: Theme.of(context).textTheme.bodyMedium, ), ], ), @@ -261,15 +250,14 @@ class CertificateOfEmploymentVerso extends Verso { text: '${l10n.issuanceDate}: ', textStyle: Theme.of(context) .textTheme - .certificateOfEmploymentData + .bodyMedium! .copyWith(fontWeight: FontWeight.bold), ), ImageCardText( text: UiDate.formatStringDate( credentialModel.credentialPreview.issuanceDate, ), - textStyle: - Theme.of(context).textTheme.certificateOfEmploymentData, + textStyle: Theme.of(context).textTheme.bodyMedium, ), ], ), diff --git a/lib/dashboard/home/tab_bar/credentials/widgets/credential_widget/credential_base_widget.dart b/lib/dashboard/home/tab_bar/credentials/widgets/credential_widget/credential_base_widget.dart index 1657ed4ca..18e1f302e 100644 --- a/lib/dashboard/home/tab_bar/credentials/widgets/credential_widget/credential_base_widget.dart +++ b/lib/dashboard/home/tab_bar/credentials/widgets/credential_widget/credential_base_widget.dart @@ -1,7 +1,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class CredentialBaseWidget extends StatelessWidget { @@ -44,8 +44,7 @@ class CredentialBaseWidget extends StatelessWidget { alignment: Alignment.centerLeft, child: MyText( title!, - style: - Theme.of(context).textTheme.credentialBaseTitleText, + style: Theme.of(context).textTheme.displayLarge, ), ), ), @@ -61,15 +60,11 @@ class CredentialBaseWidget extends StatelessWidget { children: [ TextSpan( text: '${l10n.providedBy} ', - style: Theme.of(context) - .textTheme - .credentialBaseLightText, + style: Theme.of(context).textTheme.bodyMedium, ), TextSpan( text: issuerName, - style: Theme.of(context) - .textTheme - .credentialBaseBoldText, + style: Theme.of(context).textTheme.bodyMedium, ), ], ), @@ -86,7 +81,7 @@ class CredentialBaseWidget extends StatelessWidget { alignment: Alignment.centerLeft, child: MyText( value!, - style: Theme.of(context).textTheme.credentialBaseBoldText, + style: Theme.of(context).textTheme.bodyMedium, ), ), ), @@ -99,7 +94,7 @@ class CredentialBaseWidget extends StatelessWidget { widthFactor: 0.4, child: MyText( l10n.issuedOn, - style: Theme.of(context).textTheme.credentialBaseBoldText, + style: Theme.of(context).textTheme.bodyMedium, ), ), ), @@ -111,7 +106,7 @@ class CredentialBaseWidget extends StatelessWidget { widthFactor: 0.4, child: MyText( issuanceDate!, - style: Theme.of(context).textTheme.credentialBaseLightText, + style: Theme.of(context).textTheme.bodyMedium, ), ), ), @@ -123,7 +118,7 @@ class CredentialBaseWidget extends StatelessWidget { widthFactor: 0.4, child: MyText( l10n.expirationDate, - style: Theme.of(context).textTheme.credentialBaseBoldText, + style: Theme.of(context).textTheme.bodyMedium, ), ), ), @@ -135,7 +130,7 @@ class CredentialBaseWidget extends StatelessWidget { widthFactor: 0.4, child: MyText( expirationDate!, - style: Theme.of(context).textTheme.credentialBaseLightText, + style: Theme.of(context).textTheme.bodyMedium, ), ), ), diff --git a/lib/dashboard/home/tab_bar/credentials/widgets/credential_widget/learning_achievement_widget.dart b/lib/dashboard/home/tab_bar/credentials/widgets/credential_widget/learning_achievement_widget.dart index 2485b566e..8ebcf86a9 100644 --- a/lib/dashboard/home/tab_bar/credentials/widgets/credential_widget/learning_achievement_widget.dart +++ b/lib/dashboard/home/tab_bar/credentials/widgets/credential_widget/learning_achievement_widget.dart @@ -1,7 +1,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class LearningAchievementDisplayDetail extends StatelessWidget { @@ -45,7 +45,7 @@ class LearningAchievementRecto extends Recto { id: 'name', child: DisplayNameCard( credentialModel: credentialModel, - style: Theme.of(context).textTheme.credentialTitleCard, + style: Theme.of(context).textTheme.titleLarge!, ), ), LayoutId( @@ -53,15 +53,14 @@ class LearningAchievementRecto extends Recto { child: MyText( credentialModel .credentialPreview.credentialSubjectModel.issuedBy!.name, - style: Theme.of(context).textTheme.studentCardSchool, + style: Theme.of(context).textTheme.bodyMedium, ), ), LayoutId( id: 'description', child: DisplayDescriptionCard( credentialModel: credentialModel, - style: - Theme.of(context).textTheme.credentialStudentCardTextCard, + style: Theme.of(context).textTheme.bodyMedium!, ), ), ], @@ -94,7 +93,7 @@ class LearningAchievementVerso extends Verso { id: 'name', child: DisplayNameCard( credentialModel: credentialModel, - style: Theme.of(context).textTheme.credentialTitleCard, + style: Theme.of(context).textTheme.titleLarge!, ), ), LayoutId( @@ -102,7 +101,7 @@ class LearningAchievementVerso extends Verso { child: MyText( learningAchievementModel.issuedBy!.name, overflow: TextOverflow.fade, - style: Theme.of(context).textTheme.studentCardSchool, + style: Theme.of(context).textTheme.bodyMedium, ), ), LayoutId( @@ -113,12 +112,12 @@ class LearningAchievementVerso extends Verso { text: '${l10n.personalLastName}: ', textStyle: Theme.of(context) .textTheme - .studentCardData + .bodyMedium! .copyWith(fontWeight: FontWeight.bold), ), ImageCardText( text: learningAchievementModel.familyName!, - textStyle: Theme.of(context).textTheme.studentCardData, + textStyle: Theme.of(context).textTheme.bodyMedium, ), ], ), @@ -131,12 +130,12 @@ class LearningAchievementVerso extends Verso { text: '${l10n.personalFirstName}: ', textStyle: Theme.of(context) .textTheme - .studentCardData + .bodyMedium! .copyWith(fontWeight: FontWeight.bold), ), ImageCardText( text: learningAchievementModel.givenName!, - textStyle: Theme.of(context).textTheme.studentCardData, + textStyle: Theme.of(context).textTheme.bodyMedium, ), ], ), @@ -149,14 +148,14 @@ class LearningAchievementVerso extends Verso { text: '${l10n.birthdate}: ', textStyle: Theme.of(context) .textTheme - .studentCardData + .bodyMedium! .copyWith(fontWeight: FontWeight.bold), ), ImageCardText( text: UiDate.formatStringDate( learningAchievementModel.birthDate!, ), - textStyle: Theme.of(context).textTheme.studentCardData, + textStyle: Theme.of(context).textTheme.bodyMedium, ), ], ), @@ -170,12 +169,12 @@ class LearningAchievementVerso extends Verso { '''${learningAchievementModel.hasCredential!.title}: ''', textStyle: Theme.of(context) .textTheme - .studentCardData + .bodyMedium! .copyWith(fontWeight: FontWeight.bold), ), ImageCardText( text: learningAchievementModel.hasCredential!.description, - textStyle: Theme.of(context).textTheme.studentCardData, + textStyle: Theme.of(context).textTheme.bodyMedium, ), ], ), @@ -188,7 +187,7 @@ class LearningAchievementVerso extends Verso { text: '${l10n.proof}: ', textStyle: Theme.of(context) .textTheme - .studentCardData + .bodyMedium! .copyWith(fontWeight: FontWeight.bold), ), InkWell( @@ -199,7 +198,7 @@ class LearningAchievementVerso extends Verso { }, child: ImageCardText( text: credentialModel.credentialPreview.evidence.first.id, - textStyle: Theme.of(context).textTheme.studentCardData, + textStyle: Theme.of(context).textTheme.bodyMedium, ), ), ], diff --git a/lib/dashboard/home/tab_bar/credentials/widgets/credential_widget/my_blockchain_account_base_widget.dart b/lib/dashboard/home/tab_bar/credentials/widgets/credential_widget/my_blockchain_account_base_widget.dart index 77ce906ab..b7f701708 100644 --- a/lib/dashboard/home/tab_bar/credentials/widgets/credential_widget/my_blockchain_account_base_widget.dart +++ b/lib/dashboard/home/tab_bar/credentials/widgets/credential_widget/my_blockchain_account_base_widget.dart @@ -1,6 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class MyBlockchainAccountBaseWidget extends StatelessWidget { @@ -32,7 +32,7 @@ class MyBlockchainAccountBaseWidget extends StatelessWidget { heightFactor: 0.14, child: MyText( proofMessage, - style: Theme.of(context).textTheme.subMessage.copyWith( + style: Theme.of(context).textTheme.bodyMedium!.copyWith( color: Theme.of(context).colorScheme.onSurface, fontWeight: FontWeight.w600, ), @@ -48,7 +48,7 @@ class MyBlockchainAccountBaseWidget extends StatelessWidget { alignment: Alignment.bottomLeft, child: MyText( walletAddress, - style: Theme.of(context).textTheme.subMessage.copyWith( + style: Theme.of(context).textTheme.bodyMedium!.copyWith( color: Theme.of(context).colorScheme.onSurface, ), minFontSize: 8, diff --git a/lib/dashboard/home/tab_bar/credentials/widgets/credential_widget/poo_address_base_widget.dart b/lib/dashboard/home/tab_bar/credentials/widgets/credential_widget/poo_address_base_widget.dart index 5b5a7c978..ad4f4dce1 100644 --- a/lib/dashboard/home/tab_bar/credentials/widgets/credential_widget/poo_address_base_widget.dart +++ b/lib/dashboard/home/tab_bar/credentials/widgets/credential_widget/poo_address_base_widget.dart @@ -1,6 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class PooAddressBaseWidget extends StatelessWidget { @@ -35,7 +35,7 @@ class PooAddressBaseWidget extends StatelessWidget { heightFactor: 0.14, child: MyText( name, - style: Theme.of(context).textTheme.subMessage.copyWith( + style: Theme.of(context).textTheme.bodyMedium!.copyWith( color: Theme.of(context).colorScheme.onSurface, ), ), @@ -48,7 +48,7 @@ class PooAddressBaseWidget extends StatelessWidget { // heightFactor: 0.16, // child: MyText( // accountName, - // style: Theme.of(context).textTheme.title, + // style: Theme.of(context).textTheme.headlineSmall!, // ), // ), // ), @@ -69,7 +69,7 @@ class PooAddressBaseWidget extends StatelessWidget { alignment: Alignment.centerLeft, child: MyText( walletAddress, - style: Theme.of(context).textTheme.subMessage.copyWith( + style: Theme.of(context).textTheme.bodyMedium!.copyWith( color: Theme.of(context).colorScheme.onSurface, ), minFontSize: 8, @@ -87,7 +87,7 @@ class PooAddressBaseWidget extends StatelessWidget { alignment: Alignment.bottomLeft, child: MyText( issuerName, - style: Theme.of(context).textTheme.proofCardDetail, + style: Theme.of(context).textTheme.bodyMedium, minFontSize: 8, ), ), @@ -102,7 +102,7 @@ class PooAddressBaseWidget extends StatelessWidget { alignment: Alignment.bottomLeft, child: MyText( issuedOn, - style: Theme.of(context).textTheme.proofCardDetail, + style: Theme.of(context).textTheme.bodyMedium, minFontSize: 8, ), ), diff --git a/lib/dashboard/home/tab_bar/credentials/widgets/credential_widget/professional_experience_assessment_widget.dart b/lib/dashboard/home/tab_bar/credentials/widgets/credential_widget/professional_experience_assessment_widget.dart index b517b5274..dddfde8c6 100644 --- a/lib/dashboard/home/tab_bar/credentials/widgets/credential_widget/professional_experience_assessment_widget.dart +++ b/lib/dashboard/home/tab_bar/credentials/widgets/credential_widget/professional_experience_assessment_widget.dart @@ -1,7 +1,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class ProfessionalExperienceAssessmentWidget extends StatelessWidget { @@ -46,23 +46,23 @@ class ProfessionalExperienceAssessmentWidget extends StatelessWidget { children: [ Text( '${l10n.from.toLowerCase()} ', - style: Theme.of(context).textTheme.credentialFieldTitle, + style: Theme.of(context).textTheme.bodyMedium, ), Text( UiDate.formatStringDate( professionalExperienceAssessmentModel.startDate!, ), - style: Theme.of(context).textTheme.credentialFieldDescription, + style: Theme.of(context).textTheme.bodyMedium, ), Text( ' ${l10n.to.toLowerCase()} ', - style: Theme.of(context).textTheme.credentialFieldTitle, + style: Theme.of(context).textTheme.bodyMedium, ), Text( UiDate.formatStringDate( professionalExperienceAssessmentModel.endDate!, ), - style: Theme.of(context).textTheme.credentialFieldDescription, + style: Theme.of(context).textTheme.bodyMedium, ), ], ), @@ -93,12 +93,12 @@ class ProfessionalExperienceAssessmentWidget extends StatelessWidget { ), style: Theme.of(context) .textTheme - .professionalExperienceAssessmentRating, + .bodyMedium, ), StarRating( starCount: 5, rating: double.parse(item.reviewRating.ratingValue), - color: Theme.of(context).colorScheme.star, + color: Theme.of(context).colorScheme.onErrorContainer, ), ], ), diff --git a/lib/dashboard/home/tab_bar/credentials/widgets/credential_widget/resident_card_widget.dart b/lib/dashboard/home/tab_bar/credentials/widgets/credential_widget/resident_card_widget.dart index 4195ed1b2..f60784c93 100644 --- a/lib/dashboard/home/tab_bar/credentials/widgets/credential_widget/resident_card_widget.dart +++ b/lib/dashboard/home/tab_bar/credentials/widgets/credential_widget/resident_card_widget.dart @@ -1,7 +1,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class ResidentCardWidget extends StatelessWidget { @@ -43,7 +43,7 @@ class ResidentCardWidget extends StatelessWidget { children: [ Text( '${l10n.gender}: ', - style: Theme.of(context).textTheme.credentialFieldTitle, + style: Theme.of(context).textTheme.bodyMedium, ), Flexible( child: @@ -104,14 +104,14 @@ class GenderDisplay extends StatelessWidget { switch (residentCardModel.gender) { case 'male': genderIcon = - Icon(Icons.male, color: Theme.of(context).colorScheme.genderIcon); + Icon(Icons.male, color: Theme.of(context).colorScheme.onSurface); case 'female': genderIcon = - Icon(Icons.female, color: Theme.of(context).colorScheme.genderIcon); + Icon(Icons.female, color: Theme.of(context).colorScheme.onSurface); default: genderIcon = Icon( Icons.transgender, - color: Theme.of(context).colorScheme.genderIcon, + color: Theme.of(context).colorScheme.onSurface, ); } return genderIcon; diff --git a/lib/dashboard/home/tab_bar/credentials/widgets/credential_widget/tezotopia_voucher_widget.dart b/lib/dashboard/home/tab_bar/credentials/widgets/credential_widget/tezotopia_voucher_widget.dart index 35d1e4e89..948300447 100644 --- a/lib/dashboard/home/tab_bar/credentials/widgets/credential_widget/tezotopia_voucher_widget.dart +++ b/lib/dashboard/home/tab_bar/credentials/widgets/credential_widget/tezotopia_voucher_widget.dart @@ -1,7 +1,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class TezotopiaVoucherWidget extends StatelessWidget { @@ -33,7 +33,7 @@ class TezotopiaVoucherWidget extends StatelessWidget { child: MyText( '${l10n.voucher}' ' ${tezotopiaVoucherModel.offers?.benefit!.discount ?? ''}', - style: Theme.of(context).textTheme.title, + style: Theme.of(context).textTheme.headlineSmall, ), ), ), @@ -44,7 +44,7 @@ class TezotopiaVoucherWidget extends StatelessWidget { heightFactor: 0.14, child: MyText( l10n.tezotopia, - style: Theme.of(context).textTheme.subMessage.copyWith( + style: Theme.of(context).textTheme.bodyMedium!.copyWith( color: Theme.of(context).colorScheme.onSurface, ), ), diff --git a/lib/dashboard/home/tab_bar/credentials/widgets/credential_widget/wallet_credential_widget.dart b/lib/dashboard/home/tab_bar/credentials/widgets/credential_widget/wallet_credential_widget.dart index 6d902fde1..c3df1bde2 100644 --- a/lib/dashboard/home/tab_bar/credentials/widgets/credential_widget/wallet_credential_widget.dart +++ b/lib/dashboard/home/tab_bar/credentials/widgets/credential_widget/wallet_credential_widget.dart @@ -1,7 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -40,8 +39,8 @@ class WalletCredentialetailsWidget extends StatelessWidget { @override Widget build(BuildContext context) { final l10n = context.l10n; - final titleColor = Theme.of(context).colorScheme.titleColor; - final valueColor = Theme.of(context).colorScheme.valueColor; + final titleColor = Theme.of(context).colorScheme.onSurface; + final valueColor = Theme.of(context).colorScheme.onSurface; final isDeveloperMode = context.read().state.model.isDeveloperMode; diff --git a/lib/dashboard/home/tab_bar/credentials/widgets/default_display_descriptor.dart b/lib/dashboard/home/tab_bar/credentials/widgets/default_display_descriptor.dart index e2a6680cd..9d1e185b2 100644 --- a/lib/dashboard/home/tab_bar/credentials/widgets/default_display_descriptor.dart +++ b/lib/dashboard/home/tab_bar/credentials/widgets/default_display_descriptor.dart @@ -1,7 +1,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class DefaultDisplayDescriptor extends StatelessWidget { @@ -133,7 +133,7 @@ class DefaultCardBody extends StatelessWidget { credentialModel: credentialModel, style: Theme.of(context) .textTheme - .credentialBaseTitleText + .displayLarge! .copyWith(color: textColor), ), ), @@ -159,7 +159,7 @@ class DefaultCardBody extends StatelessWidget { text: '${l10n.providedBy} ', style: Theme.of(context) .textTheme - .credentialBaseLightText + .bodyMedium! .copyWith(color: textColor), ), TextSpan( @@ -167,7 +167,7 @@ class DefaultCardBody extends StatelessWidget { .credentialSubjectModel.issuedBy?.name, style: Theme.of(context) .textTheme - .credentialBaseBoldText + .bodyMedium! .copyWith(color: textColor), ), ], @@ -187,7 +187,7 @@ class DefaultCardBody extends StatelessWidget { credentialModel: credentialModel, style: Theme.of(context) .textTheme - .credentialBaseBoldText + .bodyMedium! .copyWith(color: textColor), maxLines: descriptionMaxLine, ), @@ -204,7 +204,7 @@ class DefaultCardBody extends StatelessWidget { l10n.issuedOn, style: Theme.of(context) .textTheme - .credentialBaseBoldText + .bodyMedium! .copyWith(color: textColor), ), ), @@ -221,7 +221,7 @@ class DefaultCardBody extends StatelessWidget { ), style: Theme.of(context) .textTheme - .credentialBaseLightText + .bodyMedium! .copyWith(color: textColor), ), ), @@ -236,7 +236,7 @@ class DefaultCardBody extends StatelessWidget { l10n.expirationDate, style: Theme.of(context) .textTheme - .credentialBaseBoldText + .bodyMedium! .copyWith(color: textColor), ), ), @@ -253,7 +253,7 @@ class DefaultCardBody extends StatelessWidget { ), style: Theme.of(context) .textTheme - .credentialBaseLightText + .bodyMedium! .copyWith(color: textColor), ), ), diff --git a/lib/dashboard/home/tab_bar/credentials/widgets/descriptioni_dialog.dart b/lib/dashboard/home/tab_bar/credentials/widgets/descriptioni_dialog.dart index 84cc2257f..5aee2e202 100644 --- a/lib/dashboard/home/tab_bar/credentials/widgets/descriptioni_dialog.dart +++ b/lib/dashboard/home/tab_bar/credentials/widgets/descriptioni_dialog.dart @@ -1,6 +1,5 @@ import 'package:altme/app/app.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; class DescriptionDialog extends StatelessWidget { @@ -15,7 +14,7 @@ class DescriptionDialog extends StatelessWidget { Widget build(BuildContext context) { final colorScheme = Theme.of(context).colorScheme; - final background = Theme.of(context).colorScheme.popupBackground; + final background = Theme.of(context).colorScheme.surface; return AlertDialog( backgroundColor: background, @@ -32,10 +31,7 @@ class DescriptionDialog extends StatelessWidget { children: [ Text( '${context.l10n.credentialManifestDescription}:', - style: Theme.of(context) - .textTheme - .titleLarge! - .copyWith(color: Theme.of(context).colorScheme.dialogText), + style: Theme.of(context).textTheme.titleLarge, textAlign: TextAlign.left, ), TransparentInkWell( @@ -47,7 +43,7 @@ class DescriptionDialog extends StatelessWidget { width: 20, decoration: BoxDecoration( shape: BoxShape.circle, - color: colorScheme.background, + color: colorScheme.surface, ), child: Icon( Icons.clear_rounded, @@ -61,10 +57,7 @@ class DescriptionDialog extends StatelessWidget { const SizedBox(height: 5), Text( text, - style: Theme.of(context) - .textTheme - .bodySmall! - .copyWith(color: Theme.of(context).colorScheme.dialogText), + style: Theme.of(context).textTheme.bodySmall, textAlign: TextAlign.left, ), ], diff --git a/lib/dashboard/home/tab_bar/credentials/widgets/display_description_widget.dart b/lib/dashboard/home/tab_bar/credentials/widgets/display_description_widget.dart index fbc88384a..b0a91e72a 100644 --- a/lib/dashboard/home/tab_bar/credentials/widgets/display_description_widget.dart +++ b/lib/dashboard/home/tab_bar/credentials/widgets/display_description_widget.dart @@ -1,5 +1,4 @@ import 'package:altme/dashboard/dashboard.dart'; -import 'package:altme/theme/app_theme/app_theme.dart'; import 'package:credential_manifest/credential_manifest.dart'; import 'package:flutter/material.dart'; @@ -19,14 +18,14 @@ class DisplayDescriptionWidget extends StatelessWidget { final object = displayMapping; final style = textColor == null - ? Theme.of(context).textTheme.credentialDescription + ? Theme.of(context).textTheme.bodyMedium : Theme.of(context) .textTheme - .credentialDescription + .bodyMedium! .copyWith(color: textColor); if (object is DisplayMappingText) { - return ManifestText(text: object.text, style: style); + return ManifestText(text: object.text, style: style!); } if (object is DisplayMappingPath) { @@ -35,10 +34,10 @@ class DisplayDescriptionWidget extends StatelessWidget { textList.addAll(getTextsFromCredential(e, credentialModel.data)); } if (textList.isNotEmpty) { - return ManifestText(text: textList.first, style: style); + return ManifestText(text: textList.first, style: style!); } if (object.fallback != null) { - return ManifestText(text: object.fallback ?? '', style: style); + return ManifestText(text: object.fallback ?? '', style: style!); } } diff --git a/lib/dashboard/home/tab_bar/credentials/widgets/display_description_widget_with_title.dart b/lib/dashboard/home/tab_bar/credentials/widgets/display_description_widget_with_title.dart index 2cab3369c..ae2b15b28 100644 --- a/lib/dashboard/home/tab_bar/credentials/widgets/display_description_widget_with_title.dart +++ b/lib/dashboard/home/tab_bar/credentials/widgets/display_description_widget_with_title.dart @@ -2,7 +2,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/app/shared/widget/transparent_ink_well.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:credential_manifest/credential_manifest.dart'; import 'package:flutter/material.dart'; @@ -25,21 +25,20 @@ class DisplayDescriptionWidgetWithTitle extends StatelessWidget { final textTheme = Theme.of(context).textTheme; final titleTheme = titleColor == null - ? textTheme.credentialFieldTitle - : textTheme.credentialFieldTitle.copyWith(color: titleColor); + ? textTheme.bodyMedium!.copyWith( + color: Theme.of(context).colorScheme.secondaryContainer, + ) + : textTheme.bodyMedium!.copyWith(color: titleColor); final valueTheme = valueColor == null - ? Theme.of(context).textTheme.credentialFieldDescription - : Theme.of(context) - .textTheme - .credentialFieldDescription - .copyWith(color: valueColor); + ? Theme.of(context).textTheme.bodyMedium + : Theme.of(context).textTheme.bodyMedium!.copyWith(color: valueColor); if (object is DisplayMappingText) { return DescriptionText( //padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 8), text: object.text, titleTheme: titleTheme, - valueTheme: valueTheme, + valueTheme: valueTheme!, ); } @@ -52,14 +51,14 @@ class DisplayDescriptionWidgetWithTitle extends StatelessWidget { return DescriptionText( text: textList.first, titleTheme: titleTheme, - valueTheme: valueTheme, + valueTheme: valueTheme!, ); } if (object.fallback != null) { return DescriptionText( text: object.fallback ?? '', titleTheme: titleTheme, - valueTheme: valueTheme, + valueTheme: valueTheme!, ); } } diff --git a/lib/dashboard/home/tab_bar/credentials/widgets/display_issuer.dart b/lib/dashboard/home/tab_bar/credentials/widgets/display_issuer.dart index 7a71d27a7..c7880751a 100644 --- a/lib/dashboard/home/tab_bar/credentials/widgets/display_issuer.dart +++ b/lib/dashboard/home/tab_bar/credentials/widgets/display_issuer.dart @@ -1,6 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/home/tab_bar/credentials/models/author/author.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class DisplayIssuer extends StatelessWidget { @@ -18,7 +18,7 @@ class DisplayIssuer extends StatelessWidget { Expanded( child: MyText( issuer.name, - style: Theme.of(context).textTheme.credentialIssuer, + style: Theme.of(context).textTheme.bodyMedium, ), ), ], diff --git a/lib/dashboard/home/tab_bar/credentials/widgets/display_wiget.dart b/lib/dashboard/home/tab_bar/credentials/widgets/display_wiget.dart index 5c87be341..b8d08c301 100644 --- a/lib/dashboard/home/tab_bar/credentials/widgets/display_wiget.dart +++ b/lib/dashboard/home/tab_bar/credentials/widgets/display_wiget.dart @@ -1,6 +1,5 @@ import 'package:altme/dashboard/dashboard.dart'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; import 'package:oidc4vc/oidc4vc.dart'; @@ -14,16 +13,13 @@ class DisplayWidget extends StatelessWidget { @override Widget build(BuildContext context) { - final titleColor = Theme.of(context).colorScheme.titleColor; - final valueColor = Theme.of(context).colorScheme.valueColor; + final titleColor = Theme.of(context).colorScheme.secondaryContainer; + final valueColor = Theme.of(context).colorScheme.onSurface; final textTheme = Theme.of(context).textTheme; - final titleTheme = - textTheme.credentialFieldTitle.copyWith(color: titleColor); - final valueTheme = Theme.of(context) - .textTheme - .credentialFieldDescription - .copyWith(color: valueColor); + final titleTheme = textTheme.bodyMedium!.copyWith(color: titleColor); + final valueTheme = + Theme.of(context).textTheme.bodyMedium!.copyWith(color: valueColor); return Column( crossAxisAlignment: CrossAxisAlignment.start, diff --git a/lib/dashboard/home/tab_bar/credentials/widgets/dummy_credential_image.dart b/lib/dashboard/home/tab_bar/credentials/widgets/dummy_credential_image.dart index e0f61e14e..94192d7f6 100644 --- a/lib/dashboard/home/tab_bar/credentials/widgets/dummy_credential_image.dart +++ b/lib/dashboard/home/tab_bar/credentials/widgets/dummy_credential_image.dart @@ -1,6 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; import 'package:oidc4vc/oidc4vc.dart'; @@ -92,9 +92,7 @@ class DummyCredentialImage extends StatelessWidget { alignment: Alignment.centerLeft, child: MyText( title, - style: Theme.of(context) - .textTheme - .credentialBaseTitleText, + style: Theme.of(context).textTheme.displayLarge, ), ), ), diff --git a/lib/dashboard/home/tab_bar/credentials/widgets/list_item.dart b/lib/dashboard/home/tab_bar/credentials/widgets/list_item.dart index 2290e4a33..21eaa4a6b 100644 --- a/lib/dashboard/home/tab_bar/credentials/widgets/list_item.dart +++ b/lib/dashboard/home/tab_bar/credentials/widgets/list_item.dart @@ -1,6 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:badges/badges.dart' as badges; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -54,7 +54,7 @@ class CredentialsListPageItem extends StatelessWidget { stackFit: StackFit.expand, badgeContent: Text( badgeCount.toString(), - style: Theme.of(context).textTheme.badgeStyle, + style: Theme.of(context).textTheme.bodySmall, textAlign: TextAlign.center, ), position: badges.BadgePosition.topEnd(end: 10, top: 30), diff --git a/lib/dashboard/home/tab_bar/credentials/widgets/skills_list_display.dart b/lib/dashboard/home/tab_bar/credentials/widgets/skills_list_display.dart index 1d5ea868d..d961430bc 100644 --- a/lib/dashboard/home/tab_bar/credentials/widgets/skills_list_display.dart +++ b/lib/dashboard/home/tab_bar/credentials/widgets/skills_list_display.dart @@ -1,5 +1,5 @@ import 'package:altme/dashboard/home/tab_bar/credentials/models/professional_experience_assessment/skill.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class SkillsListDisplay extends StatelessWidget { @@ -16,7 +16,7 @@ class SkillsListDisplay extends StatelessWidget { const Icon(Icons.arrow_right_alt_sharp), Text( skill.description, - style: Theme.of(context).textTheme.credentialFieldDescription, + style: Theme.of(context).textTheme.bodyMedium, ), ], ), diff --git a/lib/dashboard/home/tab_bar/credentials/widgets/white_close_button.dart b/lib/dashboard/home/tab_bar/credentials/widgets/white_close_button.dart index 4b7938af9..0b5319a47 100644 --- a/lib/dashboard/home/tab_bar/credentials/widgets/white_close_button.dart +++ b/lib/dashboard/home/tab_bar/credentials/widgets/white_close_button.dart @@ -16,7 +16,7 @@ class WhiteCloseButton extends StatelessWidget { padding: const EdgeInsets.all(1.5), child: Icon( Icons.close, - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, size: Sizes.icon, ), ), diff --git a/lib/dashboard/home/tab_bar/nft/view/nft_details_page.dart b/lib/dashboard/home/tab_bar/nft/view/nft_details_page.dart index e41e25771..0570ecaf0 100644 --- a/lib/dashboard/home/tab_bar/nft/view/nft_details_page.dart +++ b/lib/dashboard/home/tab_bar/nft/view/nft_details_page.dart @@ -1,7 +1,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:altme/wallet/cubit/wallet_cubit.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -100,7 +100,7 @@ class _NftDetailsViewState extends State { ), MyText( widget.nftModel.symbol ?? '--', - style: Theme.of(context).textTheme.bodySmall2, + style: Theme.of(context).textTheme.bodyMedium, maxLines: 1, minFontSize: 12, ), @@ -144,7 +144,7 @@ class _NftDetailsViewState extends State { Flexible( child: Text( nftModel.contractAddress, - style: Theme.of(context).textTheme.bodySmall3, + style: Theme.of(context).textTheme.bodyMedium, ), ), IconButton( @@ -175,7 +175,7 @@ class _NftDetailsViewState extends State { ), Text( nftModel.identifier ?? '?', - style: Theme.of(context).textTheme.bodySmall3, + style: Theme.of(context).textTheme.bodyMedium, ), ], ), @@ -193,7 +193,7 @@ class _NftDetailsViewState extends State { Flexible( child: Text( nftModel.creators?.join(', ') ?? '?', - style: Theme.of(context).textTheme.bodySmall3, + style: Theme.of(context).textTheme.bodyMedium, ), ), IconButton( @@ -225,7 +225,7 @@ class _NftDetailsViewState extends State { Flexible( child: Text( nftModel.publishers?.join(', ') ?? '?', - style: Theme.of(context).textTheme.bodySmall3, + style: Theme.of(context).textTheme.bodyMedium, ), ), IconButton( @@ -257,7 +257,7 @@ class _NftDetailsViewState extends State { ), Text( UiDate.normalFormat(nftModel.firstTime ?? nftModel.date) ?? '?', - style: Theme.of(context).textTheme.bodySmall3, + style: Theme.of(context).textTheme.bodyMedium, ), ], ), @@ -278,7 +278,7 @@ class _NftDetailsViewState extends State { Flexible( child: Text( nftModel.contractAddress, - style: Theme.of(context).textTheme.bodySmall3, + style: Theme.of(context).textTheme.bodyMedium, ), ), IconButton( @@ -309,7 +309,7 @@ class _NftDetailsViewState extends State { Flexible( child: Text( nftModel.minterAddress ?? '?', - style: Theme.of(context).textTheme.bodySmall3, + style: Theme.of(context).textTheme.bodyMedium, ), ), IconButton( @@ -341,7 +341,7 @@ class _NftDetailsViewState extends State { ), Text( UiDate.normalFormat(nftModel.lastMetadataSync) ?? '?', - style: Theme.of(context).textTheme.bodySmall3, + style: Theme.of(context).textTheme.bodyMedium, ), ], ), diff --git a/lib/dashboard/home/tab_bar/nft/view/nft_page.dart b/lib/dashboard/home/tab_bar/nft/view/nft_page.dart index 5482aab15..eb02b99ef 100644 --- a/lib/dashboard/home/tab_bar/nft/view/nft_page.dart +++ b/lib/dashboard/home/tab_bar/nft/view/nft_page.dart @@ -2,7 +2,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/dashboard/home/tab_bar/nft/widgets/widgets.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:altme/wallet/cubit/wallet_cubit.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -49,7 +49,7 @@ class _NftViewState extends State { return BasePage( scrollView: false, padding: EdgeInsets.zero, - backgroundColor: Theme.of(context).colorScheme.transparent, + backgroundColor: Colors.transparent, body: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, @@ -61,7 +61,7 @@ class _NftViewState extends State { child: MyText( l10n.nftListSubtitle, maxLines: 3, - style: Theme.of(context).textTheme.credentialCategorySubTitle, + style: Theme.of(context).textTheme.titleMedium, ), ), const SizedBox(height: Sizes.spaceXSmall), diff --git a/lib/dashboard/home/tab_bar/nft/widgets/my_collection_text.dart b/lib/dashboard/home/tab_bar/nft/widgets/my_collection_text.dart index 65059875b..1826fda05 100644 --- a/lib/dashboard/home/tab_bar/nft/widgets/my_collection_text.dart +++ b/lib/dashboard/home/tab_bar/nft/widgets/my_collection_text.dart @@ -1,5 +1,5 @@ import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class MyCollectionText extends StatelessWidget { @@ -12,7 +12,7 @@ class MyCollectionText extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: 4), child: Text( l10n.myCollection, - style: Theme.of(context).textTheme.infoTitle, + style: Theme.of(context).textTheme.titleLarge, ), ); } diff --git a/lib/dashboard/home/tab_bar/nft/widgets/nft_item.dart b/lib/dashboard/home/tab_bar/nft/widgets/nft_item.dart index 1fcc0e668..fffb811ea 100644 --- a/lib/dashboard/home/tab_bar/nft/widgets/nft_item.dart +++ b/lib/dashboard/home/tab_bar/nft/widgets/nft_item.dart @@ -1,6 +1,5 @@ import 'package:altme/app/app.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; class NftItem extends StatelessWidget { @@ -25,7 +24,7 @@ class NftItem extends StatelessWidget { return InkWell( onTap: onClick, child: BackgroundCard( - color: Theme.of(context).colorScheme.surfaceContainer, + color: Theme.of(context).colorScheme.surface.withOpacity(0.07), padding: const EdgeInsets.all(10), child: Column( crossAxisAlignment: CrossAxisAlignment.start, diff --git a/lib/dashboard/home/tab_bar/nft/widgets/nft_item_shimmer.dart b/lib/dashboard/home/tab_bar/nft/widgets/nft_item_shimmer.dart index a119eabf4..dd913f34f 100644 --- a/lib/dashboard/home/tab_bar/nft/widgets/nft_item_shimmer.dart +++ b/lib/dashboard/home/tab_bar/nft/widgets/nft_item_shimmer.dart @@ -1,5 +1,4 @@ import 'package:altme/app/app.dart'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; class NftItemShimmer extends StatelessWidget { @@ -8,7 +7,7 @@ class NftItemShimmer extends StatelessWidget { @override Widget build(BuildContext context) { return BackgroundCard( - color: Theme.of(context).colorScheme.surfaceContainer, + color: Theme.of(context).colorScheme.surface.withOpacity(0.07), padding: const EdgeInsets.all(10), child: const Column( crossAxisAlignment: CrossAxisAlignment.start, diff --git a/lib/dashboard/home/tab_bar/nft/widgets/nft_url_widget.dart b/lib/dashboard/home/tab_bar/nft/widgets/nft_url_widget.dart index 02fcdbb94..d433c3dbc 100644 --- a/lib/dashboard/home/tab_bar/nft/widgets/nft_url_widget.dart +++ b/lib/dashboard/home/tab_bar/nft/widgets/nft_url_widget.dart @@ -1,5 +1,4 @@ import 'package:altme/app/app.dart'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; class NftUrlWidget extends StatelessWidget { @@ -32,7 +31,7 @@ class NftUrlWidget extends StatelessWidget { end: Alignment.topRight, stops: const [0.3, 1.0], ), - color: Theme.of(context).colorScheme.tabBarNotSelected, + color: Theme.of(context).colorScheme.secondaryContainer, ), child: Center( child: Text( diff --git a/lib/dashboard/home/tab_bar/tab_controller/widgets/tab_bar.dart b/lib/dashboard/home/tab_bar/tab_controller/widgets/tab_bar.dart index 3e64add61..3f3b82dd6 100644 --- a/lib/dashboard/home/tab_bar/tab_controller/widgets/tab_bar.dart +++ b/lib/dashboard/home/tab_bar/tab_controller/widgets/tab_bar.dart @@ -1,5 +1,5 @@ import 'package:altme/app/app.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class MyTab extends StatelessWidget { @@ -32,8 +32,8 @@ class MyTab extends StatelessWidget { gradient: isSelected ? LinearGradient( colors: [ - AppTheme.darkThemeData.colorScheme.primary, - AppTheme.darkThemeData.colorScheme.secondary, + Theme.of(context).colorScheme.primary, + Theme.of(context).colorScheme.tertiary, ], begin: Alignment.bottomLeft, end: Alignment.topRight, @@ -42,7 +42,7 @@ class MyTab extends StatelessWidget { : null, color: isSelected ? null - : AppTheme.darkThemeData.colorScheme.tabBarNotSelected, + : Theme.of(context).colorScheme.onPrimaryContainer, ), child: Row( mainAxisAlignment: MainAxisAlignment.center, @@ -52,13 +52,12 @@ class MyTab extends StatelessWidget { const SizedBox( width: Sizes.spaceXSmall, ), - MyText( + Text( text, maxLines: 1, - minFontSize: 12, - style: AppTheme.darkThemeData.textTheme.title.copyWith( - color: isSelected ? null : Colors.grey[400], - ), + style: Theme.of(context).textTheme.headlineSmall!.copyWith( + color: isSelected ? null : Colors.grey[400], + ), overflow: TextOverflow.fade, ), ], diff --git a/lib/dashboard/home/tab_bar/tokens/all_tokens/view/all_tokens_page.dart b/lib/dashboard/home/tab_bar/tokens/all_tokens/view/all_tokens_page.dart index 4b5b723f0..ebf9cb1e8 100644 --- a/lib/dashboard/home/tab_bar/tokens/all_tokens/view/all_tokens_page.dart +++ b/lib/dashboard/home/tab_bar/tokens/all_tokens/view/all_tokens_page.dart @@ -1,7 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -130,7 +129,10 @@ class _AllTokensViewState extends State<_AllTokensView> { separatorBuilder: (_, __) { return Divider( height: 0.3, - color: Theme.of(context).colorScheme.borderColor, + color: Theme.of(context) + .colorScheme + .onSurface + .withOpacity(0.12), ); }, itemCount: state.filteredContracts.length, diff --git a/lib/dashboard/home/tab_bar/tokens/all_tokens/widgets/token_contract_item.dart b/lib/dashboard/home/tab_bar/tokens/all_tokens/widgets/token_contract_item.dart index 9f490f183..74cdb2bbd 100644 --- a/lib/dashboard/home/tab_bar/tokens/all_tokens/widgets/token_contract_item.dart +++ b/lib/dashboard/home/tab_bar/tokens/all_tokens/widgets/token_contract_item.dart @@ -1,6 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/home/home.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; import 'package:switcher/core/switcher_size.dart'; import 'package:switcher/switcher.dart'; @@ -34,17 +34,17 @@ class TokenContractItem extends StatelessWidget { ), title: MyText( tokenContractModel.name ?? tokenContractModel.symbol, - style: Theme.of(context).textTheme.listTileTitle, + style: Theme.of(context).textTheme.titleMedium, maxLength: 10, ), subtitle: MyText( tokenContractModel.symbol, - style: Theme.of(context).textTheme.listTileSubtitle, + style: Theme.of(context).textTheme.bodyMedium, ), trailing: Switcher( size: SwitcherSize.small, colorOn: Theme.of(context).colorScheme.primary, - colorOff: Theme.of(context).colorScheme.cardBackground, + colorOff: Theme.of(context).colorScheme.surface, onChanged: onChange, value: isOn, ), diff --git a/lib/dashboard/home/tab_bar/tokens/confirm_token_transaction/cubit/confirm_token_transaction_cubit.dart b/lib/dashboard/home/tab_bar/tokens/confirm_token_transaction/cubit/confirm_token_transaction_cubit.dart index d0b3c1979..39d23e328 100644 --- a/lib/dashboard/home/tab_bar/tokens/confirm_token_transaction/cubit/confirm_token_transaction_cubit.dart +++ b/lib/dashboard/home/tab_bar/tokens/confirm_token_transaction/cubit/confirm_token_transaction_cubit.dart @@ -119,7 +119,10 @@ class ConfirmTokenTransactionCubit extends Cubit { final sender = credentials.address; final reciever = EthereumAddress.fromHex(state.withdrawalAddress); - final web3RpcURL = await fetchRpcUrl(manageNetworkCubit.state.network); + final web3RpcURL = await fetchRpcUrl( + blockchainNetwork: manageNetworkCubit.state.network, + dotEnv: dotenv, + ); final (maxGas, priceOfGas, feeData) = await MWeb3Client.estimateEVMFee( web3RpcURL: web3RpcURL, @@ -424,7 +427,10 @@ class ConfirmTokenTransactionCubit extends Cubit { } else { final selectedEthereumNetwork = manageNetworkCubit.state.network; - final chainRpcUrl = await fetchRpcUrl(manageNetworkCubit.state.network); + final chainRpcUrl = await fetchRpcUrl( + blockchainNetwork: manageNetworkCubit.state.network, + dotEnv: dotenv, + ); await _sendContractInvocationOperationEVM( tokenAmount: state.totalAmount, diff --git a/lib/dashboard/home/tab_bar/tokens/confirm_token_transaction/view/confirm_token_transaction_page.dart b/lib/dashboard/home/tab_bar/tokens/confirm_token_transaction/view/confirm_token_transaction_page.dart index 095b91daa..6273a4642 100644 --- a/lib/dashboard/home/tab_bar/tokens/confirm_token_transaction/view/confirm_token_transaction_page.dart +++ b/lib/dashboard/home/tab_bar/tokens/confirm_token_transaction/view/confirm_token_transaction_page.dart @@ -1,7 +1,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:altme/wallet/cubit/wallet_cubit.dart'; import 'package:decimal/decimal.dart'; import 'package:dio/dio.dart'; @@ -192,7 +192,7 @@ class _ConfirmWithdrawalViewState extends State { const FromAccountWidget(isEnabled: false), const SizedBox(height: Sizes.spaceNormal), BackgroundCard( - color: Theme.of(context).colorScheme.cardBackground, + color: Theme.of(context).colorScheme.surface, child: SizedBox( width: double.infinity, child: Column( @@ -213,7 +213,7 @@ class _ConfirmWithdrawalViewState extends State { MyText( widget.withdrawalAddress, maxLines: 2, - style: Theme.of(context).textTheme.walletAddress, + style: Theme.of(context).textTheme.bodyMedium, ), ], ), diff --git a/lib/dashboard/home/tab_bar/tokens/confirm_token_transaction/widgets/confirm_transaction_details_card.dart b/lib/dashboard/home/tab_bar/tokens/confirm_token_transaction/widgets/confirm_transaction_details_card.dart index be508ceb1..b3675eb3d 100644 --- a/lib/dashboard/home/tab_bar/tokens/confirm_token_transaction/widgets/confirm_transaction_details_card.dart +++ b/lib/dashboard/home/tab_bar/tokens/confirm_token_transaction/widgets/confirm_transaction_details_card.dart @@ -1,7 +1,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/home/tab_bar/tokens/tokens.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:decimal/decimal.dart'; import 'package:flutter/material.dart'; @@ -31,7 +31,7 @@ class ConfirmTransactionDetailsCard extends StatelessWidget { Widget build(BuildContext context) { final l10n = context.l10n; return BackgroundCard( - color: Theme.of(context).colorScheme.cardBackground, + color: Theme.of(context).colorScheme.surface, child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, @@ -58,7 +58,7 @@ class ConfirmTransactionDetailsCard extends StatelessWidget { (double.parse(grandTotal) * tokenUSDRate) .decimalNumber(2) .formatNumber, - style: Theme.of(context).textTheme.bodySmall2, + style: Theme.of(context).textTheme.bodyMedium, ), ], ), @@ -91,7 +91,7 @@ class ConfirmTransactionDetailsCard extends StatelessWidget { Text( r'$' + networkFee!.feeInUSD.decimalNumber(2).formatNumber, - style: Theme.of(context).textTheme.bodySmall2, + style: Theme.of(context).textTheme.bodyMedium, ), ], ), @@ -119,7 +119,7 @@ class ConfirmTransactionDetailsCard extends StatelessWidget { (double.parse(amount) * tokenUSDRate) .decimalNumber(2) .formatNumber, - style: Theme.of(context).textTheme.bodySmall2, + style: Theme.of(context).textTheme.bodyMedium, ), ], ), @@ -137,7 +137,7 @@ class ConfirmTransactionDetailsCard extends StatelessWidget { ), child: Divider( height: 0.1, - color: Theme.of(context).colorScheme.borderColor, + color: Theme.of(context).colorScheme.onSurface.withOpacity(0.12), ), ); } diff --git a/lib/dashboard/home/tab_bar/tokens/confirm_token_transaction/widgets/transaction_done_dialog.dart b/lib/dashboard/home/tab_bar/tokens/confirm_token_transaction/widgets/transaction_done_dialog.dart index d721c40c7..7ceec06ec 100644 --- a/lib/dashboard/home/tab_bar/tokens/confirm_token_transaction/widgets/transaction_done_dialog.dart +++ b/lib/dashboard/home/tab_bar/tokens/confirm_token_transaction/widgets/transaction_done_dialog.dart @@ -1,6 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -40,7 +40,7 @@ class TransactionDoneDialog extends StatelessWidget { Widget build(BuildContext context) { final l10n = context.l10n; return AlertDialog( - backgroundColor: Theme.of(context).colorScheme.popupBackground, + backgroundColor: Theme.of(context).colorScheme.surface, surfaceTintColor: Colors.transparent, contentPadding: const EdgeInsets.symmetric( horizontal: Sizes.spaceNormal, @@ -63,13 +63,13 @@ class TransactionDoneDialog extends StatelessWidget { const SizedBox(height: Sizes.spaceNormal), Text( '$amountAndSymbol ${l10n.sent.toLowerCase()}', - style: Theme.of(context).textTheme.defaultDialogTitle, + style: Theme.of(context).textTheme.headlineMedium, textAlign: TextAlign.center, ), const SizedBox(height: Sizes.spaceSmall), Text( l10n.operationCompleted, - style: Theme.of(context).textTheme.defaultDialogBody, + style: Theme.of(context).textTheme.bodyMedium, textAlign: TextAlign.center, ), if (transactionHash != null) ...[ @@ -91,7 +91,7 @@ class TransactionDoneDialog extends StatelessWidget { }, child: Text( transactionHash!, - style: Theme.of(context).textTheme.defaultDialogBody.copyWith( + style: Theme.of(context).textTheme.bodyMedium!.copyWith( fontWeight: FontWeight.bold, decoration: TextDecoration.underline, ), diff --git a/lib/dashboard/home/tab_bar/tokens/insert_withdrawal_amount/widgets/token_amount_calculator.dart b/lib/dashboard/home/tab_bar/tokens/insert_withdrawal_amount/widgets/token_amount_calculator.dart index cac5a12f3..9fe4e0fa5 100644 --- a/lib/dashboard/home/tab_bar/tokens/insert_withdrawal_amount/widgets/token_amount_calculator.dart +++ b/lib/dashboard/home/tab_bar/tokens/insert_withdrawal_amount/widgets/token_amount_calculator.dart @@ -3,7 +3,7 @@ import 'dart:io'; import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -206,9 +206,8 @@ class _TokenAmountCalculatorPageState extends State { digitInnerMargin: EdgeInsets.zero, keyboardRowMargin: EdgeInsets.zero, digitBorderWidth: 0, - digitTextStyle: Theme.of(context) - .textTheme - .calculatorKeyboardDigitTextStyle, + digitTextStyle: + Theme.of(context).textTheme.headlineMedium, keyboardSize: constraint.biggest, ), leadingButton: KeyboardButton( diff --git a/lib/dashboard/home/tab_bar/tokens/insert_withdrawal_amount/widgets/token_select_box.dart b/lib/dashboard/home/tab_bar/tokens/insert_withdrawal_amount/widgets/token_select_box.dart index 6a91436a0..c5f504386 100644 --- a/lib/dashboard/home/tab_bar/tokens/insert_withdrawal_amount/widgets/token_select_box.dart +++ b/lib/dashboard/home/tab_bar/tokens/insert_withdrawal_amount/widgets/token_select_box.dart @@ -1,6 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:altme/wallet/cubit/wallet_cubit.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -82,7 +82,7 @@ class _TokenSelectBoxItem extends StatelessWidget { } }, child: BackgroundCard( - color: Theme.of(context).colorScheme.cardBackground, + color: Theme.of(context).colorScheme.surface, child: Column( crossAxisAlignment: CrossAxisAlignment.end, mainAxisSize: MainAxisSize.min, @@ -106,7 +106,7 @@ class _TokenSelectBoxItem extends StatelessWidget { ? tokenModel.symbol : tokenModel.name, maxLength: 10, - style: Theme.of(context).textTheme.listTileTitle, + style: Theme.of(context).textTheme.titleMedium, minFontSize: 10, textAlign: TextAlign.left, overflow: TextOverflow.ellipsis, @@ -145,7 +145,10 @@ class _TokenSelectBoxItem extends StatelessWidget { .decimalNumber(2) .formatNumber, style: Theme.of(context).textTheme.bodySmall?.copyWith( - color: Theme.of(context).colorScheme.greyText, + color: Theme.of(context) + .colorScheme + .onSurface + .withOpacity(0.6), ), ), ], diff --git a/lib/dashboard/home/tab_bar/tokens/receive/view/receive_page.dart b/lib/dashboard/home/tab_bar/tokens/receive/view/receive_page.dart index cc3ef7188..98629ddd8 100644 --- a/lib/dashboard/home/tab_bar/tokens/receive/view/receive_page.dart +++ b/lib/dashboard/home/tab_bar/tokens/receive/view/receive_page.dart @@ -1,6 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:qr_flutter/qr_flutter.dart'; @@ -60,7 +60,7 @@ class ReceivePage extends StatelessWidget { ), BackgroundCard( padding: const EdgeInsets.all(Sizes.spaceNormal), - color: Theme.of(context).colorScheme.cardBackground, + color: Theme.of(context).colorScheme.surface, child: Column( mainAxisSize: MainAxisSize.min, children: [ @@ -96,7 +96,7 @@ class ReceivePage extends StatelessWidget { Text( description, textAlign: TextAlign.center, - style: Theme.of(context).textTheme.bodySmall2, + style: Theme.of(context).textTheme.bodyMedium, ), const SizedBox( height: Sizes.spaceXLarge, diff --git a/lib/dashboard/home/tab_bar/tokens/send_receive_home/view/send_receive_home_page.dart b/lib/dashboard/home/tab_bar/tokens/send_receive_home/view/send_receive_home_page.dart index 34e3b3647..24d6eca1c 100644 --- a/lib/dashboard/home/tab_bar/tokens/send_receive_home/view/send_receive_home_page.dart +++ b/lib/dashboard/home/tab_bar/tokens/send_receive_home/view/send_receive_home_page.dart @@ -1,7 +1,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:altme/wallet/cubit/wallet_cubit.dart'; import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; @@ -47,7 +47,8 @@ class _SendReceiveHomePageState extends State { void initState() { Future.delayed(Duration.zero, () { sendReceiveHomeCubit.init( - baseUrl: context.read().state.network.apiUrl); + baseUrl: context.read().state.network.apiUrl, + ); }); super.initState(); } @@ -174,7 +175,7 @@ class _SendReceiveHomePageView extends StatelessWidget { state.selectedToken.balanceInUSD .decimalNumber(2) .formatNumber, - style: Theme.of(context).textTheme.normal, + style: Theme.of(context).textTheme.bodyMedium, ), const SizedBox( height: Sizes.spaceNormal, diff --git a/lib/dashboard/home/tab_bar/tokens/send_receive_home/widgets/change_network_bottom_sheet.dart b/lib/dashboard/home/tab_bar/tokens/send_receive_home/widgets/change_network_bottom_sheet.dart index a0ffe7888..ac0e17c4f 100644 --- a/lib/dashboard/home/tab_bar/tokens/send_receive_home/widgets/change_network_bottom_sheet.dart +++ b/lib/dashboard/home/tab_bar/tokens/send_receive_home/widgets/change_network_bottom_sheet.dart @@ -1,7 +1,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:altme/wallet/cubit/wallet_cubit.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -75,7 +75,7 @@ class _ChangeNetworkBottomSheetPageState const SizedBox(width: Sizes.spaceXSmall), Text( l10n.chooseNetWork, - style: Theme.of(context).textTheme.accountsText, + style: Theme.of(context).textTheme.headlineMedium, ), ], ), @@ -87,7 +87,8 @@ class _ChangeNetworkBottomSheetPageState border: Border.all( color: Theme.of(context) .colorScheme - .accountBottomSheetBorderColor, + .onSurface + .withOpacity(0.12), width: 0.2, ), borderRadius: const BorderRadius.all( @@ -108,7 +109,10 @@ class _ChangeNetworkBottomSheetPageState ), child: Divider( height: 0.2, - color: Theme.of(context).colorScheme.borderColor, + color: Theme.of(context) + .colorScheme + .onSurface + .withOpacity(0.12), ), ), ), diff --git a/lib/dashboard/home/tab_bar/tokens/send_receive_home/widgets/network_switcher_button.dart b/lib/dashboard/home/tab_bar/tokens/send_receive_home/widgets/network_switcher_button.dart index d3ee84189..2e40afb86 100644 --- a/lib/dashboard/home/tab_bar/tokens/send_receive_home/widgets/network_switcher_button.dart +++ b/lib/dashboard/home/tab_bar/tokens/send_receive_home/widgets/network_switcher_button.dart @@ -1,6 +1,5 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/drawer/drawer.dart'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -20,7 +19,7 @@ class NetworkSwitcherButton extends StatelessWidget { width: 6, height: 6, decoration: BoxDecoration( - color: Theme.of(context).colorScheme.activeColorOfNetwork, + color: Theme.of(context).colorScheme.primary, shape: BoxShape.circle, ), ), diff --git a/lib/dashboard/home/tab_bar/tokens/send_receive_home/widgets/recent_transactions.dart b/lib/dashboard/home/tab_bar/tokens/send_receive_home/widgets/recent_transactions.dart index cb7220635..03cbdbc87 100644 --- a/lib/dashboard/home/tab_bar/tokens/send_receive_home/widgets/recent_transactions.dart +++ b/lib/dashboard/home/tab_bar/tokens/send_receive_home/widgets/recent_transactions.dart @@ -1,7 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -26,7 +25,7 @@ class RecentTransactions extends StatelessWidget { final l10n = context.l10n; return Expanded( child: BackgroundCard( - color: Theme.of(context).colorScheme.cardBackground, + color: Theme.of(context).colorScheme.surface, margin: const EdgeInsets.all(Sizes.spaceSmall), padding: const EdgeInsets.all(Sizes.spaceSmall), child: Column( @@ -66,7 +65,10 @@ class RecentTransactions extends StatelessWidget { ), child: Divider( height: 0.2, - color: Theme.of(context).colorScheme.borderColor, + color: Theme.of(context) + .colorScheme + .onSurface + .withOpacity(0.12), ), ); }, diff --git a/lib/dashboard/home/tab_bar/tokens/send_receive_home/widgets/transaction_item.dart b/lib/dashboard/home/tab_bar/tokens/send_receive_home/widgets/transaction_item.dart index 2c9dd5511..f627f255c 100644 --- a/lib/dashboard/home/tab_bar/tokens/send_receive_home/widgets/transaction_item.dart +++ b/lib/dashboard/home/tab_bar/tokens/send_receive_home/widgets/transaction_item.dart @@ -1,7 +1,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:altme/wallet/wallet.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -44,12 +44,12 @@ class TransactionItem extends StatelessWidget { children: [ Text( operationModel.formatedDateTime, - style: Theme.of(context).textTheme.bodySmall2, + style: Theme.of(context).textTheme.bodyMedium, ), const Spacer(), Text( l10n.seeTransaction, - style: Theme.of(context).textTheme.bodySmall2, + style: Theme.of(context).textTheme.bodyMedium, ), const SizedBox( width: Sizes.spaceSmall, @@ -59,7 +59,7 @@ class TransactionItem extends StatelessWidget { ? (tokenUsdPrice! * amount).decimalNumber(2).formatNumber + r'$' : r'$--.--', - style: Theme.of(context).textTheme.bodySmall2, + style: Theme.of(context).textTheme.bodyMedium, ), ], ), @@ -103,15 +103,13 @@ class TransactionItem extends StatelessWidget { operationModel.status, style: Theme.of(context).textTheme.titleSmall?.copyWith( color: operationModel.status.toLowerCase() == 'applied' - ? Theme.of(context).colorScheme.transactionApplied + ? Theme.of(context).colorScheme.onTertiary : operationModel.status.toLowerCase() == 'failed' || operationModel.status.toLowerCase() == 'backtracked' - ? Theme.of(context).colorScheme.transactionFailed + ? Theme.of(context).colorScheme.error : operationModel.status.toLowerCase() == 'skipped' - ? Theme.of(context) - .colorScheme - .transactionSkipped + ? Theme.of(context).colorScheme.error : null, ), ), diff --git a/lib/dashboard/home/tab_bar/tokens/token_page/view/token_page.dart b/lib/dashboard/home/tab_bar/tokens/token_page/view/token_page.dart index 0967105f0..16221cc25 100644 --- a/lib/dashboard/home/tab_bar/tokens/token_page/view/token_page.dart +++ b/lib/dashboard/home/tab_bar/tokens/token_page/view/token_page.dart @@ -1,7 +1,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:altme/wallet/wallet.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -80,7 +80,7 @@ class _TokensViewState extends State { child: BasePage( scrollView: false, padding: EdgeInsets.zero, - backgroundColor: Theme.of(context).colorScheme.transparent, + backgroundColor: Colors.transparent, body: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, @@ -92,7 +92,7 @@ class _TokensViewState extends State { child: MyText( l10n.tokenListSubtitle, maxLines: 3, - style: Theme.of(context).textTheme.credentialCategorySubTitle, + style: Theme.of(context).textTheme.titleMedium, ), ), const SizedBox(height: Sizes.spaceXSmall), diff --git a/lib/dashboard/home/tab_bar/tokens/token_page/widgets/my_assets_text.dart b/lib/dashboard/home/tab_bar/tokens/token_page/widgets/my_assets_text.dart index 2538d65ef..051794e22 100644 --- a/lib/dashboard/home/tab_bar/tokens/token_page/widgets/my_assets_text.dart +++ b/lib/dashboard/home/tab_bar/tokens/token_page/widgets/my_assets_text.dart @@ -1,5 +1,5 @@ import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class MyAssetsText extends StatelessWidget { @@ -12,7 +12,7 @@ class MyAssetsText extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: 10), child: Text( l10n.myAssets, - style: Theme.of(context).textTheme.infoTitle, + style: Theme.of(context).textTheme.titleLarge, ), ); } diff --git a/lib/dashboard/home/tab_bar/tokens/token_page/widgets/token_item.dart b/lib/dashboard/home/tab_bar/tokens/token_page/widgets/token_item.dart index b99e19308..2278d90f5 100644 --- a/lib/dashboard/home/tab_bar/tokens/token_page/widgets/token_item.dart +++ b/lib/dashboard/home/tab_bar/tokens/token_page/widgets/token_item.dart @@ -1,6 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class TokenItem extends StatelessWidget { @@ -18,7 +18,7 @@ class TokenItem extends StatelessWidget { return Padding( padding: const EdgeInsets.only(bottom: 10), child: BackgroundCard( - color: Theme.of(context).colorScheme.surfaceContainer, + color: Theme.of(context).colorScheme.surface.withOpacity(0.07), padding: const EdgeInsets.symmetric(horizontal: 10), child: ListTile( contentPadding: EdgeInsets.zero, @@ -35,12 +35,12 @@ class TokenItem extends StatelessWidget { ), title: MyText( token.name.isEmpty ? token.symbol : token.name, - style: Theme.of(context).textTheme.listTileTitle, + style: Theme.of(context).textTheme.titleMedium, maxLength: 10, ), subtitle: MyText( token.symbol, - style: Theme.of(context).textTheme.listTileSubtitle, + style: Theme.of(context).textTheme.bodyMedium, ), trailing: Column( mainAxisSize: MainAxisSize.max, @@ -54,7 +54,7 @@ class TokenItem extends StatelessWidget { : ('''${token.calculatedBalanceInDouble.decimalNumber(token.decimalsToShow).formatNumber} ${token.symbol}'''), style: Theme.of(context) .textTheme - .listTileTitle + .titleMedium! .copyWith(fontSize: 13), ), ), @@ -64,7 +64,7 @@ class TokenItem extends StatelessWidget { ? '****' : (r'$' + token.balanceInUSD.decimalNumber(2).formatNumber), - style: Theme.of(context).textTheme.listTileSubtitle, + style: Theme.of(context).textTheme.bodyMedium, ), ), ], diff --git a/lib/dashboard/home/tab_bar/tokens/token_page/widgets/token_item_shimmer.dart b/lib/dashboard/home/tab_bar/tokens/token_page/widgets/token_item_shimmer.dart index f9480cf3a..43755fe51 100644 --- a/lib/dashboard/home/tab_bar/tokens/token_page/widgets/token_item_shimmer.dart +++ b/lib/dashboard/home/tab_bar/tokens/token_page/widgets/token_item_shimmer.dart @@ -1,5 +1,4 @@ import 'package:altme/app/app.dart'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; class TokenItemShimmer extends StatelessWidget { @@ -10,7 +9,7 @@ class TokenItemShimmer extends StatelessWidget { return Padding( padding: const EdgeInsets.only(bottom: 10), child: BackgroundCard( - color: Theme.of(context).colorScheme.surfaceContainer, + color: Theme.of(context).colorScheme.surface.withOpacity(0.07), padding: const EdgeInsets.symmetric(horizontal: 10), child: const ListTile( minVerticalPadding: 0, diff --git a/lib/dashboard/home/tab_bar/tokens/widgets/account_select_box_view.dart b/lib/dashboard/home/tab_bar/tokens/widgets/account_select_box_view.dart index 319ef909b..419cbf144 100644 --- a/lib/dashboard/home/tab_bar/tokens/widgets/account_select_box_view.dart +++ b/lib/dashboard/home/tab_bar/tokens/widgets/account_select_box_view.dart @@ -1,6 +1,5 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; -import 'package:altme/theme/theme.dart'; import 'package:altme/wallet/wallet.dart'; import 'package:flutter/material.dart'; @@ -25,7 +24,7 @@ class AccountSelectBoxView extends StatelessWidget { @override Widget build(BuildContext context) { return BackgroundCard( - color: Theme.of(context).colorScheme.cardBackground, + color: Theme.of(context).colorScheme.surface, child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, @@ -41,9 +40,9 @@ class AccountSelectBoxView extends StatelessWidget { Theme( data: Theme.of(context).copyWith( unselectedWidgetColor: Theme.of(context).colorScheme.onPrimary, - dividerColor: Theme.of(context).colorScheme.cardBackground, - splashColor: Theme.of(context).colorScheme.transparent, - highlightColor: Theme.of(context).colorScheme.transparent, + dividerColor: Theme.of(context).colorScheme.surface, + splashColor: Colors.transparent, + highlightColor: Colors.transparent, colorScheme: ColorScheme.dark( primary: Theme.of(context).colorScheme.onPrimary, ), @@ -79,7 +78,10 @@ class AccountSelectBoxView extends StatelessWidget { ), child: Divider( height: 0.2, - color: Theme.of(context).colorScheme.borderColor, + color: Theme.of(context) + .colorScheme + .onSurface + .withOpacity(0.12), ), ), ), diff --git a/lib/dashboard/home/tab_bar/tokens/widgets/select_account_item.dart b/lib/dashboard/home/tab_bar/tokens/widgets/select_account_item.dart index 3342b5454..ffec5260c 100644 --- a/lib/dashboard/home/tab_bar/tokens/widgets/select_account_item.dart +++ b/lib/dashboard/home/tab_bar/tokens/widgets/select_account_item.dart @@ -1,6 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:altme/wallet/model/model.dart'; import 'package:flutter/material.dart'; @@ -43,14 +43,14 @@ class SelectedAccountItem extends StatelessWidget { maxLines: 1, minFontSize: 12, overflow: TextOverflow.ellipsis, - style: Theme.of(context).textTheme.title, + style: Theme.of(context).textTheme.headlineSmall, ), subtitle: Padding( padding: const EdgeInsets.only(top: Sizes.spaceXSmall), child: MyText( cryptoAccountData.walletAddress, maxLines: 2, - style: Theme.of(context).textTheme.walletAddress, + style: Theme.of(context).textTheme.bodyMedium, ), ), trailing: onPressed == null diff --git a/lib/dashboard/home/tab_bar/tokens/widgets/select_box_account_item.dart b/lib/dashboard/home/tab_bar/tokens/widgets/select_box_account_item.dart index 354cab5f6..822d69ad1 100644 --- a/lib/dashboard/home/tab_bar/tokens/widgets/select_box_account_item.dart +++ b/lib/dashboard/home/tab_bar/tokens/widgets/select_box_account_item.dart @@ -1,6 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:altme/wallet/model/model.dart'; import 'package:flutter/material.dart'; @@ -32,7 +32,7 @@ class SelectBoxAccountItem extends StatelessWidget { horizontalTitleGap: 0, leading: Checkbox( value: isSelected, - fillColor: MaterialStateProperty.all( + fillColor: WidgetStateProperty.all( Theme.of(context).colorScheme.primary, ), checkColor: Theme.of(context).colorScheme.onPrimary, @@ -54,7 +54,7 @@ class SelectBoxAccountItem extends StatelessWidget { maxLines: 1, minFontSize: 12, overflow: TextOverflow.ellipsis, - style: Theme.of(context).textTheme.title, + style: Theme.of(context).textTheme.headlineSmall, ), ), ], @@ -63,7 +63,7 @@ class SelectBoxAccountItem extends StatelessWidget { padding: const EdgeInsets.symmetric(vertical: Sizes.spaceXSmall), child: MyText( walletAddressExtracted, - style: Theme.of(context).textTheme.walletAddress, + style: Theme.of(context).textTheme.bodyMedium, ), ), ); diff --git a/lib/dashboard/home/tab_bar/tokens/widgets/withdrawal_address_input.dart b/lib/dashboard/home/tab_bar/tokens/widgets/withdrawal_address_input.dart index 8957727ea..ce0bfee14 100644 --- a/lib/dashboard/home/tab_bar/tokens/widgets/withdrawal_address_input.dart +++ b/lib/dashboard/home/tab_bar/tokens/widgets/withdrawal_address_input.dart @@ -1,7 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -74,7 +73,7 @@ class _WithdrawalAddressInputPageState Widget build(BuildContext context) { final l10n = context.l10n; return BackgroundCard( - color: Theme.of(context).colorScheme.cardBackground, + color: Theme.of(context).colorScheme.surface, padding: const EdgeInsets.only( top: Sizes.spaceSmall, right: Sizes.spaceSmall, diff --git a/lib/dashboard/missing_creentials/view/missing_credentials_page.dart b/lib/dashboard/missing_creentials/view/missing_credentials_page.dart index 6c1d919c9..c9a7f1c0c 100644 --- a/lib/dashboard/missing_creentials/view/missing_credentials_page.dart +++ b/lib/dashboard/missing_creentials/view/missing_credentials_page.dart @@ -2,7 +2,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; import 'package:altme/query_by_example/model/query.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:credential_manifest/credential_manifest.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -85,7 +85,7 @@ class MissingCredentialsView extends StatelessWidget { children: [ Text( '''${l10n.youAreMissing} ${state.dummyCredentials.length} ${l10n.credentialsRequestedBy} $issuerName.''', - style: Theme.of(context).textTheme.discoverFieldDescription, + style: Theme.of(context).textTheme.bodyMedium, textAlign: TextAlign.center, ), const SizedBox(height: 20), diff --git a/lib/dashboard/profile/cubit/profile_cubit.dart b/lib/dashboard/profile/cubit/profile_cubit.dart index ec0907734..782fc61c3 100644 --- a/lib/dashboard/profile/cubit/profile_cubit.dart +++ b/lib/dashboard/profile/cubit/profile_cubit.dart @@ -66,7 +66,8 @@ class ProfileCubit extends Cubit { } Future load() async { - final ssiKey = await getSecureStorage.get(SecureStorageKeys.ssiKey); + final ssiKey = await secureStorageProvider.get(SecureStorageKeys.ssiKey); + if (ssiKey == null) { return emit(state.copyWith(status: AppStatus.success)); } @@ -300,6 +301,7 @@ class ProfileCubit extends Cubit { enterpriseWalletName: profileSetting.generalOptions.profileName, ); } + await update(profileModel); } catch (e, s) { log.e( diff --git a/lib/dashboard/profile/models/profile_setting.dart b/lib/dashboard/profile/models/profile_setting.dart index 1e15cf724..95b0751c4 100644 --- a/lib/dashboard/profile/models/profile_setting.dart +++ b/lib/dashboard/profile/models/profile_setting.dart @@ -571,7 +571,8 @@ class CustomOidc4VcProfile extends Equatable { final String? clientSecret; final bool cryptoHolderBinding; final DidKeyType - defaultDid; //TODO(bibash): temporary solution to avoid who have chosen 12 + defaultDid; + // TODO(bibash): temporary solution to avoid who have chosen 12 @JsonKey( includeFromJson: true, fromJson: oidc4vciDraftFromJson, diff --git a/lib/dashboard/qr_code/qr_code_scan/cubit/qr_code_scan_cubit.dart b/lib/dashboard/qr_code/qr_code_scan/cubit/qr_code_scan_cubit.dart index 8d1822921..9a072754b 100644 --- a/lib/dashboard/qr_code/qr_code_scan/cubit/qr_code_scan_cubit.dart +++ b/lib/dashboard/qr_code/qr_code_scan/cubit/qr_code_scan_cubit.dart @@ -78,7 +78,7 @@ class QRCodeScanCubit extends Cubit { await Future.delayed(const Duration(milliseconds: 1000)); emit(state.loading(isScan: true)); try { - final isInternetAvailable = await isConnected(); + final isInternetAvailable = await isConnectedToInternet(); if (!isInternetAvailable) { throw NetworkException( message: NetworkError.NETWORK_ERROR_NO_INTERNET_CONNECTION, @@ -893,8 +893,8 @@ class QRCodeScanCubit extends Cubit { data: { 'error': 'invalid_request', 'error_description': - 'The input_descriptors is required in the presentation_definition ' - 'object', + 'The input_descriptors is required in the presentation_definition' + ' object', }, ); } @@ -1140,6 +1140,7 @@ class QRCodeScanCubit extends Cubit { stateValue: stateValue, clientType: customOidc4vcProfile.clientType, proofHeaderType: customOidc4vcProfile.proofHeader, + dio: client.dio, ); String? url; @@ -1275,6 +1276,7 @@ class QRCodeScanCubit extends Cubit { final openIdConfiguration = await oidc4vc.getOpenIdConfig( baseUrl: issuer, isAuthorizationServer: false, + dio: client.dio, ); if (savedAccessToken == null) { @@ -1297,6 +1299,7 @@ class QRCodeScanCubit extends Cubit { redirectUri: Parameters.oidc4vcUniversalLink, openIdConfiguration: openIdConfiguration, clientAssertion: clientAssertion, + dio: client.dio, ); savedAccessToken = accessToken; diff --git a/lib/dashboard/qr_code/qr_code_scan/view/qr_camera_view.dart b/lib/dashboard/qr_code/qr_code_scan/view/qr_camera_view.dart index 53c5ba0bd..62b550183 100644 --- a/lib/dashboard/qr_code/qr_code_scan/view/qr_camera_view.dart +++ b/lib/dashboard/qr_code/qr_code_scan/view/qr_camera_view.dart @@ -2,7 +2,7 @@ import 'dart:async'; import 'dart:io'; import 'package:altme/app/app.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:camera/camera.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -20,10 +20,11 @@ class QrCameraView extends StatefulWidget { }); final String title; - final Function(InputImage inputImage) onImage; + final dynamic Function(InputImage inputImage) onImage; final VoidCallback? onCameraFeedReady; final VoidCallback? onDetectorViewModeChanged; - final Function(CameraLensDirection direction)? onCameraLensDirectionChanged; + final dynamic Function(CameraLensDirection direction)? + onCameraLensDirectionChanged; final CameraLensDirection initialCameraLensDirection; @override @@ -34,13 +35,19 @@ class _QrCameraViewState extends State { static List _cameras = []; CameraController? _controller; int _cameraIndex = -1; - double _currentZoomLevel = 1.0; - double _minAvailableZoom = 1.0; - double _maxAvailableZoom = 1.0; - double _minAvailableExposureOffset = 0.0; - double _maxAvailableExposureOffset = 0.0; - double _currentExposureOffset = 0.0; - bool _changingCameraLens = false; + // ignore: unused_field + double _currentZoomLevel = 1; + // ignore: unused_field + double _minAvailableZoom = 1; + // ignore: unused_field + double _maxAvailableZoom = 1; + // ignore: unused_field + double _minAvailableExposureOffset = 0; + // ignore: unused_field + double _maxAvailableExposureOffset = 0; + // ignore: unused_field + double _currentExposureOffset = 0; + final bool _changingCameraLens = false; @override void initState() { @@ -48,7 +55,7 @@ class _QrCameraViewState extends State { _initialize(); } - void _initialize() async { + Future _initialize() async { if (_cameras.isEmpty) { _cameras = await availableCameras(); } @@ -75,18 +82,18 @@ class _QrCameraViewState extends State { if (_controller == null) return Container(); if (_controller?.value.isInitialized == false) return Container(); return Scaffold( - backgroundColor: Theme.of(context).colorScheme.background, + backgroundColor: Theme.of(context).colorScheme.surface, appBar: AppBar( title: Text( widget.title, textAlign: TextAlign.center, - style: Theme.of(context).textTheme.appBar, + style: Theme.of(context).textTheme.titleLarge, ), leading: const BackLeadingButton(), - backgroundColor: Theme.of(context).colorScheme.background, + backgroundColor: Theme.of(context).colorScheme.surface, ), body: ColoredBox( - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, child: Center( child: _changingCameraLens ? Container() @@ -106,7 +113,8 @@ class _QrCameraViewState extends State { final camera = _cameras[_cameraIndex]; _controller = CameraController( camera, - // Set to ResolutionPreset.high. Do NOT set it to ResolutionPreset.max because for some phones does NOT work. + // Set to ResolutionPreset.high. Do NOT set it to ResolutionPreset.max + // because for some phones does NOT work. ResolutionPreset.high, enableAudio: false, imageFormatGroup: Platform.isAndroid @@ -133,9 +141,11 @@ class _QrCameraViewState extends State { }); _controller?.startImageStream(_processCameraImage).then((value) { if (widget.onCameraFeedReady != null) { + // ignore: prefer_null_aware_method_calls widget.onCameraFeedReady!(); } if (widget.onCameraLensDirectionChanged != null) { + // ignore: prefer_null_aware_method_calls widget.onCameraLensDirectionChanged!(camera.lensDirection); } }); @@ -171,8 +181,6 @@ class _QrCameraViewState extends State { // in both platforms `rotation` and `camera.lensDirection` can be used to compensate `x` and `y` coordinates on a canvas: https://github.com/flutter-ml/google_ml_kit_flutter/blob/master/packages/example/lib/vision_detector_views/painters/coordinates_translator.dart final camera = _cameras[_cameraIndex]; final sensorOrientation = camera.sensorOrientation; - // print( - // 'lensDirection: ${camera.lensDirection}, sensorOrientation: $sensorOrientation, ${_controller?.value.deviceOrientation} ${_controller?.value.lockedCaptureOrientation} ${_controller?.value.isCaptureOrientationLocked}'); InputImageRotation? rotation; if (Platform.isIOS) { rotation = InputImageRotationValue.fromRawValue(sensorOrientation); diff --git a/lib/dashboard/qr_code/qr_display/view/qr_display_page.dart b/lib/dashboard/qr_code/qr_display/view/qr_display_page.dart index b4ce1fbf8..897a58b03 100644 --- a/lib/dashboard/qr_code/qr_display/view/qr_display_page.dart +++ b/lib/dashboard/qr_code/qr_display/view/qr_display_page.dart @@ -36,7 +36,7 @@ class QrCodeDisplayPage extends StatelessWidget { child: QrImageView( data: data, size: 200, - foregroundColor: Theme.of(context).colorScheme.onBackground, + foregroundColor: Theme.of(context).colorScheme.onSurface, ), ), const SizedBox(height: 10), diff --git a/lib/dashboard/qr_code/widget/developer_mode_dialog.dart b/lib/dashboard/qr_code/widget/developer_mode_dialog.dart index bdc1824c8..0ecc508f8 100644 --- a/lib/dashboard/qr_code/widget/developer_mode_dialog.dart +++ b/lib/dashboard/qr_code/widget/developer_mode_dialog.dart @@ -1,6 +1,5 @@ import 'package:altme/app/app.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; class DeveloperModeDialog extends StatelessWidget { @@ -18,8 +17,8 @@ class DeveloperModeDialog extends StatelessWidget { @override Widget build(BuildContext context) { final color = Theme.of(context).colorScheme.primary; - final background = Theme.of(context).colorScheme.popupBackground; - final textColor = Theme.of(context).colorScheme.dialogText; + final background = Theme.of(context).colorScheme.surface; + final textColor = Theme.of(context).colorScheme.onSurface; final l10n = context.l10n; return AlertDialog( diff --git a/lib/dashboard/search/widgets/search_item_shimmer.dart b/lib/dashboard/search/widgets/search_item_shimmer.dart index 4f6a53964..2135e6db2 100644 --- a/lib/dashboard/search/widgets/search_item_shimmer.dart +++ b/lib/dashboard/search/widgets/search_item_shimmer.dart @@ -1,5 +1,4 @@ import 'package:altme/app/app.dart'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; class SearchItemShimmer extends StatelessWidget { @@ -11,7 +10,7 @@ class SearchItemShimmer extends StatelessWidget { padding: const EdgeInsets.only(bottom: 10), child: BackgroundCard( padding: EdgeInsets.zero, - color: Theme.of(context).colorScheme.surfaceContainer, + color: Theme.of(context).colorScheme.surface.withOpacity(0.07), child: const AspectRatio( aspectRatio: Sizes.credentialAspectRatio, child: ShimmerWidget.rectangular( diff --git a/lib/dashboard/select_network_fee_bottom_sheet/widgets/select_network_fee_bottom_sheet.dart b/lib/dashboard/select_network_fee_bottom_sheet/widgets/select_network_fee_bottom_sheet.dart index 3feeb8a26..c1b167db5 100644 --- a/lib/dashboard/select_network_fee_bottom_sheet/widgets/select_network_fee_bottom_sheet.dart +++ b/lib/dashboard/select_network_fee_bottom_sheet/widgets/select_network_fee_bottom_sheet.dart @@ -1,7 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -114,7 +113,10 @@ class _SelectNetworkFeeBottomSheetViewState ), child: Divider( height: 0.2, - color: Theme.of(context).colorScheme.borderColor, + color: Theme.of(context) + .colorScheme + .onSurface + .withOpacity(0.12), ), ), ); diff --git a/lib/dashboard/select_network_fee_bottom_sheet/widgets/select_network_fee_item.dart b/lib/dashboard/select_network_fee_bottom_sheet/widgets/select_network_fee_item.dart index 06f45ce5a..2fa14f495 100644 --- a/lib/dashboard/select_network_fee_bottom_sheet/widgets/select_network_fee_item.dart +++ b/lib/dashboard/select_network_fee_bottom_sheet/widgets/select_network_fee_item.dart @@ -1,7 +1,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/home/tab_bar/tokens/tokens.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class SelectNetworkFeeItem extends StatelessWidget { @@ -25,7 +25,7 @@ class SelectNetworkFeeItem extends StatelessWidget { horizontalTitleGap: 0, leading: Checkbox( value: isSelected, - fillColor: MaterialStateProperty.all( + fillColor: WidgetStateProperty.all( Theme.of(context).colorScheme.inversePrimary, ), checkColor: Theme.of(context).colorScheme.primary, @@ -53,7 +53,7 @@ class SelectNetworkFeeItem extends StatelessWidget { ), MyText( '''\$${networkFeeModel.feeInUSD == 0.0 ? '--.--' : networkFeeModel.feeInUSD.decimalNumber(4).formatNumber}''', - style: Theme.of(context).textTheme.bodySmall2, + style: Theme.of(context).textTheme.bodyMedium, ), ], ), diff --git a/lib/dashboard/src/view/dashboard_page.dart b/lib/dashboard/src/view/dashboard_page.dart index a8b7bf23b..eb6a8a6fd 100644 --- a/lib/dashboard/src/view/dashboard_page.dart +++ b/lib/dashboard/src/view/dashboard_page.dart @@ -6,7 +6,6 @@ import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/enterprise/cubit/enterprise_cubit.dart'; import 'package:altme/kyc_verification/kyc_verification.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/splash/cubit/splash_cubit.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -162,12 +161,12 @@ class _DashboardViewState extends State { .helpCenterOptions .displayChatSupport; - return WillPopScope( - onWillPop: () async { + return PopScope( + canPop: false, + onPopInvoked: (_) async { if (scaffoldKey.currentState!.isDrawerOpen) { Navigator.of(context).pop(); } - return false; }, child: BasePage( scrollView: false, diff --git a/lib/dashboard/src/widgets/bottom_bar_item.dart b/lib/dashboard/src/widgets/bottom_bar_item.dart index f833bff30..6c932b422 100644 --- a/lib/dashboard/src/widgets/bottom_bar_item.dart +++ b/lib/dashboard/src/widgets/bottom_bar_item.dart @@ -1,4 +1,3 @@ -import 'package:altme/theme/theme.dart'; import 'package:badges/badges.dart' as badges; import 'package:flutter/material.dart'; @@ -32,7 +31,7 @@ class BottomBarItem extends StatelessWidget { showBadge: badgeCount > 0, badgeContent: Text( badgeCount.toString(), - style: Theme.of(context).textTheme.badgeStyle, + style: Theme.of(context).textTheme.bodySmall, textAlign: TextAlign.center, ), child: Container( @@ -41,7 +40,10 @@ class BottomBarItem extends StatelessWidget { AssetImage(icon), color: isSelected ? Theme.of(context).colorScheme.onPrimary - : Theme.of(context).colorScheme.unSelectedLabel, + : Theme.of(context) + .colorScheme + .onSurface + .withOpacity(0.6), size: 20, ), ), @@ -50,10 +52,13 @@ class BottomBarItem extends StatelessWidget { text.toUpperCase(), softWrap: false, overflow: TextOverflow.fade, - style: Theme.of(context).textTheme.bottomBar.copyWith( + style: Theme.of(context).textTheme.bodySmall!.copyWith( color: isSelected ? Theme.of(context).colorScheme.onPrimary - : Theme.of(context).colorScheme.unSelectedLabel, + : Theme.of(context) + .colorScheme + .onSurface + .withOpacity(0.6), ), ), ], diff --git a/lib/dashboard/src/widgets/home_title_leading.dart b/lib/dashboard/src/widgets/home_title_leading.dart index d0a81c7b8..15f71dd49 100644 --- a/lib/dashboard/src/widgets/home_title_leading.dart +++ b/lib/dashboard/src/widgets/home_title_leading.dart @@ -1,5 +1,4 @@ import 'package:altme/app/app.dart'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; class HomeTitleLeading extends StatelessWidget { @@ -15,7 +14,7 @@ class HomeTitleLeading extends StatelessWidget { return IconButton( icon: ImageIcon( const AssetImage(IconStrings.settings), - color: Theme.of(context).colorScheme.leadingButton, + color: Theme.of(context).colorScheme.onSurface, ), onPressed: onPressed, ); diff --git a/lib/dashboard/src/widgets/new_content.dart b/lib/dashboard/src/widgets/new_content.dart index c9d0a34d7..8f2b08512 100644 --- a/lib/dashboard/src/widgets/new_content.dart +++ b/lib/dashboard/src/widgets/new_content.dart @@ -1,5 +1,5 @@ import 'package:altme/app/app.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class NewContent extends StatelessWidget { @@ -19,7 +19,7 @@ class NewContent extends StatelessWidget { ), child: Text( version, - style: Theme.of(context).textTheme.newVersionTitle, + style: Theme.of(context).textTheme.headlineSmall, textAlign: TextAlign.center, ), ), @@ -38,7 +38,7 @@ class NewContent extends StatelessWidget { padding: EdgeInsets.only(top: 3), child: Icon( Icons.check_circle, - color: background, + color: Colors.white, size: 18, ), ), @@ -47,11 +47,8 @@ class NewContent extends StatelessWidget { child: Text( features[index], textAlign: TextAlign.left, - style: Theme.of(context) - .textTheme - .defaultDialogBody - .copyWith( - color: background, + style: Theme.of(context).textTheme.bodyMedium!.copyWith( + color: Colors.white, ), ), ), diff --git a/lib/dashboard/user_pin/view/user_pin_page.dart b/lib/dashboard/user_pin/view/user_pin_page.dart index fd119ac7a..5fc5a038d 100644 --- a/lib/dashboard/user_pin/view/user_pin_page.dart +++ b/lib/dashboard/user_pin/view/user_pin_page.dart @@ -37,8 +37,7 @@ class UserPinPage extends StatelessWidget { @override Widget build(BuildContext context) { return BlocProvider( - create: (context) => - PinCodeViewCubit(profileCubit: context.read()), + create: (context) => PinCodeViewCubit(), child: UserPinView( onCancel: onCancel, onProceed: onProceed, @@ -79,44 +78,35 @@ class _UserPinViewState extends State { @override Widget build(BuildContext context) { final l10n = context.l10n; - return BlocBuilder( - builder: (context, state) { - return WillPopScope( - onWillPop: () async => false, - child: BasePage( - backgroundColor: Theme.of(context).colorScheme.background, - scrollView: false, - body: PinCodeWidget( - title: widget.txCode?.description ?? - l10n.pleaseInsertTheSecredCodeReceived, - passwordEnteredCallback: _onPasscodeEntered, - passwordDigits: widget.txCode?.length ?? 4, - deleteButton: Text( - l10n.delete, - style: Theme.of(context).textTheme.labelLarge, - ), - cancelButton: Text( - l10n.cancel, - style: Theme.of(context).textTheme.labelLarge, - ), - cancelCallback: _onPasscodeCancelled, - isValidCallback: () { - Navigator.pop(context); - widget.onProceed.call(pinCodeValue!); - }, - shouldTriggerVerification: _verificationNotifier.stream, - ), + return WillPopScope( + onWillPop: () async => false, + child: BasePage( + backgroundColor: Theme.of(context).colorScheme.surface, + scrollView: false, + body: PinCodeWidget( + title: widget.txCode?.description ?? + l10n.pleaseInsertTheSecredCodeReceived, + passwordDigits: widget.txCode?.length ?? 4, + deleteButton: Text( + l10n.delete, + style: Theme.of(context).textTheme.labelLarge, ), - ); - }, + cancelButton: Text( + l10n.cancel, + style: Theme.of(context).textTheme.labelLarge, + ), + cancelCallback: _onPasscodeCancelled, + isValidCallback: () { + Navigator.pop(context); + widget.onProceed + .call(context.read().state.enteredPasscode); + }, + isUserPin: true, + ), + ), ); } - Future _onPasscodeEntered(String enteredPasscode) async { - pinCodeValue = enteredPasscode; - _verificationNotifier.add(true); - } - void _onPasscodeCancelled() { Navigator.of(context).pop(); widget.onCancel.call(); diff --git a/lib/enterprise/cubit/enterprise_cubit.dart b/lib/enterprise/cubit/enterprise_cubit.dart index 3220bdd68..aed347b72 100644 --- a/lib/enterprise/cubit/enterprise_cubit.dart +++ b/lib/enterprise/cubit/enterprise_cubit.dart @@ -139,19 +139,8 @@ class EnterpriseCubit extends Cubit { data: data, ); - /// parse - final header = profileCubit.jwtDecode.parseJwtHeader(response as String); - final issuerKid = header['kid'].toString(); - final did = issuerKid.split('#')[0]; - - /// verify - final VerificationType isVerified = await verifyEncodedData( - issuer: did, - jwtDecode: profileCubit.jwtDecode, - jwt: response, - ); - - final profileSettingJson = profileCubit.jwtDecode.parseJwt(response); + final profileSettingJson = + profileCubit.jwtDecode.parseJwt(response as String); await profileCubit.secureStorageProvider.set( SecureStorageKeys.enterpriseProfileSetting, @@ -166,28 +155,6 @@ class EnterpriseCubit extends Cubit { profileType: ProfileType.enterprise, ); - // if (isVerified == VerificationType.verified) { - // emit( - // state.copyWith( - // status: AppStatus.idle, - // message: const StateMessage.info( - // showDialog: true, - // stringMessage: 'Verfied', - // ), - // ), - // ); - // } else { - // emit( - // state.copyWith( - // status: AppStatus.idle, - // message: const StateMessage.info( - // showDialog: true, - // stringMessage: 'Not Verfied', - // ), - // ), - // ); - // } - emit( state.copyWith( status: AppStatus.success, @@ -479,19 +446,8 @@ class EnterpriseCubit extends Cubit { data: data, ); - /// parse - final header = profileCubit.jwtDecode.parseJwtHeader(response as String); - final issuerKid = header['kid'].toString(); - final did = issuerKid.split('#')[0]; - - /// verify - final VerificationType isVerified = await verifyEncodedData( - issuer: did, - jwtDecode: profileCubit.jwtDecode, - jwt: response, - ); - - final profileSettingJson = profileCubit.jwtDecode.parseJwt(response); + final profileSettingJson = + profileCubit.jwtDecode.parseJwt(response as String); await profileCubit.secureStorageProvider.set( SecureStorageKeys.enterpriseProfileSetting, diff --git a/lib/enterprise/cubit/enterprise_state.dart b/lib/enterprise/cubit/enterprise_state.dart index 994bc02f3..226dd6145 100644 --- a/lib/enterprise/cubit/enterprise_state.dart +++ b/lib/enterprise/cubit/enterprise_state.dart @@ -30,7 +30,6 @@ class EnterpriseState extends Equatable { EnterpriseState copyWith({ required StateMessage? message, AppStatus? status, - WalletProviderType? walletProviderType, }) { return EnterpriseState( status: status ?? this.status, diff --git a/lib/import_wallet/view/import_from_wallet_page.dart b/lib/import_wallet/view/import_from_wallet_page.dart index 018dd4863..fc16f47cb 100644 --- a/lib/import_wallet/view/import_from_wallet_page.dart +++ b/lib/import_wallet/view/import_from_wallet_page.dart @@ -4,7 +4,7 @@ import 'package:altme/import_wallet/import_wallet.dart'; import 'package:altme/l10n/l10n.dart'; import 'package:altme/onboarding/onboarding.dart'; import 'package:altme/splash/splash.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:altme/wallet/cubit/wallet_cubit.dart'; import 'package:did_kit/did_kit.dart'; import 'package:flutter/material.dart'; @@ -187,7 +187,7 @@ class _ImportFromOtherWalletViewState extends State { : 54, ), fillColor: Colors.transparent, - hintStyle: Theme.of(context).textTheme.hintTextFieldStyle, + hintStyle: Theme.of(context).textTheme.bodyMedium, maxLines: 10, borderRadius: Sizes.normalRadius, controller: mnemonicController, @@ -205,7 +205,7 @@ class _ImportFromOtherWalletViewState extends State { margin: const EdgeInsets.all(Sizes.spaceNormal), decoration: BoxDecoration( shape: BoxShape.circle, - color: Theme.of(context).colorScheme.checkMarkColor, + color: Theme.of(context).colorScheme.onTertiary, ), child: const Icon( Icons.check, @@ -218,14 +218,14 @@ class _ImportFromOtherWalletViewState extends State { const SizedBox(height: Sizes.spaceSmall), Text( l10n.recoveryPhraseDescriptions, - style: Theme.of(context).textTheme.infoSubtitle.copyWith( + style: Theme.of(context).textTheme.titleMedium!.copyWith( fontSize: 12, ), ), const SizedBox(height: Sizes.spaceLarge), Text( l10n.privateKeyDescriptions, - style: Theme.of(context).textTheme.infoSubtitle.copyWith( + style: Theme.of(context).textTheme.titleMedium!.copyWith( fontSize: 12, ), ), diff --git a/lib/import_wallet/view/import_wallet_page.dart b/lib/import_wallet/view/import_wallet_page.dart index fdbaf2fa6..f67deda18 100644 --- a/lib/import_wallet/view/import_wallet_page.dart +++ b/lib/import_wallet/view/import_wallet_page.dart @@ -5,7 +5,7 @@ import 'package:altme/l10n/l10n.dart'; import 'package:altme/onboarding/cubit/onboarding_cubit.dart'; import 'package:altme/onboarding/onboarding.dart'; import 'package:altme/splash/splash.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:altme/wallet/cubit/wallet_cubit.dart'; import 'package:did_kit/did_kit.dart'; import 'package:flutter/material.dart'; @@ -29,7 +29,7 @@ class ImportWalletPage extends StatelessWidget { accountName: accountName, isFromOnboarding: isFromOnboarding, ), - settings: const RouteSettings(name: '/onBoardingRecoveryPage'), + settings: const RouteSettings(name: '/ImportWalletPage'), ); final String? accountName; @@ -177,8 +177,7 @@ class _ImportWalletViewState extends State { ? l10n.importWalletHintText(54) : l10n.importWalletHintTextRecoveryPhraseOnly, fillColor: Colors.transparent, - hintStyle: - Theme.of(context).textTheme.hintTextFieldStyle, + hintStyle: Theme.of(context).textTheme.bodyMedium, maxLines: 10, borderRadius: Sizes.normalRadius, controller: mnemonicController, @@ -196,9 +195,7 @@ class _ImportWalletViewState extends State { margin: const EdgeInsets.all(Sizes.spaceNormal), decoration: BoxDecoration( shape: BoxShape.circle, - color: Theme.of(context) - .colorScheme - .checkMarkColor, + color: Theme.of(context).colorScheme.onTertiary, ), child: const Icon( Icons.check, @@ -236,7 +233,7 @@ class _ImportWalletViewState extends State { const SizedBox(height: Sizes.spaceLarge), Text( l10n.recoveryPhraseDescriptions, - style: Theme.of(context).textTheme.infoSubtitle.copyWith( + style: Theme.of(context).textTheme.titleMedium!.copyWith( fontSize: 12, ), ), @@ -244,7 +241,7 @@ class _ImportWalletViewState extends State { const SizedBox(height: Sizes.spaceLarge), Text( l10n.privateKeyDescriptions, - style: Theme.of(context).textTheme.infoSubtitle.copyWith( + style: Theme.of(context).textTheme.titleMedium!.copyWith( fontSize: 12, ), ), diff --git a/lib/import_wallet/widgets/wallet_type_item.dart b/lib/import_wallet/widgets/wallet_type_item.dart index 8d387509a..312117bc7 100644 --- a/lib/import_wallet/widgets/wallet_type_item.dart +++ b/lib/import_wallet/widgets/wallet_type_item.dart @@ -1,6 +1,5 @@ import 'package:altme/app/app.dart'; import 'package:altme/import_wallet/import_wallet.dart'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; class WalletTypeItem extends StatelessWidget { @@ -20,7 +19,9 @@ class WalletTypeItem extends StatelessWidget { color: Theme.of(context).highlightColor, borderRadius: const BorderRadius.all(Radius.circular(Sizes.smallRadius)), - border: Border.all(color: Theme.of(context).colorScheme.borderColor), + border: Border.all( + color: Theme.of(context).colorScheme.onSurface.withOpacity(0.12), + ), ), child: Row( mainAxisSize: MainAxisSize.max, diff --git a/lib/issuer_websites_page/widget/kyc_button.dart b/lib/issuer_websites_page/widget/kyc_button.dart index fdec566ec..e5705fc0e 100644 --- a/lib/issuer_websites_page/widget/kyc_button.dart +++ b/lib/issuer_websites_page/widget/kyc_button.dart @@ -1,6 +1,5 @@ import 'package:altme/app/app.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; class KYCButton extends StatelessWidget { @@ -17,7 +16,7 @@ class KYCButton extends StatelessWidget { await showDialog( context: context, builder: (context) => AlertDialog( - backgroundColor: Theme.of(context).colorScheme.popupBackground, + backgroundColor: Theme.of(context).colorScheme.surface, surfaceTintColor: Colors.transparent, contentPadding: const EdgeInsets.only( top: 24, diff --git a/lib/kyc_verification/widgets/finish_kyc_dialog.dart b/lib/kyc_verification/widgets/finish_kyc_dialog.dart index 8f440d34f..d3502c627 100644 --- a/lib/kyc_verification/widgets/finish_kyc_dialog.dart +++ b/lib/kyc_verification/widgets/finish_kyc_dialog.dart @@ -1,6 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class FinishKycDialog extends StatelessWidget { @@ -12,7 +12,7 @@ class FinishKycDialog extends StatelessWidget { Widget build(BuildContext context) { final l10n = context.l10n; return AlertDialog( - backgroundColor: Theme.of(context).colorScheme.popupBackground, + backgroundColor: Theme.of(context).colorScheme.surface, surfaceTintColor: Colors.transparent, contentPadding: const EdgeInsets.symmetric(horizontal: 20, vertical: 15), shape: const RoundedRectangleBorder( @@ -32,13 +32,13 @@ class FinishKycDialog extends StatelessWidget { const SizedBox(height: Sizes.spaceSmall), Text( l10n.finishedVerificationTitle, - style: Theme.of(context).textTheme.defaultDialogTitle, + style: Theme.of(context).textTheme.headlineMedium, textAlign: TextAlign.center, ), const SizedBox(height: Sizes.spaceSmall), Text( l10n.finishedVerificationDescription, - style: Theme.of(context).textTheme.defaultDialogBody, + style: Theme.of(context).textTheme.bodyMedium, textAlign: TextAlign.center, ), const SizedBox(height: Sizes.spaceSmall), diff --git a/lib/kyc_verification/widgets/kyc_dialog.dart b/lib/kyc_verification/widgets/kyc_dialog.dart index f7f7448c6..9fd36ab72 100644 --- a/lib/kyc_verification/widgets/kyc_dialog.dart +++ b/lib/kyc_verification/widgets/kyc_dialog.dart @@ -1,6 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class KycDialog extends StatelessWidget { @@ -15,7 +15,7 @@ class KycDialog extends StatelessWidget { Widget build(BuildContext context) { final l10n = context.l10n; return AlertDialog( - backgroundColor: Theme.of(context).colorScheme.popupBackground, + backgroundColor: Theme.of(context).colorScheme.surface, surfaceTintColor: Colors.transparent, contentPadding: const EdgeInsets.symmetric(horizontal: 20, vertical: 15), shape: const RoundedRectangleBorder( @@ -29,13 +29,13 @@ class KycDialog extends StatelessWidget { const SizedBox(height: 15), Text( l10n.kycDialogTitle, - style: Theme.of(context).textTheme.kycDialogTitle, + style: Theme.of(context).textTheme.headlineMedium, textAlign: TextAlign.center, ), const SizedBox(height: Sizes.spaceLarge), Text( l10n.idVerificationProcess, - style: Theme.of(context).textTheme.kycDialogBodySmall, + style: Theme.of(context).textTheme.headlineMedium, textAlign: TextAlign.center, ), Padding( @@ -61,7 +61,7 @@ class KycDialog extends StatelessWidget { ), Text( l10n.idCheck, - style: Theme.of(context).textTheme.kycDialogBody, + style: Theme.of(context).textTheme.bodyMedium, textAlign: TextAlign.center, ), ], @@ -76,7 +76,7 @@ class KycDialog extends StatelessWidget { ), Text( l10n.facialRecognition, - style: Theme.of(context).textTheme.kycDialogBody, + style: Theme.of(context).textTheme.bodyMedium, textAlign: TextAlign.center, ), ], @@ -91,8 +91,7 @@ class KycDialog extends StatelessWidget { verticalSpacing: 18, fontSize: 18, borderRadius: 20, - backgroundColor: - Theme.of(context).colorScheme.kycVerifyButtonColor, + backgroundColor: Theme.of(context).colorScheme.primary, onPressed: () { Navigator.of(context).pop(); startVerificationPressed.call(); @@ -109,12 +108,13 @@ class KycDialog extends StatelessWidget { Image.asset( IconStrings.lockCircle, width: Sizes.icon, - color: Theme.of(context).colorScheme.kycKeyIconColor, + color: + Theme.of(context).colorScheme.onSurface.withOpacity(0.6), ), Expanded( child: Text( l10n.kycDialogFooter, - style: Theme.of(context).textTheme.kycDialogFooter, + style: Theme.of(context).textTheme.bodySmall, ), ), ], diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index 8d18c3f84..82362eac2 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -1057,5 +1057,23 @@ "theWalletIsSuspended": "The wallet is suspended.", "clientTypeTitle": "Wallet Client_id Scheme", "confidentialClient": "Confidential Client", - "jwkThumbprintP256Key": "JWK Thumbprint P-256 Key" + "jwkThumbprintP256Key": "JWK Thumbprint P-256 Key", + "walletBlockedPopupTitle": "Blockerd 10 minutes", + "walletBlockedPopupDescription": "Too many failed attempts, your wallet is blocked for your security.\nYou can reset your wallet in order to use servives again.", + "deleteMyWalletForWrontPincodeTitle": "Account blocked after 3 unsuccessful attempts", + "deleteMyWalletForWrontPincodeDescription": "For your security you must reset you wallet to use our services again.", + "walletBloced": "Account blocked", + "deleteMyWallet": "Delete my account", + "pincodeRules": "Your secret code can't be a sequence or have 4 identical digit.", + "pincodeSerie": "You can't have 4 identical digits.", + "pincodeSequence": "You can't have a sequence of 4 digits.", + "pincodeDifferent": "Incorrect code.\nBoth codes are not the same.", + "codeSecretIncorrectDescription": "Be carreful, {count} attempt{plural} left.", + "@codeSecretIncorrectDescription": { + "description": "", + "placeholders": { + "count": {}, + "plural": {} + } + } } diff --git a/lib/l10n/untranslated.json b/lib/l10n/untranslated.json index ab3681128..f3ccf8ad7 100644 --- a/lib/l10n/untranslated.json +++ b/lib/l10n/untranslated.json @@ -3,14 +3,36 @@ "french", "spanish", "catalan", - "english" + "english", + "walletBlockedPopupTitle", + "walletBlockedPopupDescription", + "deleteMyWalletForWrontPincodeTitle", + "deleteMyWalletForWrontPincodeDescription", + "walletBloced", + "deleteMyWallet", + "pincodeRules", + "pincodeSerie", + "pincodeSequence", + "pincodeDifferent", + "codeSecretIncorrectDescription" ], "es": [ "french", "spanish", "catalan", - "english" + "english", + "walletBlockedPopupTitle", + "walletBlockedPopupDescription", + "deleteMyWalletForWrontPincodeTitle", + "deleteMyWalletForWrontPincodeDescription", + "walletBloced", + "deleteMyWallet", + "pincodeRules", + "pincodeSerie", + "pincodeSequence", + "pincodeDifferent", + "codeSecretIncorrectDescription" ], "fr": [ @@ -30,6 +52,17 @@ "theWalletIsSuspended", "clientTypeTitle", "confidentialClient", - "jwkThumbprintP256Key" + "jwkThumbprintP256Key", + "walletBlockedPopupTitle", + "walletBlockedPopupDescription", + "deleteMyWalletForWrontPincodeTitle", + "deleteMyWalletForWrontPincodeDescription", + "walletBloced", + "deleteMyWallet", + "pincodeRules", + "pincodeSerie", + "pincodeSequence", + "pincodeDifferent", + "codeSecretIncorrectDescription" ] } diff --git a/lib/main_staging.dart b/lib/main_staging.dart index 839dd5c93..86ac92700 100644 --- a/lib/main_staging.dart +++ b/lib/main_staging.dart @@ -7,14 +7,11 @@ import 'package:altme/app/app.dart'; import 'package:altme/bootstrap.dart'; -import 'package:device_preview/device_preview.dart'; void main() { bootstrap( - () => DevicePreview( - builder: (context) => const App( - flavorMode: FlavorMode.staging, - ), + () => const App( + flavorMode: FlavorMode.staging, ), ); } diff --git a/lib/oidc4vc/get_and_add_deffered_credential.dart b/lib/oidc4vc/get_and_add_deffered_credential.dart index a178361db..81915e146 100644 --- a/lib/oidc4vc/get_and_add_deffered_credential.dart +++ b/lib/oidc4vc/get_and_add_deffered_credential.dart @@ -3,6 +3,7 @@ import 'package:altme/credentials/credentials.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/oidc4vc/oidc4vc.dart'; +import 'package:dio/dio.dart'; import 'package:jwt_decode/jwt_decode.dart'; import 'package:oidc4vc/oidc4vc.dart'; @@ -53,6 +54,7 @@ Future getAndAddDefferedCredential({ deferredCredentialEndpoint: credentialModel.pendingInfo!.deferredCredentialEndpoint, body: body, + dio: Dio(), ); await addOIDC4VCCredential( diff --git a/lib/oidc4vc/get_authorization_uri_for_issuer.dart b/lib/oidc4vc/get_authorization_uri_for_issuer.dart index 29855277b..ad56a236c 100644 --- a/lib/oidc4vc/get_authorization_uri_for_issuer.dart +++ b/lib/oidc4vc/get_authorization_uri_for_issuer.dart @@ -98,6 +98,8 @@ Future getAuthorizationUriForIssuer({ vcFormatType: vcFormatType, clientAssertion: clientAssertion, secureAuthorizedFlow: secureAuthorizedFlow, + credentialOfferJson: credentialOfferJson, + dio: client.dio, ); final requirePushedAuthorizationRequests = @@ -108,6 +110,7 @@ Future getAuthorizationUriForIssuer({ 'Content-Type': 'application/x-www-form-urlencoded', }; + /// error we shuld get it from final response = await client.post( '$authorizationEndpoint/par', headers: headers, diff --git a/lib/oidc4vc/get_credential.dart b/lib/oidc4vc/get_credential.dart index d0b8bdd33..e1a3774e0 100644 --- a/lib/oidc4vc/get_credential.dart +++ b/lib/oidc4vc/get_credential.dart @@ -1,5 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; +import 'package:dio/dio.dart'; import 'package:oidc4vc/oidc4vc.dart'; /// Retreive credential_type from url @@ -61,6 +62,7 @@ Future< cnonce: nonce, authorizationDetails: authorizationDetails, openIdConfiguration: openIdConfiguration, + dio: Dio(), ); return ( diff --git a/lib/oidc4vc/verify_encoded_data.dart b/lib/oidc4vc/verify_encoded_data.dart index 69cf5fae9..9edf38a38 100644 --- a/lib/oidc4vc/verify_encoded_data.dart +++ b/lib/oidc4vc/verify_encoded_data.dart @@ -1,4 +1,5 @@ import 'package:altme/app/app.dart'; +import 'package:dio/dio.dart'; import 'package:jwt_decode/jwt_decode.dart'; import 'package:oidc4vc/oidc4vc.dart'; @@ -34,6 +35,7 @@ Future verifyEncodedData({ publicJwk: publicKeyJwk, fromStatusList: fromStatusList, isCachingEnabled: isCachingEnabled, + dio: Dio(), ); return verificationType; } diff --git a/lib/onboarding/activate_biometircs/view/activate_biometrics_page.dart b/lib/onboarding/activate_biometircs/view/activate_biometrics_page.dart index bbdd40d02..0949c33d1 100644 --- a/lib/onboarding/activate_biometircs/view/activate_biometrics_page.dart +++ b/lib/onboarding/activate_biometircs/view/activate_biometrics_page.dart @@ -11,19 +11,23 @@ class ActiviateBiometricsPage extends StatelessWidget { super.key, required this.isFromOnboarding, required this.onAction, + required this.localAuthApi, }); final bool isFromOnboarding; final void Function({required bool isEnabled}) onAction; + final LocalAuthApi localAuthApi; static Route route({ required void Function({required bool isEnabled}) onAction, required bool isFromOnboarding, + required LocalAuthApi localAuthApi, }) => RightToLeftRoute( builder: (context) => ActiviateBiometricsPage( onAction: onAction, isFromOnboarding: isFromOnboarding, + localAuthApi: localAuthApi, ), settings: const RouteSettings(name: '/activiateBiometricsPage'), ); @@ -37,6 +41,7 @@ class ActiviateBiometricsPage extends StatelessWidget { child: ActivateBiometricsView( isFromOnboarding: isFromOnboarding, onAction: onAction, + localAuthApi: localAuthApi, ), ); } @@ -47,18 +52,18 @@ class ActivateBiometricsView extends StatefulWidget { super.key, required this.isFromOnboarding, required this.onAction, + required this.localAuthApi, }); final bool isFromOnboarding; final void Function({required bool isEnabled}) onAction; + final LocalAuthApi localAuthApi; @override State createState() => _ActivateBiometricsViewState(); } class _ActivateBiometricsViewState extends State { - final localAuthApi = LocalAuthApi(); - @override void initState() { context.read().load(); @@ -104,11 +109,14 @@ class _ActivateBiometricsViewState extends State { BiometricsSwitch( value: isEnabled, onChange: (value) async { - final hasBiometrics = await localAuthApi.hasBiometrics(); + final hasBiometrics = + await widget.localAuthApi.hasBiometrics(); + if (hasBiometrics) { - final result = await localAuthApi.authenticate( + final result = await widget.localAuthApi.authenticate( localizedReason: l10n.scanFingerprintToAuthenticate, ); + if (result) { context .read() diff --git a/lib/onboarding/activate_biometircs/widgets/biometrics_switch.dart b/lib/onboarding/activate_biometircs/widgets/biometrics_switch.dart index f282640c1..1b609ee8d 100644 --- a/lib/onboarding/activate_biometircs/widgets/biometrics_switch.dart +++ b/lib/onboarding/activate_biometircs/widgets/biometrics_switch.dart @@ -1,6 +1,6 @@ import 'package:altme/app/shared/shared.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; @@ -18,7 +18,7 @@ class BiometricsSwitch extends StatelessWidget { final l10n = context.l10n; return BackgroundCard( padding: const EdgeInsets.all(Sizes.spaceSmall), - color: Theme.of(context).colorScheme.cardHighlighted, + color: Theme.of(context).colorScheme.surface, child: Row( children: [ Image.asset( @@ -39,7 +39,7 @@ class BiometricsSwitch extends StatelessWidget { ' (${l10n.option})', maxLines: 1, minFontSize: 10, - style: Theme.of(context).textTheme.bodySmall2, + style: Theme.of(context).textTheme.bodyMedium, ), const Spacer(), CupertinoSwitch( diff --git a/lib/onboarding/gen_phrase/cubit/onboarding_gen_phrase_cubit.dart b/lib/onboarding/gen_phrase/cubit/onboarding_gen_phrase_cubit.dart index 6f7ddcc63..1a8b54451 100644 --- a/lib/onboarding/gen_phrase/cubit/onboarding_gen_phrase_cubit.dart +++ b/lib/onboarding/gen_phrase/cubit/onboarding_gen_phrase_cubit.dart @@ -8,7 +8,6 @@ import 'package:equatable/equatable.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:json_annotation/json_annotation.dart'; import 'package:key_generator/key_generator.dart'; -import 'package:secure_storage/secure_storage.dart'; part 'onboarding_gen_phrase_cubit.g.dart'; @@ -16,7 +15,6 @@ part 'onboarding_gen_phrase_state.dart'; class OnBoardingGenPhraseCubit extends Cubit { OnBoardingGenPhraseCubit({ - required this.secureStorageProvider, required this.keyGenerator, required this.didKitProvider, required this.homeCubit, @@ -26,7 +24,6 @@ class OnBoardingGenPhraseCubit extends Cubit { required this.profileCubit, }) : super(const OnBoardingGenPhraseState()); - final SecureStorageProvider secureStorageProvider; final KeyGenerator keyGenerator; final DIDKitProvider didKitProvider; @@ -43,7 +40,6 @@ class OnBoardingGenPhraseCubit extends Cubit { try { await generateAccount( mnemonic: mnemonic, - secureStorageProvider: secureStorageProvider, keyGenerator: keyGenerator, didKitProvider: didKitProvider, homeCubit: homeCubit, @@ -52,7 +48,8 @@ class OnBoardingGenPhraseCubit extends Cubit { altmeChatSupportCubit: altmeChatSupportCubit, profileCubit: profileCubit, ); - await secureStorageProvider.set( + + await profileCubit.secureStorageProvider.set( SecureStorageKeys.hasVerifiedMnemonics, 'no', ); diff --git a/lib/onboarding/gen_phrase/view/onboarding_gen_phrase.dart b/lib/onboarding/gen_phrase/view/onboarding_gen_phrase.dart index b3fff078b..3506c4d38 100644 --- a/lib/onboarding/gen_phrase/view/onboarding_gen_phrase.dart +++ b/lib/onboarding/gen_phrase/view/onboarding_gen_phrase.dart @@ -4,7 +4,7 @@ import 'package:altme/l10n/l10n.dart'; import 'package:altme/onboarding/cubit/onboarding_cubit.dart'; import 'package:altme/onboarding/onboarding.dart'; import 'package:altme/splash/cubit/splash_cubit.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:altme/wallet/wallet.dart'; import 'package:bip39/bip39.dart' as bip39; import 'package:did_kit/did_kit.dart'; @@ -12,8 +12,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:key_generator/key_generator.dart'; -import 'package:secure_storage/secure_storage.dart'; - class OnBoardingGenPhrasePage extends StatelessWidget { const OnBoardingGenPhrasePage({super.key}); @@ -26,7 +24,6 @@ class OnBoardingGenPhrasePage extends StatelessWidget { Widget build(BuildContext context) { return BlocProvider( create: (context) => OnBoardingGenPhraseCubit( - secureStorageProvider: getSecureStorage, didKitProvider: DIDKitProvider(), keyGenerator: KeyGenerator(), homeCubit: context.read(), @@ -133,14 +130,17 @@ class _OnBoardingGenPhraseViewState extends State { // }, // child: Text( // l10n.copyToClipboard, - // style: Theme.of(context).textTheme.copyToClipBoard, + // style: Theme.of(context).textTheme.bodyMedium!, // ), // ), const SizedBox(height: Sizes.spaceLarge), Text( l10n.onboardingAltmeMessage, textAlign: TextAlign.center, - style: Theme.of(context).textTheme.genPhraseSubmessage, + style: + Theme.of(context).textTheme.headlineSmall!.copyWith( + color: Theme.of(context).colorScheme.tertiary, + ), ), ], ), @@ -161,7 +161,7 @@ class _OnBoardingGenPhraseViewState extends State { text: l10n.verifyLater, verticalSpacing: 18, backgroundColor: - Theme.of(context).colorScheme.defaultDialogDark, + Theme.of(context).colorScheme.primaryContainer, onPressed: () async { await context .read() @@ -176,7 +176,7 @@ class _OnBoardingGenPhraseViewState extends State { text: l10n.verifyNow, verticalSpacing: 18, onPressed: () { - Navigator.of(context).push( + Navigator.of(context).push( OnBoardingVerifyPhrasePage.route( mnemonic: mnemonic!, isFromOnboarding: true, diff --git a/lib/onboarding/helper_function/helper_function.dart b/lib/onboarding/helper_function/helper_function.dart index f2c6585a7..e0a953c1b 100644 --- a/lib/onboarding/helper_function/helper_function.dart +++ b/lib/onboarding/helper_function/helper_function.dart @@ -4,11 +4,9 @@ import 'package:altme/splash/splash.dart'; import 'package:altme/wallet/wallet.dart'; import 'package:did_kit/did_kit.dart'; import 'package:key_generator/key_generator.dart'; -import 'package:secure_storage/secure_storage.dart'; Future generateAccount({ required List mnemonic, - required SecureStorageProvider secureStorageProvider, required KeyGenerator keyGenerator, required DIDKitProvider didKitProvider, required HomeCubit homeCubit, @@ -20,7 +18,7 @@ Future generateAccount({ final mnemonicFormatted = mnemonic.join(' '); /// ssi wallet - await secureStorageProvider.set( + await profileCubit.secureStorageProvider.set( SecureStorageKeys.ssiMnemonic, mnemonicFormatted, ); @@ -31,7 +29,8 @@ Future generateAccount({ accountType: AccountType.ssi, ); - await secureStorageProvider.set(SecureStorageKeys.ssiKey, ssiKey); + await profileCubit.secureStorageProvider + .set(SecureStorageKeys.ssiKey, ssiKey); /// create profile await profileCubit.load(); diff --git a/lib/onboarding/onboarding.dart b/lib/onboarding/onboarding.dart index 4a305b9fa..51fef7362 100644 --- a/lib/onboarding/onboarding.dart +++ b/lib/onboarding/onboarding.dart @@ -3,7 +3,6 @@ export 'gen_phrase/onboarding_gen_phrase.dart'; export 'helper_function/helper_function.dart'; export 'protect_wallet/protect_wallet.dart'; export 'starter/starter.dart'; -export 'third/onboarding_third.dart'; export 'tos/onboarding_terms.dart'; export 'verify_phrase/onboarding_verify_phrase.dart'; export 'wallet_ready/wallet_ready.dart'; diff --git a/lib/onboarding/protect_wallet/view/protect_wallet_page.dart b/lib/onboarding/protect_wallet/view/protect_wallet_page.dart index 8c415ef6b..bfa8fa940 100644 --- a/lib/onboarding/protect_wallet/view/protect_wallet_page.dart +++ b/lib/onboarding/protect_wallet/view/protect_wallet_page.dart @@ -6,14 +6,13 @@ import 'package:altme/onboarding/cubit/onboarding_cubit.dart'; import 'package:altme/onboarding/onboarding.dart'; import 'package:altme/pin_code/pin_code.dart'; import 'package:altme/splash/splash.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:altme/wallet/wallet.dart'; import 'package:bip39/bip39.dart' as bip39; import 'package:did_kit/did_kit.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:key_generator/key_generator.dart'; -import 'package:secure_storage/secure_storage.dart'; class ProtectWalletPage extends StatelessWidget { const ProtectWalletPage({ @@ -38,7 +37,6 @@ class ProtectWalletPage extends StatelessWidget { Widget build(BuildContext context) { return BlocProvider( create: (_) => OnBoardingGenPhraseCubit( - secureStorageProvider: getSecureStorage, didKitProvider: DIDKitProvider(), keyGenerator: KeyGenerator(), homeCubit: context.read(), @@ -47,8 +45,15 @@ class ProtectWalletPage extends StatelessWidget { altmeChatSupportCubit: context.read(), profileCubit: context.read(), ), - child: ProtectWalletView( - routeType: routeType, + child: Builder( + builder: (context) { + return ProtectWalletView( + routeType: routeType, + profileCubit: context.read(), + onBoardingGenPhraseCubit: context.read(), + onboardingCubit: context.read(), + ); + }, ), ); } @@ -57,30 +62,33 @@ class ProtectWalletPage extends StatelessWidget { class ProtectWalletView extends StatefulWidget { const ProtectWalletView({ super.key, + required this.profileCubit, + required this.onBoardingGenPhraseCubit, + required this.onboardingCubit, this.routeType, }); final WalletRouteType? routeType; + final ProfileCubit profileCubit; + final OnBoardingGenPhraseCubit onBoardingGenPhraseCubit; + final OnboardingCubit onboardingCubit; @override State createState() => _ProtectWalletViewState(); } class _ProtectWalletViewState extends State { - final localAuthApi = LocalAuthApi(); - bool get byPassScreen => !Parameters.walletHandlesCrypto; bool get isFromOnboarding => widget.routeType != null; - Future createImportAccount() async { + Future createImportAccount({required bool byPassScreen}) async { if (widget.routeType == WalletRouteType.create) { if (byPassScreen) { - await context.read().emitOnboardingProcessing(); - + await widget.onboardingCubit.emitOnboardingProcessing(); final mnemonic = bip39.generateMnemonic().split(' '); - await context - .read() + + await widget.onBoardingGenPhraseCubit .generateSSIAndCryptoAccount(mnemonic); } else { await Navigator.of(context).push(OnBoardingGenPhrasePage.route()); @@ -125,20 +133,21 @@ class _ProtectWalletViewState extends State { useSafeArea: true, padding: const EdgeInsets.symmetric(horizontal: Sizes.spaceXSmall), titleLeading: const BackLeadingButton(), - backgroundColor: Theme.of(context).colorScheme.drawerBackground, + backgroundColor: Theme.of(context).colorScheme.surface, title: l10n.protectYourWallet, titleAlignment: Alignment.topCenter, body: BlocBuilder( builder: (context, profileState) { final walletProtectionType = profileState.model.walletProtectionType; + return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( l10n.protectYourWalletMessage, textAlign: TextAlign.center, - style: Theme.of(context).textTheme.bodySmall3, + style: Theme.of(context).textTheme.bodyMedium, ), const SizedBox(height: Sizes.spaceNormal), ProtectWidget( @@ -152,15 +161,14 @@ class _ProtectWalletViewState extends State { EnterNewPinCodePage.route( isFromOnboarding: isFromOnboarding, isValidCallback: () async { - await context - .read() - .setWalletProtectionType( - walletProtectionType: - WalletProtectionType.pinCode, - ); + await widget.profileCubit.setWalletProtectionType( + walletProtectionType: WalletProtectionType.pinCode, + ); Navigator.of(context).pop(); if (isFromOnboarding) { - await createImportAccount(); + await createImportAccount( + byPassScreen: byPassScreen, + ); } else { AlertMessage.showStateMessage( context: context, @@ -186,17 +194,18 @@ class _ProtectWalletViewState extends State { Navigator.of(context).push( ActiviateBiometricsPage.route( isFromOnboarding: isFromOnboarding, + localAuthApi: LocalAuthApi(), onAction: ({required bool isEnabled}) async { if (isEnabled) { - await context - .read() - .setWalletProtectionType( - walletProtectionType: - WalletProtectionType.biometrics, - ); + await widget.profileCubit.setWalletProtectionType( + walletProtectionType: + WalletProtectionType.biometrics, + ); Navigator.of(context).pop(); if (isFromOnboarding) { - await createImportAccount(); + await createImportAccount( + byPassScreen: byPassScreen, + ); } else { AlertMessage.showStateMessage( context: context, @@ -226,23 +235,26 @@ class _ProtectWalletViewState extends State { Navigator.of(context).pushReplacement( ActiviateBiometricsPage.route( isFromOnboarding: isFromOnboarding, + localAuthApi: LocalAuthApi(), onAction: ({required bool isEnabled}) async { if (isEnabled) { - await context - .read() + await widget.profileCubit .setWalletProtectionType( - walletProtectionType: - WalletProtectionType.FA2, - ); + walletProtectionType: + WalletProtectionType.FA2, + ); Navigator.of(context).pop(); if (isFromOnboarding) { - await createImportAccount(); + await createImportAccount( + byPassScreen: byPassScreen, + ); } else { AlertMessage.showStateMessage( context: context, stateMessage: StateMessage.success( showDialog: true, stringMessage: l10n + // ignore: lines_longer_than_80_chars .twoFactorAuthenticationHasBeenEnabled, ), ); diff --git a/lib/onboarding/starter/view/starter_page.dart b/lib/onboarding/starter/view/starter_page.dart index bc79732c1..b60ec9c04 100644 --- a/lib/onboarding/starter/view/starter_page.dart +++ b/lib/onboarding/starter/view/starter_page.dart @@ -3,7 +3,6 @@ import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; import 'package:altme/onboarding/onboarding.dart'; import 'package:altme/splash/splash.dart'; -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -19,9 +18,21 @@ class StarterPage extends StatelessWidget { @override Widget build(BuildContext context) { - final l10n = context.l10n; + return StarterView(profileCubit: context.read()); + } +} + +class StarterView extends StatelessWidget { + const StarterView({ + super.key, + required this.profileCubit, + }); + + final ProfileCubit profileCubit; - final profileCubit = context.read(); + @override + Widget build(BuildContext context) { + final l10n = context.l10n; return WillPopScope( onWillPop: () async { @@ -33,8 +44,8 @@ class StarterPage extends StatelessWidget { decoration: BoxDecoration( gradient: LinearGradient( colors: [ - Theme.of(context).colorScheme.darkGradientStartColor, - Theme.of(context).colorScheme.darkGradientEndColor, + Theme.of(context).colorScheme.surface, + Theme.of(context).colorScheme.surface, ], end: Alignment.topCenter, begin: Alignment.bottomCenter, @@ -67,11 +78,13 @@ class StarterPage extends StatelessWidget { await profileCubit.setWalletType( walletType: WalletType.personal, ); + await profileCubit.setProfileSetting( profileSetting: ProfileSetting.initial(), profileType: ProfileType.defaultOne, ); - await Navigator.of(context).push( + + await Navigator.of(context).push( ProtectWalletPage.route( routeType: WalletRouteType.create, ), @@ -81,9 +94,9 @@ class StarterPage extends StatelessWidget { const Spacer(flex: 1), MyOutlinedButton( text: l10n.importAccount, - textColor: Theme.of(context).colorScheme.lightPurple, + textColor: Theme.of(context).colorScheme.secondary, borderColor: - Theme.of(context).colorScheme.lightPurple, + Theme.of(context).colorScheme.secondary, backgroundColor: Colors.transparent, onPressed: () async { await profileCubit.setWalletType( @@ -93,7 +106,7 @@ class StarterPage extends StatelessWidget { profileSetting: ProfileSetting.initial(), profileType: ProfileType.defaultOne, ); - await Navigator.of(context).push( + await Navigator.of(context).push( ProtectWalletPage.route( routeType: WalletRouteType.import, ), diff --git a/lib/onboarding/third/onboarding_third.dart b/lib/onboarding/third/onboarding_third.dart deleted file mode 100644 index f96d99475..000000000 --- a/lib/onboarding/third/onboarding_third.dart +++ /dev/null @@ -1 +0,0 @@ -export 'view/onboarding_third_page.dart'; diff --git a/lib/onboarding/third/view/onboarding_third_page.dart b/lib/onboarding/third/view/onboarding_third_page.dart deleted file mode 100644 index 091f69dbc..000000000 --- a/lib/onboarding/third/view/onboarding_third_page.dart +++ /dev/null @@ -1,66 +0,0 @@ -import 'package:altme/app/app.dart'; -import 'package:altme/l10n/l10n.dart'; -import 'package:altme/onboarding/onboarding.dart'; -import 'package:altme/route/route.dart'; -import 'package:flutter/material.dart'; - -class OnBoardingThirdPage extends StatelessWidget { - const OnBoardingThirdPage({super.key}); - - static Route route() => RightToLeftRoute( - builder: (context) => const OnBoardingThirdPage(), - settings: const RouteSettings(name: '/onBoardingThirdPage'), - ); - - @override - Widget build(BuildContext context) { - return const OnBoardingThirdView(); - } -} - -class OnBoardingThirdView extends StatefulWidget { - const OnBoardingThirdView({super.key}); - - static Route route() => RightToLeftRoute( - builder: (context) => const OnBoardingThirdView(), - settings: const RouteSettings(name: '/onBoardingThirdPage'), - ); - - @override - State createState() => _OnBoardingThirdViewState(); -} - -class _OnBoardingThirdViewState extends State { - bool animate = true; - - void disableAnimation() { - animate = false; - Future.delayed(const Duration(seconds: 1), () { - animate = true; - }); - } - - @override - Widget build(BuildContext context) { - final l10n = context.l10n; - return GestureDetector( - onHorizontalDragUpdate: (drag) async { - if (animate) { - if (drag.delta.dx > 2) { - Navigator.of(context).pop(); - disableAnimation(); - } - } - }, - child: BasePage( - scrollView: false, - body: OnBoardingWidget( - title: l10n.onBoardingThirdTitle, - subtitle: l10n.onBoardingThirdSubtitle, - image: ImageStrings.onBoardingThirdImage, - index: 3, - ), - ), - ); - } -} diff --git a/lib/onboarding/tos/view/onboarding_tos_page.dart b/lib/onboarding/tos/view/onboarding_tos_page.dart index e65ff455d..0069f6493 100644 --- a/lib/onboarding/tos/view/onboarding_tos_page.dart +++ b/lib/onboarding/tos/view/onboarding_tos_page.dart @@ -18,13 +18,24 @@ class OnBoardingTosPage extends StatelessWidget { Widget build(BuildContext context) { return BlocProvider( create: (_) => OnBoardingTosCubit(), - child: const OnBoardingTosView(), + child: Builder( + builder: (context) { + return OnBoardingTosView( + onBoardingTosCubit: context.read(), + ); + }, + ), ); } } class OnBoardingTosView extends StatefulWidget { - const OnBoardingTosView({super.key}); + const OnBoardingTosView({ + super.key, + required this.onBoardingTosCubit, + }); + + final OnBoardingTosCubit onBoardingTosCubit; @override State createState() => _OnBoardingTosViewState(); @@ -41,13 +52,9 @@ class _OnBoardingTosViewState extends State { final double currentScroll = _scrollController.position.pixels; if (maxScroll - currentScroll <= 200) { - context - .read() - .setScrolledIsOver(scrollIsOver: true); + widget.onBoardingTosCubit.setScrolledIsOver(scrollIsOver: true); } else { - context - .read() - .setScrolledIsOver(scrollIsOver: false); + widget.onBoardingTosCubit.setScrolledIsOver(scrollIsOver: false); } }); super.initState(); @@ -63,7 +70,7 @@ class _OnBoardingTosViewState extends State { return false; }, child: BasePage( - backgroundColor: Theme.of(context).colorScheme.background, + backgroundColor: Theme.of(context).colorScheme.surface, title: l10n.termsOfUse, scrollView: false, padding: const EdgeInsets.symmetric(horizontal: 10), @@ -87,8 +94,7 @@ class _OnBoardingTosViewState extends State { value: state.agreeTerms, text: l10n.agreeTermsAndConditionCheckBox, onChange: (value) { - context - .read() + widget.onBoardingTosCubit .setAgreeTerms(agreeTerms: value); }, ), @@ -96,8 +102,7 @@ class _OnBoardingTosViewState extends State { value: state.readTerms, text: l10n.readTermsOfUseCheckBox, onChange: (value) { - context - .read() + widget.onBoardingTosCubit .setReadTerms(readTerms: value); }, ), diff --git a/lib/onboarding/tos/widgets/scroll_down_button.dart b/lib/onboarding/tos/widgets/scroll_down_button.dart index 9784f5e9d..80209f0f9 100644 --- a/lib/onboarding/tos/widgets/scroll_down_button.dart +++ b/lib/onboarding/tos/widgets/scroll_down_button.dart @@ -1,5 +1,5 @@ import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class ScrollDownButton extends StatelessWidget { @@ -20,7 +20,7 @@ class ScrollDownButton extends StatelessWidget { children: [ Text( l10n.scroll, - style: Theme.of(context).textTheme.scrollText, + style: Theme.of(context).textTheme.bodySmall, ), const Icon( Icons.keyboard_arrow_down_rounded, diff --git a/lib/onboarding/verify_phrase/cubit/onboarding_verify_phrase_cubit.dart b/lib/onboarding/verify_phrase/cubit/onboarding_verify_phrase_cubit.dart index 8365552d4..0c932f817 100644 --- a/lib/onboarding/verify_phrase/cubit/onboarding_verify_phrase_cubit.dart +++ b/lib/onboarding/verify_phrase/cubit/onboarding_verify_phrase_cubit.dart @@ -10,15 +10,12 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:json_annotation/json_annotation.dart'; import 'package:key_generator/key_generator.dart'; -import 'package:secure_storage/secure_storage.dart'; - part 'onboarding_verify_phrase_cubit.g.dart'; part 'onboarding_verify_phrase_state.dart'; class OnBoardingVerifyPhraseCubit extends Cubit { OnBoardingVerifyPhraseCubit({ - required this.secureStorageProvider, required this.keyGenerator, required this.didKitProvider, required this.homeCubit, @@ -29,7 +26,6 @@ class OnBoardingVerifyPhraseCubit extends Cubit { required this.profileCubit, }) : super(OnBoardingVerifyPhraseState()); - final SecureStorageProvider secureStorageProvider; final KeyGenerator keyGenerator; final DIDKitProvider didKitProvider; @@ -116,7 +112,6 @@ class OnBoardingVerifyPhraseCubit extends Cubit { if (isFromOnboarding) { await generateAccount( mnemonic: mnemonic, - secureStorageProvider: secureStorageProvider, keyGenerator: keyGenerator, didKitProvider: didKitProvider, homeCubit: homeCubit, @@ -126,7 +121,7 @@ class OnBoardingVerifyPhraseCubit extends Cubit { profileCubit: profileCubit, ); } - await secureStorageProvider.set( + await profileCubit.secureStorageProvider.set( SecureStorageKeys.hasVerifiedMnemonics, 'yes', ); diff --git a/lib/onboarding/verify_phrase/view/onboarding_verify_phrase.dart b/lib/onboarding/verify_phrase/view/onboarding_verify_phrase.dart index 0ef060e6c..a7fd2db43 100644 --- a/lib/onboarding/verify_phrase/view/onboarding_verify_phrase.dart +++ b/lib/onboarding/verify_phrase/view/onboarding_verify_phrase.dart @@ -5,13 +5,12 @@ import 'package:altme/l10n/l10n.dart'; import 'package:altme/onboarding/cubit/onboarding_cubit.dart'; import 'package:altme/onboarding/onboarding.dart'; import 'package:altme/splash/splash.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:altme/wallet/cubit/wallet_cubit.dart'; import 'package:did_kit/did_kit.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:key_generator/key_generator.dart'; -import 'package:secure_storage/secure_storage.dart'; class OnBoardingVerifyPhrasePage extends StatelessWidget { const OnBoardingVerifyPhrasePage({ @@ -39,7 +38,6 @@ class OnBoardingVerifyPhrasePage extends StatelessWidget { Widget build(BuildContext context) { return BlocProvider( create: (context) => OnBoardingVerifyPhraseCubit( - secureStorageProvider: getSecureStorage, didKitProvider: DIDKitProvider(), keyGenerator: KeyGenerator(), homeCubit: context.read(), @@ -49,9 +47,16 @@ class OnBoardingVerifyPhrasePage extends StatelessWidget { altmeChatSupportCubit: context.read(), profileCubit: context.read(), ), - child: OnBoardingVerifyPhraseView( - mnemonic: mnemonic, - isFromOnboarding: isFromOnboarding, + child: Builder( + builder: (context) { + return OnBoardingVerifyPhraseView( + mnemonic: mnemonic, + isFromOnboarding: isFromOnboarding, + onBoardingVerifyPhraseCubit: + context.read(), + onboardingCubit: context.read(), + ); + }, ), ); } @@ -61,11 +66,15 @@ class OnBoardingVerifyPhraseView extends StatefulWidget { const OnBoardingVerifyPhraseView({ required this.mnemonic, required this.isFromOnboarding, + required this.onBoardingVerifyPhraseCubit, + required this.onboardingCubit, super.key, }); final List mnemonic; final bool isFromOnboarding; + final OnBoardingVerifyPhraseCubit onBoardingVerifyPhraseCubit; + final OnboardingCubit onboardingCubit; @override State createState() => @@ -77,7 +86,7 @@ class _OnBoardingVerifyPhraseViewState @override void initState() { WidgetsBinding.instance.addPostFrameCallback((_) { - context.read().orderMnemonics(); + widget.onBoardingVerifyPhraseCubit.orderMnemonics(); }); super.initState(); } @@ -149,7 +158,7 @@ class _OnBoardingVerifyPhraseViewState Text( l10n.onboardingVerifyPhraseMessageDetails, textAlign: TextAlign.center, - style: Theme.of(context).textTheme.pheaseVerifySubmessage, + style: Theme.of(context).textTheme.bodyMedium, ), const SizedBox(height: Sizes.spaceNormal), ListView.builder( @@ -169,6 +178,7 @@ class _OnBoardingVerifyPhraseViewState children: [ Expanded( child: PhraseWord( + key: Key(col1Mnemonics.order.toString()), order: col1Mnemonics.order, word: widget.mnemonic[col1Mnemonics.order - 1], @@ -176,18 +186,17 @@ class _OnBoardingVerifyPhraseViewState col1Mnemonics.mnemonicStatus.showOrder, color: col1Mnemonics.mnemonicStatus.color, onTap: () { - context - .read() - .verify( - mnemonic: widget.mnemonic, - index: j, - ); + widget.onBoardingVerifyPhraseCubit.verify( + mnemonic: widget.mnemonic, + index: j, + ); }, ), ), const SizedBox(width: 12), Expanded( child: PhraseWord( + key: Key(col2Mnemonics.order.toString()), order: col2Mnemonics.order, word: widget.mnemonic[col2Mnemonics.order - 1], @@ -195,18 +204,17 @@ class _OnBoardingVerifyPhraseViewState col2Mnemonics.mnemonicStatus.showOrder, color: col2Mnemonics.mnemonicStatus.color, onTap: () { - context - .read() - .verify( - mnemonic: widget.mnemonic, - index: j + 1, - ); + widget.onBoardingVerifyPhraseCubit.verify( + mnemonic: widget.mnemonic, + index: j + 1, + ); }, ), ), const SizedBox(width: 12), Expanded( child: PhraseWord( + key: Key(col3Mnemonics.order.toString()), order: col3Mnemonics.order, word: widget.mnemonic[col3Mnemonics.order - 1], @@ -214,12 +222,10 @@ class _OnBoardingVerifyPhraseViewState col3Mnemonics.mnemonicStatus.showOrder, color: col3Mnemonics.mnemonicStatus.color, onTap: () { - context - .read() - .verify( - mnemonic: widget.mnemonic, - index: j + 2, - ); + widget.onBoardingVerifyPhraseCubit.verify( + mnemonic: widget.mnemonic, + index: j + 2, + ); }, ), ), @@ -241,15 +247,12 @@ class _OnBoardingVerifyPhraseViewState verticalSpacing: 18, onPressed: state.isVerified ? () async { - await context - .read() - .emitOnboardingProcessing(); - await context - .read() + await widget.onboardingCubit.emitOnboardingProcessing(); + await widget.onBoardingVerifyPhraseCubit .generateSSIAndCryptoAccount( - mnemonic: widget.mnemonic, - isFromOnboarding: widget.isFromOnboarding, - ); + mnemonic: widget.mnemonic, + isFromOnboarding: widget.isFromOnboarding, + ); } : null, ), diff --git a/lib/onboarding/wallet_ready/view/wallet_ready_page.dart b/lib/onboarding/wallet_ready/view/wallet_ready_page.dart index 029666aa7..a95a72725 100644 --- a/lib/onboarding/wallet_ready/view/wallet_ready_page.dart +++ b/lib/onboarding/wallet_ready/view/wallet_ready_page.dart @@ -22,36 +22,50 @@ class WalletReadyPage extends StatelessWidget { Widget build(BuildContext context) { return BlocProvider( create: (_) => WalletReadyCubit(), - child: const WalletReadyView(), + child: Builder( + builder: (context) { + return WalletReadyView( + profileCubit: context.read(), + walletReadyCubit: context.read(), + confettiController: ConfettiController(), + ); + }, + ), ); } } class WalletReadyView extends StatefulWidget { - const WalletReadyView({super.key}); + const WalletReadyView({ + super.key, + required this.walletReadyCubit, + required this.profileCubit, + required this.confettiController, + }); + + final WalletReadyCubit walletReadyCubit; + final ProfileCubit profileCubit; + final ConfettiController confettiController; @override State createState() => _WalletReadyViewState(); } class _WalletReadyViewState extends State { - late final ConfettiController confettiController; - @override void initState() { - confettiController = ConfettiController(); - Future.delayed(Duration.zero).then((value) { + WidgetsBinding.instance.addPostFrameCallback((_) { context.read().emitOnboardingDone(); - - confettiController.play(); + widget.confettiController.play(); }); + super.initState(); } @override void dispose() { - confettiController.stop(); - confettiController.dispose(); + widget.confettiController.stop(); + widget.confettiController.dispose(); super.dispose(); } @@ -75,7 +89,7 @@ class _WalletReadyViewState extends State { crossAxisAlignment: CrossAxisAlignment.center, children: [ WalletLogo( - profileModel: context.read().state.model, + profileModel: widget.profileCubit.state.model, height: 90, width: MediaQuery.of(context).size.shortestSide * 0.5, showPoweredBy: true, @@ -124,7 +138,7 @@ class _WalletReadyViewState extends State { scale: 1.3, child: Checkbox( value: state.isAgreeWithTerms, - fillColor: MaterialStateProperty.all( + fillColor: WidgetStateProperty.all( Theme.of(context).colorScheme.primary, ), materialTapTargetSize: @@ -134,9 +148,8 @@ class _WalletReadyViewState extends State { Radius.circular(6), ), ), - onChanged: (newValue) => context - .read() - .toggleAgreement(), + onChanged: (newValue) => + widget.walletReadyCubit.toggleAgreement(), ), ), Expanded( @@ -145,9 +158,7 @@ class _WalletReadyViewState extends State { children: [ InkWell( onTap: () { - context - .read() - .toggleAgreement(); + widget.walletReadyCubit.toggleAgreement(); }, child: MyText( l10n.iAgreeToThe, @@ -170,7 +181,7 @@ class _WalletReadyViewState extends State { l10n.termsAndConditions.toLowerCase(), style: Theme.of(context) .textTheme - .titleLarge + .titleMedium ?.copyWith( fontWeight: FontWeight.bold, color: Theme.of(context) @@ -210,7 +221,7 @@ class _WalletReadyViewState extends State { ), ), ConfettiWidget( - confettiController: confettiController, + confettiController: widget.confettiController, canvas: Size.infinite, shouldLoop: true, blastDirectionality: BlastDirectionality.explosive, diff --git a/lib/onboarding/widgets/onboarding_widget.dart b/lib/onboarding/widgets/onboarding_widget.dart deleted file mode 100644 index c07b7c988..000000000 --- a/lib/onboarding/widgets/onboarding_widget.dart +++ /dev/null @@ -1,95 +0,0 @@ -import 'package:altme/app/app.dart'; -import 'package:altme/dashboard/dashboard.dart'; -import 'package:altme/l10n/l10n.dart'; -import 'package:altme/onboarding/widgets/widgets.dart'; -import 'package:altme/theme/theme.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; - -class OnBoardingWidget extends StatelessWidget { - const OnBoardingWidget({ - super.key, - required this.title, - required this.subtitle, - required this.index, - required this.image, - }); - - final String title; - final String subtitle; - final int index; - final String image; - - @override - Widget build(BuildContext context) { - final l10n = context.l10n; - return Column( - children: [ - Padding( - padding: const EdgeInsets.all(8), - child: WalletLogo( - profileModel: context.read().state.model, - height: 90, - width: MediaQuery.of(context).size.shortestSide * 0.5, - showPoweredBy: true, - ), - ), - Padding( - padding: const EdgeInsets.all(8), - child: Text( - title, - textAlign: TextAlign.center, - style: Theme.of(context).textTheme.onBoardingTitleStyle, - ), - ), - Padding( - padding: const EdgeInsets.all(8), - child: Text( - subtitle, - textAlign: TextAlign.center, - style: Theme.of(context).textTheme.onBoardingSubTitleStyle, - ), - ), - const Spacer(), - Image.asset( - image, - fit: BoxFit.fitHeight, - height: MediaQuery.of(context).size.longestSide * 0.4, - ), - const Spacer(), - PageTracker(index: index), - const SizedBox(height: 20), - MyGradientButton( - text: l10n.onBoardingStart, - onPressed: () { - /// Removes every stack except first route (splashPage) - Navigator.pushAndRemoveUntil( - context, - DashboardPage.route(), - (Route route) => route.isFirst, - ); - }, - ), - const SizedBox(height: 20), - if (isAndroid) - Column( - children: [ - GestureDetector( - child: Text( - l10n.learnMoreAboutAltme, - textAlign: TextAlign.center, - style: Theme.of(context).textTheme.learnMoreTextStyle, - ), - onTap: () async { - await LaunchUrl.launch(Urls.appContactWebsiteUrl); - }, - ), - const SizedBox(height: 5), - ], - ) - else - const SizedBox.shrink(), - ], - ); - } -} diff --git a/lib/onboarding/widgets/page_tracker.dart b/lib/onboarding/widgets/page_tracker.dart deleted file mode 100644 index 2bd87486e..000000000 --- a/lib/onboarding/widgets/page_tracker.dart +++ /dev/null @@ -1,55 +0,0 @@ -import 'package:flutter/material.dart'; - -class PageTracker extends StatelessWidget { - const PageTracker({ - super.key, - required this.index, - }); - - final int index; - - @override - Widget build(BuildContext context) { - const double selectedWidth = 22; - const double unselectedWidth = 10; - const double height = 10; - const double radius = 10; - return Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Container( - width: index == 1 ? selectedWidth : unselectedWidth, - height: height, - decoration: BoxDecoration( - color: index == 1 - ? Theme.of(context).colorScheme.primary - : Theme.of(context).colorScheme.onPrimary, - borderRadius: BorderRadius.circular(radius), - ), - ), - const SizedBox(width: 7), - Container( - width: index == 2 ? selectedWidth : unselectedWidth, - height: height, - decoration: BoxDecoration( - color: index == 2 - ? Theme.of(context).colorScheme.primary - : Theme.of(context).colorScheme.onPrimary, - borderRadius: BorderRadius.circular(radius), - ), - ), - const SizedBox(width: 7), - Container( - width: index == 3 ? selectedWidth : unselectedWidth, - height: height, - decoration: BoxDecoration( - color: index == 3 - ? Theme.of(context).colorScheme.primary - : Theme.of(context).colorScheme.onPrimary, - borderRadius: BorderRadius.circular(radius), - ), - ), - ], - ); - } -} diff --git a/lib/onboarding/widgets/protect_widget.dart b/lib/onboarding/widgets/protect_widget.dart index ba9ae8a30..a8c0f0a1a 100644 --- a/lib/onboarding/widgets/protect_widget.dart +++ b/lib/onboarding/widgets/protect_widget.dart @@ -1,5 +1,5 @@ import 'package:altme/app/app.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class ProtectWidget extends StatelessWidget { @@ -31,7 +31,7 @@ class ProtectWidget extends StatelessWidget { decoration: BoxDecoration( color: isSelected ? Theme.of(context).colorScheme.primary - : Theme.of(context).colorScheme.drawerSurface, + : Theme.of(context).colorScheme.surfaceBright, borderRadius: const BorderRadius.all( Radius.circular( Sizes.normalRadius, @@ -57,11 +57,11 @@ class ProtectWidget extends StatelessWidget { children: [ Text( title, - style: Theme.of(context).textTheme.drawerItemTitle, + style: Theme.of(context).textTheme.titleLarge, ), Text( subtitle, - style: Theme.of(context).textTheme.drawerItemSubtitle, + style: Theme.of(context).textTheme.titleMedium, ), ], ), diff --git a/lib/onboarding/widgets/widgets.dart b/lib/onboarding/widgets/widgets.dart index 97f6e03bf..f3e14340d 100644 --- a/lib/onboarding/widgets/widgets.dart +++ b/lib/onboarding/widgets/widgets.dart @@ -1,4 +1,2 @@ export 'm_stepper.dart'; -export 'onboarding_widget.dart'; -export 'page_tracker.dart'; export 'protect_widget.dart'; diff --git a/lib/pin_code/cubit/pin_code_view_cubit.dart b/lib/pin_code/cubit/pin_code_view_cubit.dart index b20135591..db1d3cc9c 100644 --- a/lib/pin_code/cubit/pin_code_view_cubit.dart +++ b/lib/pin_code/cubit/pin_code_view_cubit.dart @@ -1,20 +1,63 @@ +import 'dart:async'; + import 'package:altme/app/app.dart'; -import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/pin_code/pin_code.dart'; import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; +import 'package:secure_storage/secure_storage.dart'; part 'pin_code_view_state.dart'; class PinCodeViewCubit extends Cubit { PinCodeViewCubit({ - required this.profileCubit, + this.totalPermitedLoginAttempt = 3, }) : super(const PinCodeViewState()); - final ProfileCubit profileCubit; + final int totalPermitedLoginAttempt; + + void loginAttempt() { + final loginAttemptCount = state.loginAttemptCount + 1; + final loginAttemptsRemaining = + totalPermitedLoginAttempt - state.loginAttemptCount - 1; + const allowAction = true; + // if (loginAttemptCount >= totalPermitedLoginAttempt) { + // allowAction = false; + // _startLockTimer(); + // } + emit( + state.copyWith( + loginAttemptCount: loginAttemptCount, + loginAttemptsRemaining: loginAttemptsRemaining, + allowAction: allowAction, + ), + ); + } + + Timer? _timer; + + void _startLockTimer() { + _timer = Timer.periodic(const Duration(minutes: 10), (timer) { + _resetloginAttemptCount(); + _timer?.cancel(); + }); + } + + void _resetloginAttemptCount() { + emit( + state.copyWith( + loginAttemptCount: 0, + allowAction: true, + loginAttemptsRemaining: totalPermitedLoginAttempt, + ), + ); + } void setEnteredPasscode(String enteredPasscode) { - emit(state.copyWith(enteredPasscode: enteredPasscode)); + emit( + state.copyWith( + enteredPasscode: enteredPasscode, + ), + ); } void onDeleteCancelButtonPressed(CancelCallback? cancelCallback) { @@ -30,12 +73,12 @@ class PinCodeViewCubit extends Cubit { } } - void onKeyboardButtonPressed({ + Future onKeyboardButtonPressed({ required String text, CancelCallback? cancelCallback, required int passwordDigits, - required PasswordEnteredCallback passwordEnteredCallback, - }) { + bool isNewCode = false, + }) async { final enteredPasscode = state.enteredPasscode; if (text == NumericKeyboard.deleteButton) { onDeleteCancelButtonPressed(cancelCallback); @@ -43,10 +86,117 @@ class PinCodeViewCubit extends Cubit { } if (enteredPasscode.length < passwordDigits) { final passCode = enteredPasscode + text; - setEnteredPasscode(passCode); + if (passCode.length == passwordDigits) { - passwordEnteredCallback(passCode); + if (isNewCode) { + if (isPincodeSeries( + digit: passCode, + passwordDigits: passwordDigits, + )) { + emit( + state.copyWith( + isPincodeSeries: true, + enteredPasscode: passCode, + ), + ); + return; + } + if (isPincodeSequence( + digit: passCode, + passwordDigits: passwordDigits, + )) { + emit( + state.copyWith( + isPincodeSequence: true, + enteredPasscode: passCode, + ), + ); + return; + } + await getSecureStorage.set(SecureStorageKeys.pinCode, passCode); + } else { + final isValid = + (await getSecureStorage.get(SecureStorageKeys.pinCode)) == + passCode; + if (!isValid) { + setEnteredPasscode(passCode); + loginAttempt(); + return; + } + } + emit( + state.copyWith( + isPinCodeValid: true, + enteredPasscode: passCode, + ), + ); + +// on new pincode + // void _onPasscodeEntered(String enteredPasscode) { + // Navigator.pushReplacement( + // context, + // ConfirmPinCodePage.route( + // storedPassword: enteredPasscode, + // isValidCallback: widget.isValidCallback, + // isFromOnboarding: widget.isFromOnboarding, + // ), + // ); + // } + } else { + setEnteredPasscode(passCode); + } + } else { + emit( + state.copyWith( + enteredPasscode: text, + isPincodeSequence: false, + isPincodeSeries: false, + ), + ); + } + } + + bool areAllDigitsIdentical(String number) { + // Convert the number to a string to easily access its digits + + // Check if all digits are the same + for (int i = 1; i < number.length; i++) { + if (number[i] != number[0]) { + return false; } } + + return true; + } + + bool isPincodeSequence({required String digit, required int passwordDigits}) { + if (digit.length == passwordDigits) { + bool isValidDesc = false; + bool isValidAsc = false; + // test if pin code is a serie asc or desc + for (int i = 1; i < digit.length; i++) { + if (i > 0) { + if (int.parse(digit[i]) != (int.parse(digit[i - 1]) + 1)) { + isValidAsc = true; + } + if (int.parse(digit[i]) != (int.parse(digit[i - 1]) - 1)) { + isValidDesc = true; + } + } + } + if (isValidAsc && isValidDesc) { + return false; + } else { + return true; + } + } + return false; + } + + bool isPincodeSeries({required String digit, required int passwordDigits}) { + if (digit.length == passwordDigits) { + return areAllDigitsIdentical(digit); + } + return true; } } diff --git a/lib/pin_code/cubit/pin_code_view_state.dart b/lib/pin_code/cubit/pin_code_view_state.dart index ba520b7a2..7ffd1e053 100644 --- a/lib/pin_code/cubit/pin_code_view_state.dart +++ b/lib/pin_code/cubit/pin_code_view_state.dart @@ -3,16 +3,51 @@ part of 'pin_code_view_cubit.dart'; class PinCodeViewState extends Equatable { const PinCodeViewState({ this.enteredPasscode = '', + this.loginAttemptCount = 0, + this.loginAttemptsRemaining = 3, + this.allowAction = true, + this.isPincodeSequence = false, + this.isPincodeSeries = false, + this.isPinCodeValid = false, }); final String enteredPasscode; + final int loginAttemptCount; + final int loginAttemptsRemaining; + final bool allowAction; + final bool isPincodeSeries; + final bool isPincodeSequence; + final bool isPinCodeValid; - PinCodeViewState copyWith({String? enteredPasscode}) { + PinCodeViewState copyWith({ + String? enteredPasscode, + int? loginAttemptCount, + int? loginAttemptsRemaining, + bool? allowAction, + bool? isPincodeSequence, + bool? isPincodeSeries, + bool? isPinCodeValid, + }) { return PinCodeViewState( enteredPasscode: enteredPasscode ?? this.enteredPasscode, + loginAttemptCount: loginAttemptCount ?? this.loginAttemptCount, + loginAttemptsRemaining: + loginAttemptsRemaining ?? this.loginAttemptsRemaining, + allowAction: allowAction ?? this.allowAction, + isPincodeSequence: isPincodeSequence ?? this.isPincodeSequence, + isPincodeSeries: isPincodeSeries ?? this.isPincodeSeries, + isPinCodeValid: isPinCodeValid ?? this.isPinCodeValid, ); } @override - List get props => [enteredPasscode]; + List get props => [ + enteredPasscode, + loginAttemptCount, + allowAction, + loginAttemptsRemaining, + isPincodeSequence, + isPincodeSeries, + isPinCodeValid, + ]; } diff --git a/lib/pin_code/pin_code.dart b/lib/pin_code/pin_code.dart index aeab34334..c188aa1af 100644 --- a/lib/pin_code/pin_code.dart +++ b/lib/pin_code/pin_code.dart @@ -1,6 +1,7 @@ export 'cubit/pin_code_view_cubit.dart'; export 'typedef.dart'; export 'view/confirm_pin_code_page.dart'; +export 'view/delete_my_wallet_page.dart'; export 'view/enter_new_pin_code_page.dart'; export 'view/pin_code_page.dart'; export 'widgets/widgets.dart'; diff --git a/lib/pin_code/typedef.dart b/lib/pin_code/typedef.dart index 915c03d7f..b6e8af307 100644 --- a/lib/pin_code/typedef.dart +++ b/lib/pin_code/typedef.dart @@ -1,3 +1,2 @@ -typedef PasswordEnteredCallback = dynamic Function(String text); typedef IsValidCallback = dynamic Function(); typedef CancelCallback = dynamic Function(); diff --git a/lib/pin_code/view/confirm_pin_code_page.dart b/lib/pin_code/view/confirm_pin_code_page.dart index 2a9f96cfb..3f89e92bc 100644 --- a/lib/pin_code/view/confirm_pin_code_page.dart +++ b/lib/pin_code/view/confirm_pin_code_page.dart @@ -1,13 +1,9 @@ -import 'dart:async'; - import 'package:altme/app/app.dart'; -import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/onboarding/onboarding.dart'; +import 'package:altme/onboarding/widgets/m_stepper.dart'; import 'package:altme/pin_code/pin_code.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:secure_storage/secure_storage.dart'; class ConfirmPinCodePage extends StatelessWidget { const ConfirmPinCodePage({ @@ -39,8 +35,7 @@ class ConfirmPinCodePage extends StatelessWidget { @override Widget build(BuildContext context) { return BlocProvider( - create: (context) => - PinCodeViewCubit(profileCubit: context.read()), + create: (context) => PinCodeViewCubit(), child: ConfirmPinCodeView( storedPassword: storedPassword, isValidCallback: isValidCallback, @@ -67,9 +62,6 @@ class ConfirmPinCodeView extends StatefulWidget { } class _ConfirmPinCodeViewState extends State { - final StreamController _verificationNotifier = - StreamController.broadcast(); - bool get byPassScreen => !Parameters.walletHandlesCrypto; @override @@ -79,50 +71,45 @@ class _ConfirmPinCodeViewState extends State { @override void dispose() { - _verificationNotifier.close(); super.dispose(); } @override Widget build(BuildContext context) { final l10n = context.l10n; - return BasePage( - scrollView: false, - titleLeading: const BackLeadingButton(), - padding: const EdgeInsets.symmetric(horizontal: Sizes.spaceSmall), - backgroundColor: Theme.of(context).colorScheme.background, - body: PinCodeWidget( - title: l10n.confirmYourPinCode, - passwordEnteredCallback: _onPasscodeEntered, - header: widget.isFromOnboarding - ? MStepper( - step: 1, - totalStep: byPassScreen ? 2 : 3, - ) - : null, - deleteButton: Text( - l10n.delete, - style: Theme.of(context).textTheme.labelLarge, - ), - cancelButton: Text( - l10n.cancel, - style: Theme.of(context).textTheme.labelLarge, + return WillPopScope( + onWillPop: () async => !widget.isFromOnboarding, + child: BasePage( + scrollView: false, + title: '', + titleAlignment: Alignment.topCenter, + titleLeading: const BackLeadingButton(), + padding: const EdgeInsets.symmetric(horizontal: Sizes.spaceSmall), + backgroundColor: Theme.of(context).colorScheme.surface, + body: PinCodeWidget( + title: l10n.confirmYourPinCode, + header: widget.isFromOnboarding + ? MStepper( + step: 1, + totalStep: byPassScreen ? 2 : 3, + ) + : null, + deleteButton: Text( + l10n.delete, + style: Theme.of(context).textTheme.labelLarge, + ), + cancelButton: Text( + l10n.cancel, + style: Theme.of(context).textTheme.labelLarge, + ), + cancelCallback: _onPasscodeCancelled, + isValidCallback: widget.isValidCallback, + isNewCode: false, ), - cancelCallback: _onPasscodeCancelled, - isValidCallback: widget.isValidCallback, - shouldTriggerVerification: _verificationNotifier.stream, ), ); } - Future _onPasscodeEntered(String enteredPasscode) async { - final bool isValid = widget.storedPassword == enteredPasscode; - if (isValid) { - await getSecureStorage.set(SecureStorageKeys.pinCode, enteredPasscode); - } - _verificationNotifier.add(isValid); - } - void _onPasscodeCancelled() { Navigator.maybePop(context); } diff --git a/lib/pin_code/view/delete_my_wallet_page.dart b/lib/pin_code/view/delete_my_wallet_page.dart new file mode 100644 index 000000000..ad2e67d74 --- /dev/null +++ b/lib/pin_code/view/delete_my_wallet_page.dart @@ -0,0 +1,73 @@ +import 'package:altme/app/shared/constants/image_strings.dart'; +import 'package:altme/app/shared/constants/sizes.dart'; +import 'package:altme/app/shared/widget/widget.dart'; +import 'package:altme/dashboard/drawer/reset_wallet/helper_functions/reset_wallet.dart'; +import 'package:altme/l10n/l10n.dart'; +import 'package:flutter/material.dart'; + +class DeleteMyWalletPage extends StatelessWidget { + const DeleteMyWalletPage({super.key}); + + static Route route() { + return MaterialPageRoute( + builder: (_) => const DeleteMyWalletPage(), + settings: const RouteSettings(name: '/deleteMyWalletPage'), + ); + } + + @override + Widget build(BuildContext context) { + return const DeleteMyWalletView(); + } +} + +class DeleteMyWalletView extends StatelessWidget { + const DeleteMyWalletView({ + super.key, + }); + @override + Widget build(BuildContext context) { + final l10n = context.l10n; + return BasePage( + title: l10n.walletBloced, + titleAlignment: Alignment.topCenter, + scrollView: false, + body: Column( + children: [ + const Spacer( + flex: 8, + ), + Image.asset( + ImageStrings.cardMissing, + width: Sizes.icon, + ), + const SizedBox( + height: Sizes.spaceLarge, + ), + Text( + l10n.deleteMyWalletForWrontPincodeTitle, + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.displaySmall, + ), + const SizedBox( + height: Sizes.spaceXSmall, + ), + Text( + l10n.deleteMyWalletForWrontPincodeDescription, + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.labelMedium, + ), + const Spacer( + flex: 14, + ), + MyElevatedButton( + text: l10n.deleteMyWallet, + onPressed: () async { + await resetWallet(context); + }, + ), + ], + ), + ); + } +} diff --git a/lib/pin_code/view/enter_new_pin_code_page.dart b/lib/pin_code/view/enter_new_pin_code_page.dart index 0b0356207..25ac66037 100644 --- a/lib/pin_code/view/enter_new_pin_code_page.dart +++ b/lib/pin_code/view/enter_new_pin_code_page.dart @@ -1,7 +1,6 @@ import 'package:altme/app/app.dart'; -import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/onboarding/onboarding.dart'; +import 'package:altme/onboarding/widgets/m_stepper.dart'; import 'package:altme/pin_code/pin_code.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -19,7 +18,6 @@ class EnterNewPinCodePage extends StatelessWidget { static Route route({ required VoidCallback isValidCallback, required bool isFromOnboarding, - bool restrictToBack = true, }) => MaterialPageRoute( builder: (_) => EnterNewPinCodePage( @@ -32,8 +30,7 @@ class EnterNewPinCodePage extends StatelessWidget { @override Widget build(BuildContext context) { return BlocProvider( - create: (context) => - PinCodeViewCubit(profileCubit: context.read()), + create: (context) => PinCodeViewCubit(), child: EnterNewPinCodeView( isValidCallback: isValidCallback, isFromOnboarding: isFromOnboarding, @@ -76,10 +73,9 @@ class _EnterNewPinCodeViewState extends State { scrollView: false, titleLeading: const BackLeadingButton(), padding: const EdgeInsets.symmetric(horizontal: Sizes.spaceSmall), - backgroundColor: Theme.of(context).colorScheme.background, + backgroundColor: Theme.of(context).colorScheme.surface, body: PinCodeWidget( title: l10n.enterNewPinCode, - passwordEnteredCallback: _onPasscodeEntered, header: widget.isFromOnboarding ? MStepper( step: 1, @@ -95,15 +91,17 @@ class _EnterNewPinCodeViewState extends State { style: Theme.of(context).textTheme.labelLarge, ), cancelCallback: _onPasscodeCancelled, + isNewCode: true, + isValidCallback: _isValidCallback, ), ); } - void _onPasscodeEntered(String enteredPasscode) { + void _isValidCallback() { Navigator.pushReplacement( context, ConfirmPinCodePage.route( - storedPassword: enteredPasscode, + storedPassword: context.read().state.enteredPasscode, isValidCallback: widget.isValidCallback, isFromOnboarding: widget.isFromOnboarding, ), diff --git a/lib/pin_code/view/pin_code_page.dart b/lib/pin_code/view/pin_code_page.dart index 7cb0efa0d..a9bc9f239 100644 --- a/lib/pin_code/view/pin_code_page.dart +++ b/lib/pin_code/view/pin_code_page.dart @@ -1,13 +1,8 @@ -import 'dart:async'; - import 'package:altme/app/app.dart'; -import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/pin_code/cubit/pin_code_view_cubit.dart'; -import 'package:altme/pin_code/widgets/widgets.dart'; +import 'package:altme/pin_code/pin_code.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:secure_storage/secure_storage.dart'; class PinCodePage extends StatelessWidget { const PinCodePage({ @@ -41,8 +36,7 @@ class PinCodePage extends StatelessWidget { @override Widget build(BuildContext context) { return BlocProvider( - create: (context) => - PinCodeViewCubit(profileCubit: context.read()), + create: (context) => PinCodeViewCubit(totalPermitedLoginAttempt: 3), child: PinCodeView( isValidCallback: isValidCallback, restrictToBack: restrictToBack, @@ -72,96 +66,91 @@ class PinCodeView extends StatefulWidget { } class _PinCodeViewState extends State { - final StreamController _verificationNotifier = - StreamController.broadcast(); + final totalPermitedLoginAttempt = 3; + + @override + void initState() { + super.initState(); + } @override void dispose() { - _verificationNotifier.close(); super.dispose(); } @override Widget build(BuildContext context) { final l10n = context.l10n; - return WillPopScope( - onWillPop: () async => !widget.restrictToBack, + return PopScope( + canPop: !widget.restrictToBack, child: BasePage( - backgroundColor: Theme.of(context).colorScheme.background, + backgroundColor: Theme.of(context).colorScheme.surface, + title: '', + titleAlignment: Alignment.topCenter, + titleLeading: widget.restrictToBack ? null : const BackLeadingButton(), scrollView: false, - body: BlocBuilder( - builder: (context, state) { - return PinCodeWidget( - title: l10n.enterYourPinCode, - subTitle: l10n.pinCodeMessage, - passwordEnteredCallback: _onPasscodeEntered, - deleteButton: Text( - l10n.delete, - style: Theme.of(context).textTheme.labelLarge, - ), - cancelButton: Text( - l10n.cancel, - style: Theme.of(context).textTheme.labelLarge, - ), - cancelCallback: _onPasscodeCancelled, - isValidCallback: () async { - switch (widget.walletProtectionType) { - case WalletProtectionType.pinCode: - Navigator.pop(context); - widget.isValidCallback.call(); - case WalletProtectionType.biometrics: - throw ResponseMessage( - data: { - 'error': 'invalid_format', - 'error_description': 'The biomertics is not supported.', - }, - ); - case WalletProtectionType.FA2: - final LocalAuthApi localAuthApi = LocalAuthApi(); - final authenticated = await localAuthApi.authenticate( - localizedReason: l10n.scanFingerprintToAuthenticate, - ); - if (authenticated) { - Navigator.pop(context); - widget.isValidCallback.call(); - } else { - Navigator.pop(context); - AlertMessage.showStateMessage( - context: context, - stateMessage: StateMessage.success( - showDialog: false, - stringMessage: l10n.authenticationFailed, - ), - ); - } + body: PinCodeWidget( + title: l10n.enterYourPinCode, + subTitle: l10n.pinCodeMessage, + deleteButton: Text( + l10n.delete, + style: Theme.of(context).textTheme.labelLarge, + ), + cancelButton: Text( + l10n.cancel, + style: Theme.of(context).textTheme.labelLarge, + ), + cancelCallback: _onPasscodeCancelled, + isValidCallback: () async { + switch (widget.walletProtectionType) { + case WalletProtectionType.pinCode: + Navigator.pop(context); + widget.isValidCallback.call(); + case WalletProtectionType.biometrics: + throw ResponseMessage( + data: { + 'error': 'invalid_format', + 'error_description': 'The biomertics is not supported.', + }, + ); + case WalletProtectionType.FA2: + final LocalAuthApi localAuthApi = LocalAuthApi(); + final authenticated = await localAuthApi.authenticate( + localizedReason: l10n.scanFingerprintToAuthenticate, + ); + if (authenticated) { + Navigator.pop(context); + widget.isValidCallback.call(); + } else { + Navigator.pop(context); + AlertMessage.showStateMessage( + context: context, + stateMessage: StateMessage.success( + showDialog: false, + stringMessage: l10n.authenticationFailed, + ), + ); } - }, - shouldTriggerVerification: _verificationNotifier.stream, - allowAction: state.allowLogin, - ); + } + }, + // shouldTriggerVerification: _verificationNotifier.stream, + onLoginAttempt: (loginAttempt, loginAttemptsRemaining) { + if (loginAttemptsRemaining > 0) { + showModalBottomSheet( + context: context, + builder: (_) => WrongPinCodePopUp( + loginAttemptsRemaining: loginAttemptsRemaining, + ), + ); + } else { + Navigator.of(context).push(DeleteMyWalletPage.route()); + } }, ), ), ); } - Future _onPasscodeEntered(String enteredPasscode) async { - final profileCubit = context.read(); - - profileCubit.passcodeEntered(); - - bool isValid = false; - - if (profileCubit.state.allowLogin) { - isValid = (await getSecureStorage.get(SecureStorageKeys.pinCode)) == - enteredPasscode; - if (isValid) { - profileCubit.resetloginAttemptCount(); - } - } - _verificationNotifier.add(isValid); - } - void _onPasscodeCancelled() { Navigator.of(context).maybePop(); } diff --git a/lib/pin_code/widgets/circle_ui_config.dart b/lib/pin_code/widgets/circle_ui_config.dart index c1ca51523..2c338c2eb 100644 --- a/lib/pin_code/widgets/circle_ui_config.dart +++ b/lib/pin_code/widgets/circle_ui_config.dart @@ -1,12 +1,11 @@ -import 'package:altme/theme/app_theme/app_theme.dart'; import 'package:flutter/material.dart'; @immutable class CircleUIConfig { const CircleUIConfig({ - this.borderColor = pinCodeMiniCircle, + this.borderColor = Colors.white, this.borderWidth = 1.5, - this.fillColor = primary, + this.fillColor = const Color(0xff1EAADC), this.defaultColor = Colors.white, this.circleSize = 22, }); diff --git a/lib/pin_code/widgets/delete_button.dart b/lib/pin_code/widgets/delete_button.dart index 57941a128..2602c8fe5 100644 --- a/lib/pin_code/widgets/delete_button.dart +++ b/lib/pin_code/widgets/delete_button.dart @@ -1,6 +1,6 @@ import 'package:altme/app/app.dart'; import 'package:altme/pin_code/pin_code.dart'; -import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; class DeleteButton extends StatelessWidget { @@ -22,12 +22,12 @@ class DeleteButton extends StatelessWidget { Widget build(BuildContext context) { final PinCodeViewCubit pinCodeViewCubit = context.read(); final passCode = pinCodeViewCubit.state.enteredPasscode; - return CupertinoButton( + return TextButton( onPressed: () => context .read() .onDeleteCancelButtonPressed(cancelCallback), child: Container( - margin: keyboardUIConfig.digitInnerMargin, + // margin: keyboardUIConfig.digitInnerMargin, child: passCode.isEmpty ? cancelButton : deleteButton, ), ); diff --git a/lib/pin_code/widgets/num_keyboard.dart b/lib/pin_code/widgets/num_keyboard.dart index 82b023866..762064b7f 100644 --- a/lib/pin_code/widgets/num_keyboard.dart +++ b/lib/pin_code/widgets/num_keyboard.dart @@ -8,31 +8,33 @@ class NumKeyboard extends StatelessWidget { super.key, KeyboardUIConfig? keyboardUIConfig, this.passwordDigits = 4, - required this.passwordEnteredCallback, this.cancelCallback, required this.allowAction, + this.isNewCode = false, + required this.keyboardButton, }) : keyboardUIConfig = keyboardUIConfig ?? const KeyboardUIConfig(); final KeyboardUIConfig keyboardUIConfig; final int passwordDigits; - final PasswordEnteredCallback passwordEnteredCallback; final CancelCallback? cancelCallback; final bool allowAction; - + final bool isNewCode; + final Widget keyboardButton; @override Widget build(BuildContext context) { return NumericKeyboard( - allowAction: allowAction, + allowAction: true, onKeyboardTap: (text) { if (!allowAction) return; context.read().onKeyboardButtonPressed( passwordDigits: passwordDigits, - passwordEnteredCallback: passwordEnteredCallback, text: text, cancelCallback: cancelCallback, + isNewCode: isNewCode, ); }, keyboardUIConfig: keyboardUIConfig, + trailingButton: keyboardButton, ); } } diff --git a/lib/pin_code/widgets/pin_code_title.dart b/lib/pin_code/widgets/pin_code_title.dart index 940630da0..4865686de 100644 --- a/lib/pin_code/widgets/pin_code_title.dart +++ b/lib/pin_code/widgets/pin_code_title.dart @@ -1,4 +1,3 @@ -import 'package:altme/theme/theme.dart'; import 'package:flutter/material.dart'; class PinCodeTitle extends StatelessWidget { @@ -21,22 +20,22 @@ class PinCodeTitle extends StatelessWidget { title, textAlign: TextAlign.center, style: allowAction - ? Theme.of(context).textTheme.pinCodeTitle + ? Theme.of(context).textTheme.titleLarge : Theme.of(context) .textTheme - .pinCodeTitle - .copyWith(color: Theme.of(context).colorScheme.redColor), + .titleLarge! + .copyWith(color: Theme.of(context).colorScheme.error), ), if (subTitle != null) ...[ const SizedBox(height: 10), Text( subTitle!, style: allowAction - ? Theme.of(context).textTheme.pinCodeMessage + ? Theme.of(context).textTheme.bodyMedium : Theme.of(context) .textTheme - .pinCodeMessage - .copyWith(color: Theme.of(context).colorScheme.redColor), + .bodyMedium! + .copyWith(color: Theme.of(context).colorScheme.error), textAlign: TextAlign.center, ), ], diff --git a/lib/pin_code/widgets/pin_code_widget.dart b/lib/pin_code/widgets/pin_code_widget.dart index 2a832a227..e338e265d 100644 --- a/lib/pin_code/widgets/pin_code_widget.dart +++ b/lib/pin_code/widgets/pin_code_widget.dart @@ -1,22 +1,19 @@ -import 'dart:async'; - import 'package:altme/app/app.dart'; -import 'package:altme/dashboard/dashboard.dart'; +import 'package:altme/dashboard/profile/cubit/profile_cubit.dart'; import 'package:altme/l10n/l10n.dart'; import 'package:altme/pin_code/pin_code.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +typedef OnLoginAttempt = void Function(int, int); + class PinCodeWidget extends StatefulWidget { const PinCodeWidget({ super.key, required this.title, this.passwordDigits = 4, - required this.passwordEnteredCallback, required this.cancelButton, required this.deleteButton, - this.shouldTriggerVerification, this.isValidCallback, CircleUIConfig? circleUIConfig, KeyboardUIConfig? keyboardUIConfig, @@ -25,21 +22,24 @@ class PinCodeWidget extends StatefulWidget { this.cancelCallback, this.subTitle, this.header, - this.allowAction = true, + this.onLoginAttempt, + this.isNewCode = false, + this.isUserPin = false, }) : circleUIConfig = circleUIConfig ?? const CircleUIConfig(), keyboardUIConfig = keyboardUIConfig ?? const KeyboardUIConfig(); + final OnLoginAttempt? onLoginAttempt; final Widget? header; final String title; final String? subTitle; final int passwordDigits; - final PasswordEnteredCallback passwordEnteredCallback; - final bool allowAction; + final bool isNewCode; + // When a specific pin is requested to get a credential + final bool isUserPin; // Cancel button and delete button will be switched based on the screen state final Widget cancelButton; final Widget deleteButton; - final Stream? shouldTriggerVerification; final CircleUIConfig circleUIConfig; final KeyboardUIConfig keyboardUIConfig; @@ -56,40 +56,50 @@ class PinCodeWidget extends StatefulWidget { class _PinCodeWidgetState extends State with SingleTickerProviderStateMixin { - late StreamSubscription? streamSubscription; - late AnimationController controller; - late Animation animation; + late bool isPincodeDifferent; final log = getLogger('PinCodeWidget'); @override void initState() { super.initState(); - streamSubscription = - widget.shouldTriggerVerification?.listen(_showValidation); - controller = AnimationController( - duration: const Duration(milliseconds: 400), - vsync: this, - ); - final Animation curve = - CurvedAnimation(parent: controller, curve: ShakeCurve()); - animation = Tween(begin: 0, end: 10).animate(curve) - ..addStatusListener((status) { - if (status == AnimationStatus.completed) { - context.read().setEnteredPasscode(''); - controller.value = 0; - } - }); + isPincodeDifferent = false; } @override Widget build(BuildContext context) { final l10n = context.l10n; - return BlocBuilder( + return BlocConsumer( + listenWhen: (previous, current) { + if (current.enteredPasscode.length < widget.passwordDigits) { + setState(() { + isPincodeDifferent = false; + }); + } + if (current.enteredPasscode.length == widget.passwordDigits) { + if (current.isPinCodeValid || widget.isUserPin) { + _validationCallback(); + } else { + if (widget.title == l10n.confirmYourPinCode) { + setState(() { + isPincodeDifferent = true; + }); + } + } + } + return previous.loginAttemptCount != current.loginAttemptCount; + }, + listener: (context, state) { + widget.onLoginAttempt + ?.call(state.loginAttemptCount, state.loginAttemptsRemaining); + // if (state.enteredPasscode.length == widget.passwordDigits && + // widget.isUserPin) { + // _validationCallback(); + // } + }, builder: (context, state) { - final PinCodeViewCubit pinCodeViewCubit = - context.read(); - final enteredPasscode = pinCodeViewCubit.state.enteredPasscode; + final enteredPasscode = state.enteredPasscode; + final titleStyle = Theme.of(context).textTheme.labelMedium; return OrientationBuilder( builder: (context, orientation) { @@ -117,62 +127,89 @@ class _PinCodeWidgetState extends State 0.5, showPoweredBy: true, ), - const SizedBox(height: Sizes.spaceNormal), + const SizedBox(height: Sizes.spaceSmall), PinCodeTitle( title: widget.title, - subTitle: widget.allowAction + subTitle: state.allowAction ? widget.subTitle : l10n.pincodeAttemptMessage, - allowAction: widget.allowAction, + allowAction: state.allowAction, ), + const SizedBox(height: Sizes.spaceXSmall), Container( margin: const EdgeInsets.only(top: 16), height: 40, - child: AnimatedBuilder( - animation: animation, - builder: (_, __) { - return Row( - mainAxisAlignment: - MainAxisAlignment.center, - children: List.generate( - widget.passwordDigits, - (index) => Container( - margin: const EdgeInsets.all(8), - child: Circle( - allowAction: widget.allowAction, - filled: index < - enteredPasscode.length, - circleUIConfig: - widget.circleUIConfig, - extraSize: animation.value, - ), - ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: List.generate( + widget.passwordDigits, + (index) => Container( + margin: const EdgeInsets.all(8), + child: Circle( + allowAction: state.allowAction, + filled: + index < enteredPasscode.length, + circleUIConfig: (state + .isPincodeSequence || + state.isPincodeSeries || + isPincodeDifferent) + ? CircleUIConfig( + fillColor: Theme.of(context) + .colorScheme + .error, + ) + : widget.circleUIConfig, ), - ); - }, + ), + ), ), ), + SizedBox( + height: Sizes.spaceSmall, + child: isPincodeDifferent + ? Text( + l10n.pincodeDifferent, + style: titleStyle?.copyWith( + color: Theme.of(context) + .colorScheme + .error, + ), + textAlign: TextAlign.center, + ) + : state.isPincodeSeries + ? Text( + l10n.pincodeSerie, + style: titleStyle?.copyWith( + color: Theme.of(context) + .colorScheme + .error, + ), + textAlign: TextAlign.center, + ) + : state.isPincodeSequence + ? Text( + l10n.pincodeSequence, + style: titleStyle?.copyWith( + color: Theme.of(context) + .colorScheme + .error, + ), + textAlign: TextAlign.center, + ) + : const SizedBox.shrink(), + ), NumKeyboard( - passwordEnteredCallback: - widget.passwordEnteredCallback, keyboardUIConfig: widget.keyboardUIConfig, passwordDigits: widget.passwordDigits, cancelCallback: widget.cancelCallback, - allowAction: widget.allowAction, + allowAction: true, + isNewCode: widget.isNewCode, + keyboardButton: + KeyboardButton(widget: widget), ), widget.bottomWidget ?? Container(), ], ), - Positioned( - bottom: 0, - right: 0, - child: DeleteButton( - cancelButton: widget.cancelButton, - deleteButton: widget.deleteButton, - cancelCallback: widget.cancelCallback, - keyboardUIConfig: widget.keyboardUIConfig, - ), - ), ], ), ], @@ -199,58 +236,41 @@ class _PinCodeWidgetState extends State if (widget.header != null) widget.header! else - WalletLogo( - profileModel: context - .read() - .state - .model, - height: 90, - width: MediaQuery.of(context) - .size - .shortestSide * - 0.5, - showPoweredBy: true, - ), + // const AltMeLogo( + // size: Sizes.logoLarge, + // ), + const Center(), const SizedBox( height: Sizes.spaceNormal, ), PinCodeTitle( title: widget.title, subTitle: widget.subTitle, - allowAction: widget.allowAction, + allowAction: state.allowAction, ), Container( margin: const EdgeInsets.only(top: 20), height: 40, - child: AnimatedBuilder( - animation: animation, - builder: (_, __) { - return Row( - mainAxisAlignment: - MainAxisAlignment.center, - children: List.generate( - widget.passwordDigits, - (index) => Container( - margin: - const EdgeInsets.all( - 8, - ), - child: Circle( - allowAction: - widget.allowAction, - filled: index < - enteredPasscode - .length, - circleUIConfig: widget - .circleUIConfig, - extraSize: - animation.value, - ), - ), + child: Row( + mainAxisAlignment: + MainAxisAlignment.center, + children: List.generate( + widget.passwordDigits, + (index) => Container( + margin: const EdgeInsets.all( + 8, + ), + child: Circle( + allowAction: + state.allowAction, + filled: index < + enteredPasscode.length, + circleUIConfig: + widget.circleUIConfig, ), - ); - }, + ), + ), ), ), ], @@ -271,30 +291,16 @@ class _PinCodeWidgetState extends State ), Expanded( child: NumKeyboard( - passwordEnteredCallback: - widget.passwordEnteredCallback, keyboardUIConfig: widget.keyboardUIConfig, passwordDigits: widget.passwordDigits, cancelCallback: widget.cancelCallback, - allowAction: widget.allowAction, + allowAction: true, + keyboardButton: KeyboardButton(widget: widget), ), ), ], ), ), - Positioned( - bottom: 0, - right: 0, - child: Align( - alignment: Alignment.bottomRight, - child: DeleteButton( - cancelButton: widget.cancelButton, - deleteButton: widget.deleteButton, - cancelCallback: widget.cancelCallback, - keyboardUIConfig: widget.keyboardUIConfig, - ), - ), - ), ], ); }, @@ -303,32 +309,6 @@ class _PinCodeWidgetState extends State ); } - @override - void didUpdateWidget(PinCodeWidget old) { - super.didUpdateWidget(old); - // in case the stream instance changed, subscribe to the new one - if (widget.shouldTriggerVerification != old.shouldTriggerVerification) { - streamSubscription?.cancel(); - streamSubscription = - widget.shouldTriggerVerification?.listen(_showValidation); - } - } - - @override - void dispose() { - controller.dispose(); - streamSubscription?.cancel(); - super.dispose(); - } - - void _showValidation(bool isValid) { - if (isValid) { - _validationCallback(); - } else { - controller.forward(); - } - } - void _validationCallback() { if (widget.isValidCallback != null) { widget.isValidCallback!.call(); @@ -340,3 +320,22 @@ class _PinCodeWidgetState extends State } } } + +class KeyboardButton extends StatelessWidget { + const KeyboardButton({ + super.key, + required this.widget, + }); + + final PinCodeWidget widget; + + @override + Widget build(BuildContext context) { + return DeleteButton( + cancelButton: widget.cancelButton, + deleteButton: widget.deleteButton, + cancelCallback: widget.cancelCallback, + keyboardUIConfig: widget.keyboardUIConfig, + ); + } +} diff --git a/lib/pin_code/widgets/widgets.dart b/lib/pin_code/widgets/widgets.dart index e5cdb9f4d..778e28fcb 100644 --- a/lib/pin_code/widgets/widgets.dart +++ b/lib/pin_code/widgets/widgets.dart @@ -4,3 +4,4 @@ export 'num_keyboard.dart'; export 'pin_code_title.dart'; export 'pin_code_widget.dart'; export 'shake_curve.dart'; +export 'wrong_pincode_popup.dart'; diff --git a/lib/pin_code/widgets/wrong_pincode_popup.dart b/lib/pin_code/widgets/wrong_pincode_popup.dart new file mode 100644 index 000000000..7c3728199 --- /dev/null +++ b/lib/pin_code/widgets/wrong_pincode_popup.dart @@ -0,0 +1,28 @@ +import 'package:altme/app/shared/constants/icon_strings.dart'; +import 'package:altme/app/shared/widget/widget.dart'; +import 'package:altme/l10n/l10n.dart'; +import 'package:flutter/material.dart'; + +class WrongPinCodePopUp extends StatelessWidget { + const WrongPinCodePopUp({ + super.key, + required this.loginAttemptsRemaining, + }); + + final int loginAttemptsRemaining; + + @override + Widget build(BuildContext context) { + final l10n = context.l10n; + + return ConfirmDialog( + icon: IconStrings.alert, + title: l10n.userPinIsIncorrect, + subtitle: l10n.codeSecretIncorrectDescription( + loginAttemptsRemaining, + loginAttemptsRemaining == 1 ? '' : 's', + ), + yes: l10n.tryAgain, + ); + } +} diff --git a/lib/scan/cubit/scan_cubit.dart b/lib/scan/cubit/scan_cubit.dart index 577fcd88c..7f67490c4 100644 --- a/lib/scan/cubit/scan_cubit.dart +++ b/lib/scan/cubit/scan_cubit.dart @@ -81,9 +81,6 @@ class ScanCubit extends Cubit { ); if (isSIOPV2OROIDC4VPUrl(uri)) { - // final bool isEBSIV3 = - // await isEBSIV3ForVerifier(client: client, uri: uri) ?? false; - final responseType = uri.queryParameters['response_type'] ?? ''; final stateValue = uri.queryParameters['state']; @@ -96,26 +93,10 @@ class ScanCubit extends Cubit { 'The verifier side must not contain id_token only.', }, ); - } else if (isVPTokenOnly(responseType)) { - /// verifier side (oidc4vp) with request uri as value + } - await presentCredentialToOIDC4VPAndSIOPV2Request( - uri: uri, - issuer: issuer, - credentialsToBePresented: credentialsToBePresented!, - presentationDefinition: - credentialModel.credentialManifest!.presentationDefinition!, - oidc4vc: oidc4vc, - did: did, - kid: kid, - privateKey: privateKey, - stateValue: stateValue, - idTokenNeeded: false, - qrCodeScanCubit: qrCodeScanCubit!, - ); - return; - } else if (isIDTokenAndVPToken(responseType)) { - /// verifier side (oidc4vp and siopv2) with request uri as value + if (hasVPToken(responseType)) { + /// verifier side (oidc4vp) with request uri as value await presentCredentialToOIDC4VPAndSIOPV2Request( uri: uri, @@ -128,16 +109,16 @@ class ScanCubit extends Cubit { kid: kid, privateKey: privateKey, stateValue: stateValue, - idTokenNeeded: true, + idTokenNeeded: hasIDToken(responseType), qrCodeScanCubit: qrCodeScanCubit!, ); - return; } else { throw ResponseMessage( data: { 'error': 'invalid_request', 'error_description': + // ignore: lines_longer_than_80_chars 'The response type should contain id_token, vp_token or both.', }, ); @@ -183,8 +164,8 @@ class ScanCubit extends Cubit { for (final item in credentialsToBePresented) { final presentationId = 'urn:uuid:${const Uuid().v4()}'; - /// proof is done with a creation date 20 seconds in the past to avoid - /// proof check to fail because of time difference on server + /// proof is done with a creation date 20 seconds in the past to + /// avoid proof check to fail because of time difference on server final options = jsonEncode({ 'verificationMethod': kid, diff --git a/lib/selective_disclosure/widget/display_selective_disclosure.dart b/lib/selective_disclosure/widget/display_selective_disclosure.dart index 5757ef2fc..72634a1b4 100644 --- a/lib/selective_disclosure/widget/display_selective_disclosure.dart +++ b/lib/selective_disclosure/widget/display_selective_disclosure.dart @@ -2,7 +2,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/lang/cubit/lang_cubit.dart'; import 'package:altme/selective_disclosure/selective_disclosure.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -39,24 +39,6 @@ class DisplaySelectiveDisclosure extends StatelessWidget { final key = map.key; final value = map.value; - // "value_type": "string", - // "display": [ - // {"name": "Address", "locale": "en-US"}, - // {"name": "Adresse", "locale": "fr-FR"} - // ], - // "street_address": { - // "value_type": "string", - // "display": [ - // {"name": "Street address", "locale": "en-US"}, - // {"name": "Rue", "locale": "fr-FR"}], - // }, - // "locality": { - // "value_type": "string", - // "display": [ - // {"name": "Locality", "locale": "en-US"}, - // {"name": "Ville", "locale": "fr-FR"}], - // }, - /// nested day contains more data if (value is! Map) return Container(); @@ -76,10 +58,9 @@ class DisplaySelectiveDisclosure extends StatelessWidget { padding: const EdgeInsets.only(top: 10), child: Text( title, - style: - Theme.of(context).textTheme.credentialFieldTitle.copyWith( - color: Theme.of(context).colorScheme.titleColor, - ), + style: Theme.of(context).textTheme.bodyMedium!.copyWith( + color: Theme.of(context).colorScheme.onSurface, + ), ), ), Padding( @@ -203,8 +184,8 @@ class DisplaySelectiveDisclosure extends StatelessWidget { padding: EdgeInsets.only(top: isFirstElement ? 10 : 0), title: title, value: claims.data, - titleColor: Theme.of(context).colorScheme.titleColor, - valueColor: Theme.of(context).colorScheme.valueColor, + titleColor: Theme.of(context).colorScheme.onSurface, + valueColor: Theme.of(context).colorScheme.onSurface, showVertically: showVertically, ), if (selectiveDisclosureState != null && @@ -221,8 +202,8 @@ class DisplaySelectiveDisclosure extends StatelessWidget { color: disable != null && disable! ? Theme.of(context) .colorScheme - .lightGrey - .withOpacity(0.2) + .onSurface + .withOpacity(0.3) : Theme.of(context).colorScheme.onPrimary, ), ), diff --git a/lib/splash/bloclisteners/blocklisteners.dart b/lib/splash/bloclisteners/blocklisteners.dart index 38076f3fa..c928bb0bb 100644 --- a/lib/splash/bloclisteners/blocklisteners.dart +++ b/lib/splash/bloclisteners/blocklisteners.dart @@ -276,6 +276,7 @@ final qrCodeBlocListener = BlocListener( openIdConfiguration: openIdConfiguration, issuer: issuer, oidc4vciDraftType: customOidc4vcProfile.oidc4vciDraft, + dio: Dio(), ); credentialEndpoint = diff --git a/lib/splash/cubit/splash_cubit.dart b/lib/splash/cubit/splash_cubit.dart index b3e5835a0..c958c22e8 100644 --- a/lib/splash/cubit/splash_cubit.dart +++ b/lib/splash/cubit/splash_cubit.dart @@ -23,8 +23,9 @@ class SplashCubit extends Cubit { required this.altmeChatSupportCubit, required this.client, required this.profileCubit, + this.packageInfo, }) : super(const SplashState()) { - _getAppVersion(); + _getAppVersion(packageInfo); } final SecureStorageProvider secureStorageProvider; @@ -34,6 +35,7 @@ class SplashCubit extends Cubit { final AltmeChatSupportCubit altmeChatSupportCubit; final DioClient client; final ProfileCubit profileCubit; + final PackageInfo? packageInfo; Future initialiseApp() async { double counter = 0; @@ -71,8 +73,14 @@ class SplashCubit extends Cubit { }); } - Future _getAppVersion() async { - final PackageInfo packageInfo = await PackageInfo.fromPlatform(); + Future _getAppVersion(PackageInfo? packageInformation) async { + late PackageInfo packageInfo; + if (packageInformation == null) { + packageInfo = await PackageInfo.fromPlatform(); + } else { + packageInfo = packageInformation; + } + final String? savedVersion = await secureStorageProvider.get( SecureStorageKeys.version, ); diff --git a/lib/splash/view/splash_page.dart b/lib/splash/view/splash_page.dart index 74e646c1b..f66cfb19a 100644 --- a/lib/splash/view/splash_page.dart +++ b/lib/splash/view/splash_page.dart @@ -7,6 +7,7 @@ import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/deep_link/deep_link.dart'; import 'package:altme/enterprise/enterprise.dart'; import 'package:altme/l10n/l10n.dart'; +import 'package:altme/pin_code/view/pin_code_page.dart'; import 'package:altme/polygon_id/polygon_id.dart'; import 'package:altme/splash/splash.dart'; import 'package:flutter/foundation.dart'; @@ -42,7 +43,28 @@ class _SplashViewState extends State { void initState() { WidgetsBinding.instance.addPostFrameCallback( (_) async { + final Uri? initialUri = await _handleInitialUri(context); + await context.read().initialiseApp(); + + /// In case app is opened through deeplink we need to handle + /// incoming request. + if (initialUri != null) { + await Navigator.of(context).push( + PinCodePage.route( + restrictToBack: true, + isValidCallback: () { + Navigator.of(context).push(DashboardPage.route()); + }, + walletProtectionType: WalletProtectionType.pinCode, + ), + ); + + await processIncomingUri( + initialUri, + context, + ); + } }, ); super.initState(); @@ -198,12 +220,12 @@ class _SplashViewState extends State { /// throughout your app's life. /// /// We handle all exceptions, since it is called from initState. - Future _handleInitialUri(BuildContext context) async { + Future _handleInitialUri(BuildContext context) async { // In this example app this is an almost useless guard, but it is here to // show we are not going to call getInitialUri multiple times, even if this // was a widget that will be disposed of (ex. a navigation route change). final log = getLogger('DeepLink - _handleInitialUri'); - if (!mounted) return; + if (!mounted) return null; if (!_initialUriIsHandled) { _initialUriIsHandled = true; @@ -213,18 +235,19 @@ class _SplashViewState extends State { log.i('no initial uri'); } else { log.i('got initial uri: $uri'); - if (!mounted) return; + if (!mounted) return null; log.i('got uri: $uri'); - await processIncomingUri(uri, context); + return uri; } } on services.PlatformException { // Platform messages may fail but we ignore the exception log.e('falied to get initial uri'); } on FormatException catch (err) { - if (!mounted) return; + if (!mounted) return null; log.e('malformed initial uri: $err'); } } + return null; } @override @@ -249,7 +272,7 @@ class _SplashViewState extends State { if (state.status != AppStatus.success) return Container(); return BasePage( - backgroundColor: Theme.of(context).colorScheme.background, + backgroundColor: Theme.of(context).colorScheme.surface, scrollView: false, body: SafeArea( child: Padding( diff --git a/lib/splash/widgets/loading_progress.dart b/lib/splash/widgets/loading_progress.dart index 49dc2bb42..1382003ad 100644 --- a/lib/splash/widgets/loading_progress.dart +++ b/lib/splash/widgets/loading_progress.dart @@ -24,7 +24,7 @@ class LoadingProgress extends StatelessWidget { child: DecoratedBox( decoration: BoxDecoration( border: Border.all( - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, width: 1.5, ), borderRadius: BorderRadius.circular(borderRadius), diff --git a/lib/splash/widgets/subtitle_text.dart b/lib/splash/widgets/subtitle_text.dart index 10aef79db..e17b4d3e4 100644 --- a/lib/splash/widgets/subtitle_text.dart +++ b/lib/splash/widgets/subtitle_text.dart @@ -1,7 +1,7 @@ import 'package:altme/app/shared/enum/enum.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class SubTitle extends StatelessWidget { @@ -23,7 +23,7 @@ class SubTitle extends StatelessWidget { child: Text( isEnterprise ? tag : l10n.splashSubtitle, textAlign: TextAlign.center, - style: Theme.of(context).textTheme.starterSubTitleStyle, + style: Theme.of(context).textTheme.titleLarge, ), ); } diff --git a/lib/splash/widgets/title_text.dart b/lib/splash/widgets/title_text.dart index b0021d7e1..22ef88bc3 100644 --- a/lib/splash/widgets/title_text.dart +++ b/lib/splash/widgets/title_text.dart @@ -1,7 +1,7 @@ import 'package:altme/app/app.dart'; import 'package:altme/dashboard/profile/profile.dart'; import 'package:altme/l10n/l10n.dart'; -import 'package:altme/theme/theme.dart'; + import 'package:flutter/material.dart'; class TitleText extends StatelessWidget { @@ -26,7 +26,7 @@ class TitleText extends StatelessWidget { isEnterprise ? splashScreenTitle : '${Parameters.appName} ${l10n.wallet}', maxLines: 1, textAlign: TextAlign.center, - style: Theme.of(context).textTheme.starterTitleStyle, + style: Theme.of(context).textTheme.headlineLarge, ); } } diff --git a/lib/theme/app_theme/app_theme.dart b/lib/theme/app_theme/app_theme.dart index 0085936a7..c6b0b08ae 100644 --- a/lib/theme/app_theme/app_theme.dart +++ b/lib/theme/app_theme/app_theme.dart @@ -1,1044 +1,47 @@ import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; -const Color primary = icon; -// -const Color onPrimary = Color(0xffFFFFFF); -const Color secondary = Color(0xff00A1FF); -const Color onTertiary = Color(0xFFECF3FA); -// secondary dans figma Christophe pour new talao -const Color surface = Color(0xff191D2E); -const Color onSurface = Color(0xffEDEAF5); -const Color background = Color(0xff020820); -const Color error = Color(0xffcf6679); -const Color onError = Colors.black; -Color shadow = const Color(0xff1D1D1D).withOpacity(0.1); -const Color divider = Color(0xFF605A71); -const Color highlight = Color(0xFF36334E); -const Color icon = Color(0xff1EAADC); -const Color snackBarBackground = Colors.green; -const Color applied = Color(0xFF00B267); -const Color failed = Color(0xFFFF0045); -const Color skipped = Color(0xFFFF5F0A); -const Color network = Color(0xFF2C7DF7); - -const Color cardHighlight = Color(0xFF251F38); -const Color defaultDialog = Color(0xFF322643); -const Color closeIcon = Color(0xFFA79ABA); -const Color kycVerifyButton = Color(0xFF0045FF); -Color accountBottomSheetBorder = Colors.grey[200]!; -const Color digitPrimary = Colors.white; -const Color pinCodeMiniCircle = Color(0xFF656A73); -const Color startButtonA = Color(0xFF1EA3DC); -const Color startButtonB = Color(0xFF1EAADC); abstract class AppTheme { - /// A color frequently across your app’s screens and components. - static Color darkPrimary = primary; - - /// A color that's clearly legible when drawn on primary - static Color darkOnPrimary = onPrimary; - - /// An accent color used for less prominent components in the UI, such as - /// filter chips, while expanding the opportunity for color expression. - static Color darkSecondary = secondary; - - /// A color that's clearly legible when drawn on secondary - static Color darkOnSecondary = onPrimary; - - /// A color used as a contrasting accent that can balance primary - /// and secondary colors or bring heightened attention to an element, - /// such as an input field. - static Color darkTertiary = onPrimary; - - /// A color that's clearly legible when drawn on tertiary. - static Color darkOnTertiary = onTertiary; - - /// The background color for widgets like Card. - static Color darkSurface = surface; - - /// A color that's clearly legible when drawn on surface. - static Color darkOnSurface = onSurface; - - /// A color that typically appears behind scrollable content. - static Color darkBackground = background; - - /// A color that's clearly legible when drawn on background. - static Color darkOnBackground = onPrimary; - - /// The color to use for input validation errors, e.g. for - /// InputDecoration.errorText - static const Color darkError = error; - - /// A color that's clearly legible when drawn on error. - static const Color darkOnError = onError; - - /// A color use to paint the drop shadows of elevated components. - static Color darkShadow = shadow; - - static Color dividerColor = divider; - - static Color highlightColor = highlight; - - static ThemeData get darkThemeData => ThemeData( - useMaterial3: true, + static ThemeData get darkThemeData { + final theme = ThemeData( + useMaterial3: true, + brightness: Brightness.dark, + dividerColor: const Color(0xFF605A71), + highlightColor: const Color(0xFF36334E), + colorScheme: ColorScheme( + primary: const Color(0xff1EAADC), + onPrimary: const Color(0xffFFFFFF), + onPrimaryContainer: const Color(0xffFFFFFF), + primaryContainer: const Color(0xFF322643), + secondary: const Color(0xff00A1FF), + secondaryContainer: const Color(0xFFECF3FA), + onSecondary: const Color(0xffFFFFFF), + tertiary: const Color(0xFF1EA3DC), + onTertiary: const Color(0xFFECF3FA), + surface: const Color(0xff191D2E), + onSurface: const Color(0xffEDEAF5), + error: const Color(0xffcf6679), + onError: Colors.black, + shadow: const Color(0xff1D1D1D).withOpacity(0.1), + surfaceBright: const Color(0xff191D2E), + surfaceDim: const Color(0xff020820), brightness: Brightness.dark, - dividerColor: dividerColor, - highlightColor: highlightColor, - colorScheme: ColorScheme( - primary: darkPrimary, - onPrimary: darkOnPrimary, - secondary: darkSecondary, - onSecondary: darkOnSecondary, - tertiary: darkTertiary, - onTertiary: darkOnTertiary, - surface: darkSurface, - onSurface: darkOnSurface, - background: darkBackground, - onBackground: darkOnBackground, - error: darkError, - onError: darkOnError, - shadow: darkShadow, - brightness: Brightness.dark, - ), - textTheme: TextTheme( - bodySmall: GoogleFonts.poppins( - color: onPrimary, - fontSize: 13, - fontWeight: FontWeight.w600, - ), - titleLarge: GoogleFonts.poppins( - color: onPrimary, - fontSize: 20, - fontWeight: FontWeight.bold, - ), - headlineSmall: GoogleFonts.poppins( - color: onPrimary, - fontSize: 24, - fontWeight: FontWeight.bold, - ), - headlineMedium: GoogleFonts.poppins( - color: onPrimary, - fontSize: 28, - fontWeight: FontWeight.bold, - ), - ), - iconTheme: const IconThemeData(color: icon), - snackBarTheme: SnackBarThemeData( - backgroundColor: snackBarBackground, - contentTextStyle: GoogleFonts.poppins( - color: onPrimary, - fontSize: 12, - fontWeight: FontWeight.w400, - ), + ), + // textTheme: GoogleFonts.nunitoTextTheme(), + iconTheme: const IconThemeData(color: Color(0xff1EAADC)), + snackBarTheme: SnackBarThemeData( + backgroundColor: Colors.green, + contentTextStyle: GoogleFonts.poppins( + color: const Color(0xffFFFFFF), + fontSize: 12, + fontWeight: FontWeight.w400, ), - tabBarTheme: const TabBarTheme(dividerColor: Colors.transparent), - ); -} - -extension CustomColorScheme on ColorScheme { - Color get redColor => const Color(0xFFFF0045); - Color get transactionApplied => applied; - Color get transactionFailed => failed; - Color get transactionSkipped => skipped; - Color get activeColorOfNetwork => network; - - Color get greyText => onTertiary; - - Color get kycKeyIconColor => const Color(0xFF86809D); - - Color get lightPurple => const Color(0xFF5F556F); - - Color get popupBackground => onPrimary; - - Color get cardHighlighted => cardHighlight; - - Color get defaultDialogDark => defaultDialog; - - Color get closeIconColor => closeIcon; - - Color get kycVerifyButtonColor => kycVerifyButton; - - Color get checkMarkColor => applied; - - Color get accountBottomSheetBorderColor => accountBottomSheetBorder; - - Color get digitPrimaryColor => digitPrimary; - - Color get digitFillColor => Colors.transparent; - - Color get disabledBgColor => const Color(0xFF6A5F7B); - - Color get disabledTextColor => const Color(0xFF000000); - - Color get darkGradientStartColor => background; - - Color get darkGradientEndColor => background; - - Color get transparent => Colors.transparent; - - Color get onOutlineButton => primary; - - Color get onElevatedButton => onPrimary; - - Color get appBarUpperLayer => const Color(0xff25095B); - - Color get appBarLowerLayer => background; - - Color get surfaceContainer => const Color(0xff707070).withOpacity(0.07); - - // Color get drawerSurface => const Color(0xff232630); - Color get drawerSurface => surface; - - Color get label => digitPrimary; - - Color get unSelectedLabel => onTertiary; - - Color get leadingButton => const Color(0xffF1EFF8); - - Color get selectedBottomBar => surface; - - Color get drawerBackground => background; - - Color get borderColor => const Color(0xFFDDCEF4); - - Color get defualtDialogCancelButtonBorderColor => primary; - - Color get defualtDialogCancelButtonTextColor => primary; - - Color get markDownH1 => onPrimary; - - Color get markDownH2 => onPrimary; - - Color get markDownP => onTertiary; - - Color get markDownA => const Color(0xff517bff); - - Color get subtitle1 => onPrimary; - - Color get subtitle2 => const Color(0xFF8B8C92); - - Color get profileDummy => const Color(0xFF212121); - - Color get documentShadow => const Color(0xFF424242); - - Color get documentShape => const Color(0xff3700b3).withOpacity(0.05); - - Color get star => const Color(0xFFFFB83D); - - Color get genderIcon => const Color(0xFF212121); - - Color get activeCredential => snackBarBackground; - - Color get expiredCredential => Colors.orange; - - Color get revokedCredential => Colors.red; - - Color get buttonDisabled => const Color(0xFF424242); - - Color get alertErrorMessage => Colors.red; - - Color get alertWarningMessage => Colors.yellow; - - Color get alertInfoMessage => Colors.cyan; - - Color get alertSuccessMessage => snackBarBackground; - - Color get qrScanBackground => const Color(0xff191D2E); - - Color get qrScanInnerShadow => const Color(0xff0A0215); - - Color get qrScanOuterShadow => const Color(0xff430F91); - - Color get dialogText => const Color(0xFF180B2B); - - Color get tabBarNotSelected => const Color(0xFF280164); - - Color get credentialBackground => const Color(0xFF211F33); - - Color get cryptoAccountNotSelected => Colors.grey.withOpacity(0.15); - - Color get startButtonColorA => startButtonA; - - Color get startButtonColorB => startButtonB; - - Color get associatedWalletBorder => const Color(0xff524B67); - - Color get deleteColor => defaultDialog; - - Color get titleColor => onTertiary; - - Color get valueColor => onPrimary; - - Color get lightGrey => const Color(0xFF616161); - - Color get darkGrey => const Color(0xFF212121); - - Color get activeColor => const Color(0xFF08B530); - - Color get inactiveColor => failed; - - Color get beaconBorder => onTertiary; - - Color get cardBackground => const Color(0xFF211F33); -} - -extension CustomTextTheme on TextTheme { - TextStyle get hintTextFieldStyle => GoogleFonts.poppins( - fontSize: 14, - height: 1.5, - letterSpacing: 1.02, - fontWeight: FontWeight.normal, - color: onTertiary, - ); - - TextStyle get textFieldTitle => GoogleFonts.poppins( - color: const Color(0xFFFFFFFF), - fontSize: 16, - fontWeight: FontWeight.w400, - ); - - TextStyle get keyboardDigitTextStyle => - GoogleFonts.poppins(fontSize: 30, color: digitPrimary); - - TextStyle get calculatorKeyboardDigitTextStyle => GoogleFonts.poppins( - fontSize: 30, - color: digitPrimary, - fontWeight: FontWeight.bold, - ); - - TextStyle get keyboardDeleteButtonTextStyle => - GoogleFonts.poppins(fontSize: 16, color: digitPrimary); - - TextStyle get loadingText => GoogleFonts.poppins( - color: onPrimary, - fontSize: 16, - fontWeight: FontWeight.w600, - ); - - TextStyle get starterTitleStyle => GoogleFonts.poppins( - color: onPrimary, - fontSize: 32, - fontWeight: FontWeight.w700, - ); - - TextStyle get subtitle3 => GoogleFonts.poppins( - color: onTertiary, - fontSize: 16, - fontWeight: FontWeight.w400, - ).copyWith(height: 1.4); - - TextStyle get customListTileTitleStyle => GoogleFonts.poppins( - color: onPrimary, - fontSize: 18, - fontWeight: FontWeight.w800, - ); - - TextStyle get customListTileSubTitleStyle => GoogleFonts.poppins( - color: onTertiary, - fontSize: 16, - fontWeight: FontWeight.w400, - ); - - TextStyle get starterSubTitleStyle => GoogleFonts.poppins( - color: const Color(0xFFEEEEEE), - fontSize: 22, - fontWeight: FontWeight.w600, - ); - - TextStyle get badgeStyle => GoogleFonts.poppins( - color: const Color(0xFFEEEEEE), - fontSize: 8, - fontWeight: FontWeight.w500, - ); - - TextStyle get onBoardingTitleStyle => GoogleFonts.poppins( - color: onPrimary, - fontSize: 22, - fontWeight: FontWeight.w600, - ); - - TextStyle get onBoardingSubTitleStyle => GoogleFonts.poppins( - color: onTertiary, - fontSize: 16, - fontWeight: FontWeight.w400, - ); - - TextStyle get learnMoreTextStyle => GoogleFonts.poppins( - color: onTertiary, - fontSize: 16, - fontWeight: FontWeight.w400, - decoration: TextDecoration.underline, - ); - - TextStyle get infoTitle => GoogleFonts.poppins( - color: onSurface, - fontSize: 20, - fontWeight: FontWeight.w600, - ); - - TextStyle get infoSubtitle => GoogleFonts.poppins( - color: onTertiary, - fontSize: 16, - fontWeight: FontWeight.w400, - ); - - TextStyle get normal => GoogleFonts.poppins( - color: onTertiary, - fontSize: 16, - fontWeight: FontWeight.w400, - ); - - TextStyle get appBar => GoogleFonts.poppins( - color: onPrimary, - fontSize: 24, - fontWeight: FontWeight.w800, - ); - - TextStyle get bottomBar => GoogleFonts.poppins( - color: onPrimary, - fontSize: 10, - fontWeight: FontWeight.w600, - ); - - TextStyle get title => GoogleFonts.poppins( - color: onPrimary, - fontSize: 18, - fontWeight: FontWeight.w800, - ); - - TextStyle get listTitle => GoogleFonts.poppins( - color: onSurface, - fontSize: 20, - fontWeight: FontWeight.w600, - ); - - TextStyle get listSubtitle => GoogleFonts.poppins( - color: onSurface, - fontSize: 13, - fontWeight: FontWeight.w500, - ); - - TextStyle get bodySmall2 => GoogleFonts.poppins( - color: const Color(0xFF8682A8), - fontSize: 12, - fontWeight: FontWeight.w400, - ); - - TextStyle get bodySmall3 => GoogleFonts.poppins( - color: onTertiary, - fontSize: 16, - fontWeight: FontWeight.w400, - ); - - TextStyle get listTileTitle => GoogleFonts.poppins( - color: onPrimary, - fontSize: 14, - fontWeight: FontWeight.w600, - ); - - TextStyle get listTileSubtitle => GoogleFonts.poppins( - color: const Color(0xFF8682A8), - fontSize: 12, - fontWeight: FontWeight.w400, - ); - - TextStyle get close => GoogleFonts.poppins( - color: const Color(0xFFD6C3F2), - fontSize: 13, - fontWeight: FontWeight.w400, - ); - - TextStyle get dialogClose => GoogleFonts.poppins( - color: closeIcon, - fontSize: 12, - fontWeight: FontWeight.w400, - ); - - TextStyle get drawerMenu => GoogleFonts.poppins( - color: onTertiary, - fontSize: 15, - fontWeight: FontWeight.w400, - ); - - TextStyle get drawerItemTitle => GoogleFonts.poppins( - color: onPrimary, - fontSize: 16, - fontWeight: FontWeight.w800, - ); - - TextStyle get drawerItemSubtitle => GoogleFonts.poppins( - color: onPrimary, - fontSize: 14, - fontWeight: FontWeight.w500, - ); - - TextStyle get drawerCategoryTitle => GoogleFonts.poppins( - color: onPrimary, - fontSize: 18, - fontWeight: FontWeight.w800, - ); - - TextStyle get resetWalletTitle => GoogleFonts.poppins( - color: onPrimary, - fontSize: 16, - fontWeight: FontWeight.w700, - ); - TextStyle get resetWalletSubtitle => GoogleFonts.poppins( - color: onPrimary, - fontSize: 16, - fontWeight: FontWeight.w500, - ); - TextStyle get subtitle4 => GoogleFonts.poppins( - color: secondary, - fontSize: 16, - fontWeight: FontWeight.w500, - ); - - TextStyle get drawerCategorySubTitle => GoogleFonts.poppins( - color: onTertiary, - fontSize: 16, - fontWeight: FontWeight.w400, - ); - - TextStyle get biometricMessage => GoogleFonts.poppins( - color: const Color(0xFFB1ADC3), - fontSize: 12, - fontWeight: FontWeight.w400, - ); - - TextStyle get pinCodeTitle => GoogleFonts.poppins( - color: onPrimary, - fontSize: 20, - fontWeight: FontWeight.w700, - ); - - TextStyle get pinCodeMessage => GoogleFonts.poppins( - color: onPrimary, - fontSize: 14, - fontWeight: FontWeight.w400, - ); - - TextStyle get getCardsButton => GoogleFonts.poppins( - color: onPrimary, - fontSize: 12, - fontWeight: FontWeight.w600, - ); - - TextStyle get miniButton => GoogleFonts.poppins( - color: onPrimary, - fontSize: 11, - fontWeight: FontWeight.w500, - ); - - TextStyle get credentialTitle => GoogleFonts.poppins( - color: const Color(0xFF424242), - fontSize: 15, - fontWeight: FontWeight.bold, - ); - - TextStyle get credentialDescription => GoogleFonts.poppins( - color: const Color(0xFF757575), - fontSize: 13, - fontWeight: FontWeight.w400, - ); - - TextStyle get polygonCardDetail => GoogleFonts.roboto( - color: const Color(0xffFFFFFF), - fontSize: 13, - fontWeight: FontWeight.w400, - ); - - TextStyle get credentialFieldTitle => GoogleFonts.poppins( - color: const Color(0xff212121), - fontSize: 14, - fontWeight: FontWeight.w400, - ); - - TextStyle get credentialFieldDescription => GoogleFonts.poppins( - color: const Color(0xff212121), - fontSize: 14, - height: 1.5, - fontWeight: FontWeight.w400, - ); - - TextStyle get discoverFieldTitle => GoogleFonts.poppins( - color: onTertiary, - fontSize: 14, - fontWeight: FontWeight.w800, - ); - - TextStyle get discoverFieldDescription => GoogleFonts.poppins( - color: onPrimary, - fontSize: 14, - height: 1.5, - fontWeight: FontWeight.w400, - ); - - TextStyle get learningAchievementTitle => GoogleFonts.poppins( - color: const Color(0xff212121), - fontSize: 12, - fontWeight: FontWeight.w600, - ); - - TextStyle get learningAchievementDescription => GoogleFonts.poppins( - color: const Color(0xff212121), - fontSize: 12, - fontWeight: FontWeight.w400, - ); - - TextStyle get credentialIssuer => GoogleFonts.poppins( - color: const Color(0xff212121), - fontSize: 13, - fontWeight: FontWeight.w500, - ); - - TextStyle get imageCard => GoogleFonts.poppins( - color: const Color(0xff212121), - fontSize: 12, - fontWeight: FontWeight.w500, - ); - - TextStyle get loyaltyCard => GoogleFonts.poppins( - color: onPrimary, - fontSize: 13, - fontWeight: FontWeight.w600, - ); - - TextStyle get professionalExperienceAssessmentRating => GoogleFonts.poppins( - color: const Color(0xff212121), - fontSize: 13, - fontWeight: FontWeight.w500, - ); - - TextStyle get voucherOverlay => GoogleFonts.poppins( - color: onPrimary, - fontSize: 13, - fontWeight: FontWeight.w500, - ); - - TextStyle get certificateOfEmploymentTitleCard => GoogleFonts.roboto( - color: const Color(0xFF0650C6), - fontSize: 20, - fontWeight: FontWeight.bold, - ); - - TextStyle get certificateOfEmploymentDescription => GoogleFonts.poppins( - color: const Color(0xFF757575), - fontSize: 13, - fontWeight: FontWeight.normal, - ); - - TextStyle get certificateOfEmploymentData => GoogleFonts.poppins( - color: const Color(0xFF434e62), - fontSize: 12, - fontWeight: FontWeight.normal, - ); - - TextStyle get identityCardData => GoogleFonts.poppins( - color: onPrimary, - fontSize: 12, - fontWeight: FontWeight.normal, - ); - - TextStyle get tezosAssociatedAddressData => GoogleFonts.poppins( - color: divider, - fontSize: 17, - fontWeight: FontWeight.normal, - ); - - TextStyle get tezosAssociatedAddressTitleCard => GoogleFonts.poppins( - color: const Color(0xffFAFDFF), - fontSize: 20, - fontWeight: FontWeight.w700, - ); - - TextStyle get credentialStudentCardTextCard => GoogleFonts.poppins( - color: onPrimary, - fontSize: 14, - fontWeight: FontWeight.normal, - ); - - TextStyle get over18 => GoogleFonts.poppins( - color: onPrimary, - fontSize: 20, - fontWeight: FontWeight.normal, - ); - - TextStyle get studentCardSchool => GoogleFonts.poppins( - color: const Color(0xff9dc5ff), - fontSize: 15, - fontWeight: FontWeight.bold, - ); - - TextStyle get studentCardData => GoogleFonts.poppins( - color: onPrimary, - fontSize: 12, - fontWeight: FontWeight.normal, - ); - - TextStyle get credentialTitleCard => GoogleFonts.poppins( - color: onPrimary, - fontSize: 20, - fontWeight: FontWeight.bold, - ); - - TextStyle get voucherValueCard => GoogleFonts.poppins( - color: const Color(0xFFFEEA00), - fontSize: 50, - fontWeight: FontWeight.bold, - ); - - TextStyle get credentialTextCard => GoogleFonts.poppins( - color: const Color(0xff212121), - fontSize: 14, - fontWeight: FontWeight.normal, - ); - - TextStyle get illustrationPageDescription => GoogleFonts.poppins( - color: digitPrimary, - fontSize: 16, - fontWeight: FontWeight.w600, - ); - - TextStyle get dialogTitle => GoogleFonts.poppins( - fontSize: 18, - fontWeight: FontWeight.w600, - color: digitPrimary, - ); - - TextStyle get dialogSubtitle => GoogleFonts.poppins( - fontSize: 13, - fontWeight: FontWeight.w400, - color: digitPrimary.withOpacity(0.67), - ); - - TextStyle get walletAltme => GoogleFonts.poppins( - fontSize: 25, - fontWeight: FontWeight.w600, - color: const Color(0xff180B2B), - ); - - TextStyle get finishVerificationDialogTitle => GoogleFonts.poppins( - fontSize: 25, - fontWeight: FontWeight.bold, - color: const Color(0xff180B2B), - ); - - TextStyle get finishVerificationDialogBody => GoogleFonts.poppins( - fontSize: 14, - fontWeight: FontWeight.w600, - color: const Color(0xFF5F556F), - ); - - TextStyle get defaultDialogTitle => GoogleFonts.poppins( - fontSize: 25, - fontWeight: FontWeight.bold, - color: const Color(0xffF5F5F5), - ); - - TextStyle get defaultDialogBody => GoogleFonts.poppins( - fontSize: 14, - fontWeight: FontWeight.w400, - color: const Color(0xFF86809D), - ); - - TextStyle get defaultDialogSubtitle => GoogleFonts.poppins( - fontSize: 20, - color: const Color(0xff86809D), - ); - - TextStyle get newVersionTitle => GoogleFonts.poppins( - fontSize: 18, - fontWeight: FontWeight.w800, - color: background, - ); - - TextStyle get kycDialogTitle => GoogleFonts.poppins( - fontSize: 25, - fontWeight: FontWeight.bold, - color: const Color(0xffF5F5F5), - ); - - TextStyle get kycDialogBodySmall => GoogleFonts.poppins( - fontSize: 20, - fontWeight: FontWeight.bold, - color: kycVerifyButton, - ); - - TextStyle get kycDialogBody => GoogleFonts.poppins( - fontSize: 14, - fontWeight: FontWeight.w700, - color: const Color(0xff86809D), - ); - - TextStyle get kycDialogFooter => GoogleFonts.poppins( - fontSize: 10, - fontWeight: FontWeight.w500, - color: const Color(0xff86809D), - ); - - TextStyle get walletAltmeMessage => GoogleFonts.poppins( - fontSize: 16, - fontWeight: FontWeight.w400, - color: const Color(0xff9A8BB1), - ); - - TextStyle get credentialCategoryTitle => GoogleFonts.poppins( - fontSize: 18, - fontWeight: FontWeight.w600, - color: onSurface, - ); - - TextStyle get credentialCategorySubTitle => GoogleFonts.poppins( - fontSize: 14, - fontWeight: FontWeight.normal, - color: onTertiary, - ); - - TextStyle get credentialSurfaceText => GoogleFonts.poppins( - fontSize: 10, - fontWeight: FontWeight.w400, - color: applied, - ); - - TextStyle get errorMessage => GoogleFonts.poppins( - fontSize: 15, - fontWeight: FontWeight.w400, - color: onPrimary, - ); - - TextStyle get accountsText => GoogleFonts.poppins( - fontSize: 22, - fontWeight: FontWeight.w600, - color: onPrimary, - ); - - TextStyle get accountsName => GoogleFonts.poppins( - fontSize: 18, - fontWeight: FontWeight.w500, - color: onPrimary, - ); - - TextStyle get accountsListItemTitle => GoogleFonts.poppins( - fontSize: 16, - fontWeight: FontWeight.w500, - color: onPrimary, - ); - - TextStyle get walletAddress => GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w400, - color: const Color(0xFF757575), - ); - - TextStyle get textButton => GoogleFonts.poppins( - fontSize: 16, - fontWeight: FontWeight.w700, - color: primary, - ); - - TextStyle get scrollText => GoogleFonts.poppins( - fontSize: 9, - fontWeight: FontWeight.w500, - color: onPrimary, - ); - - TextStyle get passPhraseText => GoogleFonts.poppins( - fontSize: 14, - fontWeight: FontWeight.w600, - color: onTertiary, - ); - - TextStyle get message => GoogleFonts.poppins( - fontSize: 18, - fontWeight: FontWeight.w600, - color: onSurface, - ); - - TextStyle get subMessage => GoogleFonts.poppins( - fontSize: 15, - fontWeight: FontWeight.w400, - color: const Color(0xff71CBFF), - ); - - TextStyle get genPhraseSubmessage => GoogleFonts.poppins( - fontSize: 18, - fontWeight: FontWeight.w400, - color: const Color(0xff71CBFF), - ); - - TextStyle get pheaseVerifySubmessage => GoogleFonts.poppins( - fontSize: 18, - fontWeight: FontWeight.w400, - color: const Color(0xff86809D), - ); - - TextStyle get credentialBaseLightText => GoogleFonts.poppins( - fontSize: 16, - fontWeight: FontWeight.w300, - color: onPrimary, - ); - TextStyle get credentialBaseBoldText => GoogleFonts.poppins( - fontSize: 16, - fontWeight: FontWeight.w600, - color: onPrimary, - ); - - TextStyle get credentialBaseTitleText => GoogleFonts.poppins( - fontSize: 36, - fontWeight: FontWeight.w600, - color: const Color(0xffFFFFFF), - ); - - TextStyle get copyToClipBoard => GoogleFonts.poppins( - fontSize: 18, - fontWeight: FontWeight.w400, - color: const Color(0xffDED6EA), - decoration: TextDecoration.underline, - ); - - TextStyle get onBoardingCheckMessage => GoogleFonts.poppins( - fontSize: 18, - fontWeight: FontWeight.w700, - color: onPrimary, - ); - - TextStyle get messageTitle => GoogleFonts.poppins( - fontSize: 20, - fontWeight: FontWeight.w600, - color: onSurface, - ); - - TextStyle get messageSubtitle => GoogleFonts.poppins( - fontSize: 16, - fontWeight: FontWeight.w400, - color: onSurface, - ); - - TextStyle get radioTitle => GoogleFonts.poppins( - color: onPrimary, - fontSize: 20, - fontWeight: FontWeight.w600, - ); - - TextStyle get radioOption => GoogleFonts.poppins( - color: onPrimary, - fontSize: 16, - fontWeight: FontWeight.w400, - ); - - TextStyle get credentialManifestTitle1 => GoogleFonts.poppins( - color: onPrimary, - fontSize: 18, - fontWeight: FontWeight.w700, - ); - - TextStyle get credentialManifestDescription => GoogleFonts.poppins( - color: onPrimary, - fontSize: 16, - fontWeight: FontWeight.w400, - ); - - TextStyle get credentialManifestTitle2 => GoogleFonts.poppins( - color: onPrimary, - fontSize: 16, - fontWeight: FontWeight.w700, - ); - - TextStyle get credentialSubtitle => GoogleFonts.poppins( - color: onPrimary, - fontSize: 14, - fontWeight: FontWeight.w400, - ); - - TextStyle get credentialStatus => GoogleFonts.poppins( - color: onPrimary, - fontSize: 18, - fontWeight: FontWeight.w800, - ); - - TextStyle get beaconRequestPermission => GoogleFonts.poppins( - color: onTertiary, - fontSize: 16, - fontWeight: FontWeight.w400, - ); - - TextStyle get beaconSelectAccont => GoogleFonts.poppins( - color: onPrimary, - fontSize: 18, - fontWeight: FontWeight.w800, - ); - - TextStyle get uploadFileTitle => GoogleFonts.poppins( - color: onPrimary, - fontSize: 18, - fontWeight: FontWeight.w800, - ); - - TextStyle get beaconPermissionTitle => GoogleFonts.poppins( - color: onPrimary, - fontSize: 18, - fontWeight: FontWeight.w800, - ); - - TextStyle get beaconPermissions => GoogleFonts.poppins( - color: onPrimary, - fontSize: 16, - fontWeight: FontWeight.w400, - ); - - TextStyle get beaconPayload => GoogleFonts.poppins( - color: onPrimary, - fontSize: 16, - fontWeight: FontWeight.w400, - ); - - TextStyle get beaconWalletAddress => GoogleFonts.poppins( - fontSize: 16, - fontWeight: FontWeight.w400, - color: onPrimary, - ); - - TextStyle get dappName => GoogleFonts.poppins( - fontSize: 16, - fontWeight: FontWeight.w400, - color: onPrimary, - ); - - TextStyle get cacheErrorMessage => GoogleFonts.poppins( - fontSize: 16, - fontWeight: FontWeight.w600, - color: onPrimary, - ); - - TextStyle get credentialSteps => GoogleFonts.poppins( - color: onPrimary, - fontSize: 18, - fontWeight: FontWeight.w600, - ); - - TextStyle get discoverOverlayDescription => GoogleFonts.poppins( - color: onPrimary, - fontSize: 11, - fontWeight: FontWeight.w600, - ); - - TextStyle get faqQue => GoogleFonts.poppins( - color: onPrimary, - fontSize: 16, - fontWeight: FontWeight.normal, - ); - - TextStyle get faqAns => GoogleFonts.poppins( - color: const Color(0xFF757575), - fontSize: 14, - fontWeight: FontWeight.w400, - ); - - TextStyle get proofCardDetail => GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w300, - color: const Color(0xffFFFFFF), - ); + ), + tabBarTheme: const TabBarTheme(dividerColor: Colors.transparent), + ); + return theme.copyWith( + textTheme: GoogleFonts.poppinsTextTheme(theme.textTheme), + ); + } } diff --git a/packages/cryptocurrency_keys/test/src/cryptocurrency_keys_test.dart b/packages/cryptocurrency_keys/test/src/cryptocurrency_keys_test.dart index a0228db15..8803b7690 100644 --- a/packages/cryptocurrency_keys/test/src/cryptocurrency_keys_test.dart +++ b/packages/cryptocurrency_keys/test/src/cryptocurrency_keys_test.dart @@ -6,8 +6,9 @@ void main() { 'notice photo opera keen climb agent soft parrot best joke field devote'; const message = '{"name": "My name is Bibash."}'; - const cipherText = '¨`ýp<ÐW3AR1¯#.í©¥¿e’,|VrtuXÅ'; - const authenticationTag = '䃆U~ḈÞ¦BÌuDÅ'; + const cipherText = '‚Ô\$æ²½-ôÜwĪ8aã~…\n' + 'Yyz¶«Þž[f*ýç'; + const authenticationTag = '¸^\x01áAê?^Ü6u¿¡¡S'; const encryptionModelJson = { 'cipherText': cipherText, 'authenticationTag': authenticationTag, @@ -42,12 +43,13 @@ void main() { 'generateKeyPair() should always derive the same keypair when using the' ' same mnemonic', () async { final generatedKey = await cryptocurrencyKeys.generateKeyPair(mnemonic); - expect(generatedKey.privateKey.hashCode, equals(1100798733)); - expect(generatedKey.publicKey.hashCode, equals(1100798733)); + expect(generatedKey.privateKey.hashCode, equals(264718007)); + expect(generatedKey.publicKey.hashCode, equals(264718007)); }); test('response is encrypted correctly', () async { final encryptedData = await cryptocurrencyKeys.encrypt(message, mnemonic); + expect(encryptedData.cipherText, equals(cipherText)); expect(encryptedData.authenticationTag, equals(authenticationTag)); }); @@ -64,32 +66,29 @@ void main() { expect(decryptedData, equals(message)); }); - test('invalid cipherText throws Auth message', () async { + test('invalid cipherText throws Exception', () async { const inCipherText = '123'; const encryption = Encryption( cipherText: inCipherText, authenticationTag: authenticationTag, ); - try { - await cryptocurrencyKeys.decrypt(mnemonic, encryption); - } catch (e) { - final error = e.toString().startsWith('Auth message'); - expect(error, true); - } + + expect( + () => cryptocurrencyKeys.decrypt(mnemonic, encryption), + throwsException, + ); }); - test('invalid authenticationTag throws Auth message', () async { + test('invalid authenticationTag throws Exception', () async { const inValidAuthenticationTag = '123'; const encryption = Encryption( cipherText: cipherText, authenticationTag: inValidAuthenticationTag, ); - try { - await cryptocurrencyKeys.decrypt(mnemonic, encryption); - } catch (e) { - final error = e.toString().startsWith('Auth message'); - expect(error, true); - } + expect( + () => cryptocurrencyKeys.decrypt(mnemonic, encryption), + throwsException, + ); }); }); } diff --git a/packages/did_kit/lib/src/did_kit_provider.dart b/packages/did_kit/lib/src/did_kit_provider.dart index c4d19d442..fba7dd2c0 100644 --- a/packages/did_kit/lib/src/did_kit_provider.dart +++ b/packages/did_kit/lib/src/did_kit_provider.dart @@ -1,20 +1,26 @@ -import 'package:didkit/didkit.dart'; +import 'package:did_kit/src/didkit_interface.dart'; +import 'package:did_kit/src/didkit_wrapper.dart'; class DIDKitProvider { + DIDKitProvider({DIDKitInterface? didKit}) + : didKit = didKit ?? DIDKitWrapper(); + + final DIDKitInterface didKit; + String getVersion() { - return DIDKit.getVersion(); + return didKit.getVersion(); } String generateEd25519Key() { - return DIDKit.generateEd25519Key(); + return didKit.generateEd25519Key(); } String keyToDID(String methodName, String key) { - return DIDKit.keyToDID(methodName, key); + return didKit.keyToDID(methodName, key); } Future keyToVerificationMethod(String methodName, String key) async { - return DIDKit.keyToVerificationMethod(methodName, key); + return didKit.keyToVerificationMethod(methodName, key); } Future issueCredential( @@ -22,14 +28,14 @@ class DIDKitProvider { String options, String key, ) async { - return DIDKit.issueCredential(credential, options, key); + return didKit.issueCredential(credential, options, key); } Future verifyCredential( String credential, String options, ) async { - return DIDKit.verifyCredential(credential, options); + return didKit.verifyCredential(credential, options); } Future issuePresentation( @@ -37,28 +43,28 @@ class DIDKitProvider { String options, String key, ) async { - return DIDKit.issuePresentation(presentation, options, key); + return didKit.issuePresentation(presentation, options, key); } Future verifyPresentation( String presentation, String options, ) async { - return DIDKit.verifyPresentation(presentation, options); + return didKit.verifyPresentation(presentation, options); } Future resolveDID( String did, String inputMetadata, ) async { - return DIDKit.resolveDID(did, inputMetadata); + return didKit.resolveDID(did, inputMetadata); } Future dereferenceDIDURL( String didUrl, String inputMetadata, ) async { - return DIDKit.dereferenceDIDURL(didUrl, inputMetadata); + return didKit.dereferenceDIDURL(didUrl, inputMetadata); } Future didAuth( @@ -66,6 +72,6 @@ class DIDKitProvider { String options, String key, ) async { - return DIDKit.DIDAuth(did, options, key); + return didKit.didAuth(did, options, key); } } diff --git a/packages/did_kit/lib/src/didkit_interface.dart b/packages/did_kit/lib/src/didkit_interface.dart new file mode 100644 index 000000000..be25ced3f --- /dev/null +++ b/packages/did_kit/lib/src/didkit_interface.dart @@ -0,0 +1,17 @@ +abstract class DIDKitInterface { + String getVersion(); + String generateEd25519Key(); + String keyToDID(String methodName, String key); + Future keyToVerificationMethod(String methodName, String key); + Future issueCredential(String credential, String options, String key); + Future verifyCredential(String credential, String options); + Future issuePresentation( + String presentation, + String options, + String key, + ); + Future verifyPresentation(String presentation, String options); + Future resolveDID(String did, String inputMetadata); + Future dereferenceDIDURL(String didUrl, String inputMetadata); + Future didAuth(String did, String options, String key); +} diff --git a/packages/did_kit/lib/src/didkit_wrapper.dart b/packages/did_kit/lib/src/didkit_wrapper.dart new file mode 100644 index 000000000..3edbdf29b --- /dev/null +++ b/packages/did_kit/lib/src/didkit_wrapper.dart @@ -0,0 +1,83 @@ +import 'package:did_kit/src/didkit_interface.dart'; +import 'package:didkit/didkit.dart'; + +class DIDKitWrapper implements DIDKitInterface { + @override + String getVersion() { + return DIDKit.getVersion(); + } + + @override + String generateEd25519Key() { + return DIDKit.generateEd25519Key(); + } + + @override + String keyToDID(String methodName, String key) { + return DIDKit.keyToDID(methodName, key); + } + + @override + Future keyToVerificationMethod(String methodName, String key) async { + return DIDKit.keyToVerificationMethod(methodName, key); + } + + @override + Future issueCredential( + String credential, + String options, + String key, + ) async { + return DIDKit.issueCredential(credential, options, key); + } + + @override + Future verifyCredential( + String credential, + String options, + ) async { + return DIDKit.verifyCredential(credential, options); + } + + @override + Future issuePresentation( + String presentation, + String options, + String key, + ) async { + return DIDKit.issuePresentation(presentation, options, key); + } + + @override + Future verifyPresentation( + String presentation, + String options, + ) async { + return DIDKit.verifyPresentation(presentation, options); + } + + @override + Future resolveDID( + String did, + String inputMetadata, + ) async { + return DIDKit.resolveDID(did, inputMetadata); + } + + @override + Future dereferenceDIDURL( + String didUrl, + String inputMetadata, + ) async { + return DIDKit.dereferenceDIDURL(didUrl, inputMetadata); + } + + @override + Future didAuth( + String did, + String options, + String key, + ) async { + return DIDKit.DIDAuth(did, options, key); + } +} diff --git a/packages/did_kit/test/src/did_kit_test.dart b/packages/did_kit/test/src/did_kit_provider_test.dart similarity index 55% rename from packages/did_kit/test/src/did_kit_test.dart rename to packages/did_kit/test/src/did_kit_provider_test.dart index 4640c86b0..02510b29c 100644 --- a/packages/did_kit/test/src/did_kit_test.dart +++ b/packages/did_kit/test/src/did_kit_provider_test.dart @@ -2,8 +2,11 @@ import 'dart:convert'; import 'package:did_kit/did_kit.dart'; import 'package:did_kit/src/did_kit_provider.dart'; -import 'package:didkit/didkit.dart'; +import 'package:did_kit/src/didkit_interface.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; + +class MockDIDKitInterface extends Mock implements DIDKitInterface {} void main() { const didKitVersion = '0.3.0'; @@ -55,55 +58,53 @@ void main() { 'challenge': 'Uuid().v4()', }); + late DIDKitInterface mockDIDKit; late DIDKitProvider didKitProvider; - setUpAll(() { - didKitProvider = DIDKitProvider(); + setUp(() { + mockDIDKit = MockDIDKitInterface(); + didKitProvider = DIDKitProvider(didKit: mockDIDKit); }); group('DidKitProvider', () { test('verify did kit version is $didKitVersion', () { + when(() => mockDIDKit.getVersion()).thenReturn(didKitVersion); expect(didKitProvider.getVersion(), didKitVersion); + verify(() => mockDIDKit.getVersion()).called(1); }); - test('exceptions with empty inputs', () async { - expect( - () => didKitProvider.issueCredential('', '', ''), - throwsA(isInstanceOf()), - ); - expect( - () => didKitProvider.issuePresentation('', '', ''), - throwsA(isInstanceOf()), - ); - expect( - () => didKitProvider.verifyCredential('', ''), - throwsA(isInstanceOf()), - ); - expect( - () => didKitProvider.verifyPresentation('', ''), - throwsA(isInstanceOf()), - ); - }); - - test('generateEd25519Key method mocked', () { + test('generateEd25519Key returns correct key', () { + when(() => mockDIDKit.generateEd25519Key()).thenReturn(ed25519Key); expect(didKitProvider.generateEd25519Key(), equals(ed25519Key)); + verify(() => mockDIDKit.generateEd25519Key()).called(1); }); - test('keyToDID method mocked', () async { + test('keyToDID returns correct DID', () { + when(() => mockDIDKit.keyToDID(any(), any())).thenReturn(did); + expect( didKitProvider.keyToDID(key, ed25519Key), equals(did), ); + verify(() => mockDIDKit.keyToDID(key, ed25519Key)).called(1); }); - test('keyToVerificationMethod method mocked', () async { + test('keyToVerificationMethod returns correct verification method', + () async { + when(() => mockDIDKit.keyToVerificationMethod(any(), any())) + .thenAnswer((_) async => vm); + expect( await didKitProvider.keyToVerificationMethod(key, ed25519Key), equals(vm), ); + verify(() => mockDIDKit.keyToVerificationMethod(key, ed25519Key)) + .called(1); }); - test('issueCredential method mocked', () async { + test('issueCredential returns correct vc', () async { + when(() => mockDIDKit.issueCredential(any(), any(), any())) + .thenAnswer((_) async => vc); expect( await didKitProvider.issueCredential( jsonEncode(credential), @@ -112,16 +113,30 @@ void main() { ), equals(vc), ); + verify( + () => mockDIDKit.issueCredential( + jsonEncode(credential), + jsonEncode(options), + key, + ), + ).called(1); }); - test('verifyCredential method mocked', () async { + test('verifyCredential returns correct verifyResult', () async { + when(() => mockDIDKit.verifyCredential(any(), any())) + .thenAnswer((_) async => verifyResult); expect( await didKitProvider.verifyCredential(vc, jsonEncode(verifyOptions)), equals(verifyResult), ); + verify( + () => mockDIDKit.verifyCredential(vc, jsonEncode(verifyOptions)), + ).called(1); }); - test('issuePresentation method mocked', () async { + test('issuePresentation returns correct vc', () async { + when(() => mockDIDKit.issuePresentation(any(), any(), any())) + .thenAnswer((_) async => vc); expect( await didKitProvider.issuePresentation( jsonEncode(presentation), @@ -130,9 +145,18 @@ void main() { ), equals(vc), ); + verify( + () => mockDIDKit.issuePresentation( + jsonEncode(presentation), + jsonEncode(options), + key, + ), + ).called(1); }); - test('verifyPresentation method mocked', () async { + test('verifyPresentation returns correct verifyResult', () async { + when(() => mockDIDKit.verifyPresentation(any(), any())) + .thenAnswer((_) async => verifyResult); expect( await didKitProvider.verifyPresentation( vc, @@ -140,27 +164,48 @@ void main() { ), equals(verifyResult), ); + verify( + () => mockDIDKit.verifyPresentation( + vc, + jsonEncode(verifyOptions), + ), + ).called(1); }); test('resolveDID method mocked', () async { + when(() => mockDIDKit.resolveDID(any(), any())) + .thenAnswer((_) async => ''); expect( await didKitProvider.resolveDID(did, '{}'), isInstanceOf(), ); + verify( + () => mockDIDKit.resolveDID(did, '{}'), + ).called(1); }); test('dereferenceDIDURL method mocked', () async { + when(() => mockDIDKit.dereferenceDIDURL(any(), any())) + .thenAnswer((_) async => ''); expect( await didKitProvider.dereferenceDIDURL(vm, '{}'), isInstanceOf(), ); + verify( + () => mockDIDKit.dereferenceDIDURL(vm, '{}'), + ).called(1); }); test('didAuth method mocked', () async { + when(() => mockDIDKit.didAuth(any(), any(), any())) + .thenAnswer((_) async => ''); expect( await didKitProvider.didAuth(did, proofOptions, key), isInstanceOf(), ); + verify( + () => mockDIDKit.didAuth(did, proofOptions, key), + ).called(1); }); }); } diff --git a/packages/jwt_decode/test/src/jwt_decode_test.dart b/packages/jwt_decode/test/src/jwt_decode_test.dart index 6d067404b..74c066e10 100644 --- a/packages/jwt_decode/test/src/jwt_decode_test.dart +++ b/packages/jwt_decode/test/src/jwt_decode_test.dart @@ -63,7 +63,7 @@ void main() { const validJwtTokenWithInvalidPayload = '''eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.W3siSGVsbG8iOjEyfV0.vfPVZG-CndvivC2JaWA4leWl_1F3pm1lr-l3BISqVAc'''; const jsonStringOfValidTokenOne = - r'''{scope: openid, response_type: id_token, client_id: did:web:altme.co, redirect_uri: https://altme.co/gaiax/login_redirect/287f58e9-a50c-11ec-bea0-0a1628958560, response_mode: post, claims: {"id_token":{},"vp_token":{"presentation_definition":{"id":"pass_for_gaiax","input_descriptors":[{"id":"GaiaxPass issued by altme","purpose":"Test for Gaia-X hackathon","format":{"ldp_vc":{"proof_type":["JsonWebSignature2020"]}},"constraints":{"limit_disclosure":"required","fields":[{"path":["$.credentialSubject.type"],"purpose":"One can only accept GaiaxPass","filter":{"type":"string","pattern":"GaiaxPass"}},{"path":["$.issuer"],"purpose":"One can accept only GaiaxPass signed by altme","filter":{"type":"string","pattern":"did:web:altme.co"}}]}}]}}}, nonce: 6j0RATZeIj, registration: {"id_token_signing_alg_values_supported":["RS256","ES256","ES256K","EdDSA"],"subject_syntax_types_supported":["did:web","did:tz","did:key","did:ion","did:pkh","did:ethr"]}, request_uri: https://altme.co/gaiax/login_request_uri/287f58e9-a50c-11ec-bea0-0a1628958560}'''; + r'''{scope: openid, response_type: id_token, client_id: did:web:talao.co, redirect_uri: https://talao.co/gaiax/login_redirect/287f58e9-a50c-11ec-bea0-0a1628958560, response_mode: post, claims: {"id_token":{},"vp_token":{"presentation_definition":{"id":"pass_for_gaiax","input_descriptors":[{"id":"GaiaxPass issued by Talao","purpose":"Test for Gaia-X hackathon","format":{"ldp_vc":{"proof_type":["JsonWebSignature2020"]}},"constraints":{"limit_disclosure":"required","fields":[{"path":["$.credentialSubject.type"],"purpose":"One can only accept GaiaxPass","filter":{"type":"string","pattern":"GaiaxPass"}},{"path":["$.issuer"],"purpose":"One can accept only GaiaxPass signed by Talao","filter":{"type":"string","pattern":"did:web:talao.co"}}]}}]}}}, nonce: 6j0RATZeIj, registration: {"id_token_signing_alg_values_supported":["RS256","ES256","ES256K","EdDSA"],"subject_syntax_types_supported":["did:web","did:tz","did:key","did:ion","did:pkh","did:ethr"]}, request_uri: https://talao.co/gaiax/login_request_uri/287f58e9-a50c-11ec-bea0-0a1628958560}'''; const inValidJwtTokenWithLessThanThreePart = 'partOne.partTwo'; const inValidJwtTokenWithThreePart = 'partOne.partTwo.partThree'; @@ -96,7 +96,7 @@ void main() { isA().having( (p0) => p0.toString(), 'toString()', - 'Exception: Invalid Token', + 'Exception: INVALID_TOKEN', ), ), ); @@ -118,10 +118,36 @@ void main() { isA().having( (p0) => p0.toString(), 'toString()', - 'Exception: Invalid Payload', + 'Exception: INVALID_PAYLOAD', ), ), ); }); + + test('parseJwtHeader throws exception for invalid token', () { + expect( + () => jwtDecode.parseJwtHeader('invalid.token'), + throwsA( + isA().having( + (p0) => p0.toString(), + 'toString()', + 'Exception: INVALID_TOKEN', + ), + ), + ); + }); + + test('parseJwtHeader returns header for valid token', () { + final header = jwtDecode.parseJwtHeader(validJwtToken); + expect(header['alg'], 'RS256'); + expect(header['typ'], 'JWT'); + }); + + test('parsePolygonIdJwtHeader throws exception for invalid token', () { + expect( + () => jwtDecode.parsePolygonIdJwtHeader('invalid.token'), + throwsException, + ); + }); }); } diff --git a/packages/key_generator/lib/src/key_generator.dart b/packages/key_generator/lib/src/key_generator.dart index 46f10a4ec..918c476c3 100644 --- a/packages/key_generator/lib/src/key_generator.dart +++ b/packages/key_generator/lib/src/key_generator.dart @@ -1,6 +1,5 @@ import 'dart:convert'; import 'dart:typed_data'; - import 'package:bip32/bip32.dart' as bip32; import 'package:bip39/bip39.dart' as bip393; import 'package:dart_bip32_bip44/dart_bip32_bip44.dart'; diff --git a/packages/key_generator/pubspec.yaml b/packages/key_generator/pubspec.yaml index 8feaaa36e..819da38bd 100644 --- a/packages/key_generator/pubspec.yaml +++ b/packages/key_generator/pubspec.yaml @@ -13,17 +13,15 @@ dependencies: dart_web3: ^0.0.3 ed25519_hd_key: ^2.2.0 hex: ^0.2.0 + pinenacl: ^0.3.3 secp256k1: ^0.3.0 tezart: git: url: https://github.com/autonomy-system/tezart.git ref: e53e4ab9eaabea53cbf70e814efd2245b4659f48 -dependency_overrides: - pinenacl: ^0.5.1 # tezart from git depends on pinenacl ^0.3.3 - dev_dependencies: - coverage: ^1.6.3 - mocktail: ^0.3.0 - test: ^1.24.3 + flutter_test: + sdk: flutter + mocktail: ^1.0.2 very_good_analysis: ^5.0.0+1 diff --git a/packages/key_generator/test/src/key_generator_test.dart b/packages/key_generator/test/src/key_generator_test.dart index ec12b309a..81f83783d 100644 --- a/packages/key_generator/test/src/key_generator_test.dart +++ b/packages/key_generator/test/src/key_generator_test.dart @@ -1,5 +1,5 @@ +import 'package:flutter_test/flutter_test.dart'; import 'package:key_generator/key_generator.dart'; -import 'package:test/test.dart'; void main() { const mnemonics = @@ -138,50 +138,63 @@ void main() { }); }); - group('ethereum', () { - const accountType = AccountType.ethereum; - test('throw Exception for secretKey from ethereum', () async { - final key = await keyGenerator.jwkFromMnemonic( - mnemonic: mnemonics, - accountType: accountType, - derivePathIndex: derivePathIndex, - ); - expect(key, evmJwkKey); - }); + group('EVM', () { + const accountTypes = [ + AccountType.ethereum, + AccountType.fantom, + AccountType.polygon, + AccountType.binance, + ]; + + for (final accountType in accountTypes) { + test('throw Exception for secretKey from ethereum', () async { + final key = await keyGenerator.jwkFromMnemonic( + mnemonic: mnemonics, + accountType: accountType, + derivePathIndex: derivePathIndex, + ); + expect(key, evmJwkKey); + }); - test('key from secretKey for ethereum', () async { - final key = await keyGenerator.jwkFromSecretKey( - secretKey: ethereumSecretKey, - accountType: accountType, - ); - expect(key, evmJwkKey); - }); + test('key from secretKey for ethereum', () async { + final key = await keyGenerator.jwkFromSecretKey( + secretKey: ethereumSecretKey, + accountType: accountType, + ); + expect(key, evmJwkKey); + }); - test('0x wallet address from mnemonics for ethereum', () async { - final walletAddress = await keyGenerator.walletAddressFromMnemonic( - mnemonic: mnemonics, - accountType: accountType, - derivePathIndex: derivePathIndex, - ); - expect(walletAddress, ethereumWalletAddress); - }); + test('0x wallet address from mnemonics for ethereum', () async { + final walletAddress = await keyGenerator.walletAddressFromMnemonic( + mnemonic: mnemonics, + accountType: accountType, + derivePathIndex: derivePathIndex, + ); + expect(walletAddress, ethereumWalletAddress); + }); - test('secretKey from mnemonics for ethereum ', () async { - final secretKey = await keyGenerator.secretKeyFromMnemonic( - mnemonic: mnemonics, - accountType: accountType, - derivePathIndex: derivePathIndex, - ); - expect(secretKey, ethereumSecretKey); - }); + test('secretKey from mnemonics for ethereum ', () async { + final secretKey = await keyGenerator.secretKeyFromMnemonic( + mnemonic: mnemonics, + accountType: accountType, + derivePathIndex: derivePathIndex, + ); + expect(secretKey, ethereumSecretKey); + }); - test('0x wallet address from secret key for ethereum', () async { - final walletAddress = await keyGenerator.walletAddressFromSecretKey( - secretKey: ethereumSecretKey, - accountType: accountType, - ); - expect(walletAddress, ethereumWalletAddress); - }); + test('0x wallet address from secret key for ethereum', () async { + final walletAddress = await keyGenerator.walletAddressFromSecretKey( + secretKey: ethereumSecretKey, + accountType: accountType, + ); + expect(walletAddress, ethereumWalletAddress); + }); + } + }); + + test('getKeystore returns correct value', () { + final data = keyGenerator.getKeystore(secretKey: tezosSecretKey); + expect(data.address, tezosWalletAddress); }); }); } diff --git a/packages/oidc4vc/analysis_options.yaml b/packages/oidc4vc/analysis_options.yaml index a608bb333..2239654e7 100644 --- a/packages/oidc4vc/analysis_options.yaml +++ b/packages/oidc4vc/analysis_options.yaml @@ -1,4 +1,8 @@ include: package:very_good_analysis/analysis_options.yaml +analyzer: + exclude: + - "**/*.g.dart" + linter: rules: public_member_api_docs: false diff --git a/packages/oidc4vc/lib/src/models/openid_configuration.dart b/packages/oidc4vc/lib/src/models/openid_configuration.dart index db21197c2..6cb570fcb 100644 --- a/packages/oidc4vc/lib/src/models/openid_configuration.dart +++ b/packages/oidc4vc/lib/src/models/openid_configuration.dart @@ -9,6 +9,7 @@ class OpenIdConfiguration extends Equatable { const OpenIdConfiguration({ required this.requirePushedAuthorizationRequests, this.authorizationServer, + this.authorizationServers, this.credentialsSupported, this.credentialConfigurationsSupported, this.credentialEndpoint, @@ -24,7 +25,6 @@ class OpenIdConfiguration extends Equatable { this.credentialManifests, this.issuer, this.jwksUri, - required this.requirePushedAuthorizationRequests, this.grantTypesSupported, }); @@ -33,6 +33,8 @@ class OpenIdConfiguration extends Equatable { @JsonKey(name: 'authorization_server') final String? authorizationServer; + @JsonKey(name: 'authorization_servers') + final List? authorizationServers; @JsonKey(name: 'credential_endpoint') final String? credentialEndpoint; @JsonKey(name: 'credential_issuer') @@ -72,6 +74,7 @@ class OpenIdConfiguration extends Equatable { @override List get props => [ authorizationServer, + authorizationServers, credentialEndpoint, credentialIssuer, subjectSyntaxTypesSupported, diff --git a/packages/oidc4vc/lib/src/oidc4vc.dart b/packages/oidc4vc/lib/src/oidc4vc.dart index c28d82ee2..db203d910 100644 --- a/packages/oidc4vc/lib/src/oidc4vc.dart +++ b/packages/oidc4vc/lib/src/oidc4vc.dart @@ -28,8 +28,6 @@ class OIDC4VC { /// {@macro ebsi} OIDC4VC(); - final Dio dio = Dio(); - /// create JWK from mnemonic String privateKeyFromMnemonic({ required String mnemonic, @@ -150,17 +148,25 @@ class OIDC4VC { required VCFormatType vcFormatType, required String? clientAssertion, required bool secureAuthorizedFlow, + required Dio dio, + required dynamic credentialOfferJson, + SecureStorageProvider? secureStorage, }) async { try { final openIdConfiguration = await getOpenIdConfig( baseUrl: issuer, isAuthorizationServer: false, + dio: dio, + secureStorage: secureStorage, ); - final authorizationEndpoint = await readAuthorizationEndPoint( + final credentialAuthorizationEndpoint = await readAuthorizationEndPoint( openIdConfiguration: openIdConfiguration, issuer: issuer, oidc4vciDraftType: oidc4vciDraftType, + dio: dio, + credentialOfferJson: credentialOfferJson, + secureStorage: secureStorage, ); final authorizationRequestParemeters = getAuthorizationRequestParemeters( @@ -184,7 +190,7 @@ class OIDC4VC { ); return ( - authorizationEndpoint, + credentialAuthorizationEndpoint, authorizationRequestParemeters, openIdConfiguration, ); @@ -439,6 +445,7 @@ class OIDC4VC { required OpenIdConfiguration openIdConfiguration, required String accessToken, required String? cnonce, + required Dio dio, List? authorizationDetails, }) async { var nonce = cnonce; @@ -511,6 +518,7 @@ class OIDC4VC { privateKey: privateKey, accessToken: accessToken, nonce: nonce, + dio: dio, ); /// update nonce value @@ -543,6 +551,7 @@ class OIDC4VC { privateKey: privateKey, accessToken: accessToken, nonce: cnonce, + dio: dio, ); credentialResponseData.add(credentialResponseDataValue); @@ -564,6 +573,7 @@ class OIDC4VC { required OIDC4VCIDraftType oidc4vciDraftType, required String redirectUri, required OpenIdConfiguration openIdConfiguration, + required Dio dio, String? preAuthorizedCode, String? userPin, String? code, @@ -575,6 +585,7 @@ class OIDC4VC { openIdConfiguration: openIdConfiguration, issuer: issuer, oidc4vciDraftType: oidc4vciDraftType, + dio: dio, ); Map? tokenResponse; @@ -598,6 +609,7 @@ class OIDC4VC { tokenEndPoint: tokenEndPoint, tokenData: tokenData, authorization: authorization, + dio: dio, ); if (tokenResponse.containsKey('c_nonce')) { @@ -630,6 +642,7 @@ class OIDC4VC { required String privateKey, required String accessToken, required String? nonce, + required Dio dio, }) async { final credentialData = await buildCredentialData( nonce: nonce, @@ -676,6 +689,7 @@ class OIDC4VC { required Map credentialHeaders, required Map? body, required String deferredCredentialEndpoint, + required Dio dio, }) async { final dynamic credentialResponse = await dio.post( deferredCredentialEndpoint, @@ -741,6 +755,8 @@ class OIDC4VC { required String didKey, required bool fromStatusList, required bool isCachingEnabled, + required Dio dio, + SecureStorageProvider? secureStorage, }) async { try { if (isURL(didKey)) { @@ -757,6 +773,8 @@ class OIDC4VC { baseUrl: didKey, isAuthorizationServer: isAuthorizationServer, isCachingEnabled: isCachingEnabled, + dio: dio, + secureStorage: secureStorage, ); final authorizationServer = openIdConfiguration.authorizationServer; @@ -766,6 +784,8 @@ class OIDC4VC { baseUrl: authorizationServer, isAuthorizationServer: true, isCachingEnabled: isCachingEnabled, + dio: dio, + secureStorage: secureStorage, ); } @@ -776,6 +796,8 @@ class OIDC4VC { final response = await dioGet( openIdConfiguration.jwksUri!, isCachingEnabled: isCachingEnabled, + dio: dio, + secureStorage: secureStorage, ); return response as Map; @@ -810,6 +832,8 @@ class OIDC4VC { required OpenIdConfiguration openIdConfiguration, required String issuer, required OIDC4VCIDraftType oidc4vciDraftType, + required Dio dio, + SecureStorageProvider? secureStorage, }) async { var tokenEndPoint = '$issuer/token'; @@ -822,6 +846,8 @@ class OIDC4VC { final authorizationServerConfiguration = await getOpenIdConfig( baseUrl: authorizationServer, isAuthorizationServer: true, + dio: dio, + secureStorage: secureStorage, ); if (authorizationServerConfiguration.tokenEndpoint != null) { @@ -836,26 +862,78 @@ class OIDC4VC { required OpenIdConfiguration openIdConfiguration, required String issuer, required OIDC4VCIDraftType oidc4vciDraftType, + required Dio dio, + required dynamic credentialOfferJson, + SecureStorageProvider? secureStorage, }) async { - var authorizationEndpoint = '$issuer/authorize'; + String? authorizationEndpoint; - if (openIdConfiguration.authorizationEndpoint != null) { - authorizationEndpoint = openIdConfiguration.authorizationEndpoint!; - } else { - final authorizationServer = - openIdConfiguration.authorizationServer ?? issuer; + switch (oidc4vciDraftType) { + case OIDC4VCIDraftType.draft11: + if (openIdConfiguration.authorizationEndpoint != null) { + authorizationEndpoint = openIdConfiguration.authorizationEndpoint; + } else { + final authorizationServer = + openIdConfiguration.authorizationServer ?? issuer; - final authorizationServerConfiguration = await getOpenIdConfig( - baseUrl: authorizationServer, - isAuthorizationServer: true, - ); + final authorizationServerConfiguration = await getOpenIdConfig( + baseUrl: authorizationServer, + isAuthorizationServer: true, + dio: dio, + secureStorage: secureStorage, + ); - if (authorizationServerConfiguration.authorizationEndpoint != null) { - authorizationEndpoint = - authorizationServerConfiguration.authorizationEndpoint!; - } + if (authorizationServerConfiguration.authorizationEndpoint != null) { + authorizationEndpoint = + authorizationServerConfiguration.authorizationEndpoint; + } + } + case OIDC4VCIDraftType.draft13: + + /// Extract the authorization endpoint from from first element of + /// authorization_servers in opentIdConfiguration.authorizationServers + final listOpenIDConfiguration = + openIdConfiguration.authorizationServers ?? []; + if (listOpenIDConfiguration.isNotEmpty) { + if (listOpenIDConfiguration.length == 1) { + authorizationEndpoint = + '${listOpenIDConfiguration.first}/authorize'; + } else { + /// Extract the authorization endpoint from from + /// authorization_server in credentialOfferJson + final jsonPathCredentialOffer = JsonPath( + // ignore: lines_longer_than_80_chars + r'$..urn:ietf:params:oauth:grant-type:pre-authorized_code.authorization_server', + ); + final data = jsonPathCredentialOffer + .read(credentialOfferJson) + .first + .value as List; + if (data.isNotEmpty && + listOpenIDConfiguration.contains(data.first)) { + authorizationEndpoint = '${data.first}/authorize'; + } + if (authorizationEndpoint == null) { + final jsonPathCredentialOffer = JsonPath( + // ignore: lines_longer_than_80_chars + r'$..authorization_code.authorization_server', + ); + final data = jsonPathCredentialOffer + .read(credentialOfferJson) + .first + .value as List; + if (data.isNotEmpty && + listOpenIDConfiguration.contains(data.first)) { + authorizationEndpoint = '${data.first}/authorize'; + } + } + } + } } - return authorizationEndpoint; + // If authorizationEndpoint is null, we consider the issuer + // as the authorizationEndpoint + + return authorizationEndpoint ??= '$issuer/authorize'; } String readIssuerDid( @@ -1144,6 +1222,7 @@ class OIDC4VC { required Map? publicJwk, required bool fromStatusList, required bool isCachingEnabled, + required Dio dio, }) async { try { Map? publicKeyJwk; @@ -1155,6 +1234,7 @@ class OIDC4VC { didKey: issuer, fromStatusList: fromStatusList, isCachingEnabled: isCachingEnabled, + dio: dio, ); publicKeyJwk = readPublicKeyJwk( @@ -1171,7 +1251,6 @@ class OIDC4VC { } late final bool isVerified; - if (kty == 'OKP') { isVerified = verifyTokenEdDSA( publicKey: publicKeyJwk, @@ -1292,6 +1371,7 @@ class OIDC4VC { required String tokenEndPoint, required Map tokenData, required String? authorization, + required Dio dio, }) async { /// getting token final tokenHeaders = { @@ -1386,6 +1466,7 @@ class OIDC4VC { required String? stateValue, required ClientType clientType, required ProofHeaderType proofHeaderType, + required Dio dio, }) async { try { final private = jsonDecode(privateKey) as Map; @@ -1616,13 +1697,15 @@ class OIDC4VC { Future getOpenIdConfig({ required String baseUrl, required bool isAuthorizationServer, + required Dio dio, bool isCachingEnabled = false, + SecureStorageProvider? secureStorage, }) async { ///for OIDC4VCI, the server is an issuer the metadata are all in th ////openid-issuer-configuration or some are in the /openid-configuration ///(token endpoint etc,) and other are in the /openid-credential-issuer ///(credential supported) for OIDC4VP and SIOPV2, the serve is a client, - ///the wallet is the suthorization server the verifier metadata are in + ///the wallet is the authorization server the verifier metadata are in ////openid-configuration final url = '$baseUrl/.well-known/openid-configuration'; @@ -1631,6 +1714,8 @@ class OIDC4VC { final data = await getOpenIdConfigSecondMethod( baseUrl, isCachingEnabled: isCachingEnabled, + dio: dio, + secureStorage: secureStorage, ); return data; } @@ -1639,6 +1724,8 @@ class OIDC4VC { final response = await dioGet( url, isCachingEnabled: isCachingEnabled, + dio: dio, + secureStorage: secureStorage, ); final data = response is String ? jsonDecode(response) as Map @@ -1649,6 +1736,7 @@ class OIDC4VC { final data = await getOpenIdConfigSecondMethod( baseUrl, isCachingEnabled: isCachingEnabled, + dio: dio, ); return data; } @@ -1657,6 +1745,8 @@ class OIDC4VC { Future getOpenIdConfigSecondMethod( String baseUrl, { required bool isCachingEnabled, + required Dio dio, + SecureStorageProvider? secureStorage, }) async { final url = '$baseUrl/.well-known/openid-credential-issuer'; @@ -1664,10 +1754,13 @@ class OIDC4VC { final response = await dioGet( url, isCachingEnabled: isCachingEnabled, + dio: dio, + secureStorage: secureStorage, ); final data = response is String ? jsonDecode(response) as Map : response as Map; + return OpenIdConfiguration.fromJson(data); } catch (e) { throw Exception('OPENID-CONFIGURATION-ISSUE'); @@ -1732,14 +1825,16 @@ class OIDC4VC { Future dioGet( String uri, { + required Dio dio, Map headers = const { 'Content-Type': 'application/json; charset=UTF-8', }, bool isCachingEnabled = false, + SecureStorageProvider? secureStorage, }) async { try { - final secureStorageProvider = getSecureStorage; - final cachedData = await secureStorageProvider.get(uri); + final secureStorageProvider = secureStorage ?? getSecureStorage; + // final cachedData = await secureStorageProvider.get(uri); // TODO(hawkbee): To be removed. /// temporary solution to purge faulty stored data /// Will be removed in the future @@ -1750,23 +1845,27 @@ class OIDC4VC { dio.options.headers = headers; - if (!isCachingEnabled || cachedData == null) { - response = await dio.get(uri); - } else { - final cachedDataJson = jsonDecode(cachedData); - final expiry = int.parse(cachedDataJson['expiry'].toString()); - - final isExpired = DateTime.now().millisecondsSinceEpoch > expiry; - - if (isExpired) { - response = await dio.get(uri); - } else { - /// directly return cached data - /// returned here to avoid the caching override everytime - final response = await cachedDataJson['data']; - return response; - } - } + // if (isCachingEnabled) { + // final secureStorageProvider = getSecureStorage; + // final cachedData = await secureStorageProvider.get(uri); + // if (cachedData == null) { + // response = await dio.get(uri); + // } else { + // final cachedDataJson = jsonDecode(cachedData); + // final expiry = int.parse(cachedDataJson['expiry'].toString()); + + // final isExpired = DateTime.now().millisecondsSinceEpoch > expiry; + + // if (isExpired) { + // response = await dio.get(uri); + // } else { + // /// directly return cached data + // /// returned here to avoid the caching override everytime + // final response = await cachedDataJson['data']; + // return response; + // } + // } + // } // temporary deactiviting this caching du to issue with // flutter_secure_storage on ios #2657 // final expiry = @@ -1774,9 +1873,10 @@ class OIDC4VC { // final value = {'expiry': expiry, 'data': response.data}; // await secureStorageProvider.set(uri, jsonEncode(value)); + response = await dio.get(uri); return response.data; - } on FormatException catch (_) { + } on FormatException { throw Exception(); } catch (e) { if (e is DioException) { diff --git a/packages/oidc4vc/test/src/client_authentication_test.dart b/packages/oidc4vc/test/src/client_authentication_test.dart new file mode 100644 index 000000000..f3bf8bff2 --- /dev/null +++ b/packages/oidc4vc/test/src/client_authentication_test.dart @@ -0,0 +1,29 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:oidc4vc/oidc4vc.dart'; + +void main() { + group('ClientAuthenticationX', () { + test('value', () { + expect( + ClientAuthentication.none.value, + 'none', + ); + expect( + ClientAuthentication.clientSecretBasic.value, + 'client_secret_basic', + ); + expect( + ClientAuthentication.clientSecretPost.value, + 'client_secret_post', + ); + expect( + ClientAuthentication.clientId.value, + 'client_id', + ); + expect( + ClientAuthentication.clientSecretJwt.value, + 'client_secret_jwt', + ); + }); + }); +} diff --git a/packages/oidc4vc/test/src/client_type_test.dart b/packages/oidc4vc/test/src/client_type_test.dart new file mode 100644 index 000000000..dd5de9522 --- /dev/null +++ b/packages/oidc4vc/test/src/client_type_test.dart @@ -0,0 +1,12 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:oidc4vc/oidc4vc.dart'; + +void main() { + group('ClientTypeX', () { + test('getTitle', () { + expect(ClientType.p256JWKThumprint.getTitle, 'P-256 JWK Thumbprint'); + expect(ClientType.did.getTitle, 'DID'); + expect(ClientType.confidential.getTitle, 'Confidential Client'); + }); + }); +} diff --git a/packages/oidc4vc/test/src/const_values.dart b/packages/oidc4vc/test/src/const_values.dart index 3169cbc83..1eb369812 100644 --- a/packages/oidc4vc/test/src/const_values.dart +++ b/packages/oidc4vc/test/src/const_values.dart @@ -30,10 +30,12 @@ const keyWithAlg = { 'alg': 'HS256', }; -const didKey = 'did:ebsi:zo9FR1YfAKFP3Q6dvqhxcXxnfeDiJDP97kmnqhyAUSACj'; +const issuer = 'https://talao.co/issuer/zxhaokccsi'; -const kid = - '''did:ebsi:zo9FR1YfAKFP3Q6dvqhxcXxnfeDiJDP97kmnqhyAUSACj#Cgcg1y9xj9uWFw56PMc29XBd9EReixzvnftBz8JwQFiB'''; +const clientId = + '''did:key:z2dmzD81cgPx8Vki7JbuuMmFYrWPgYoytykUZ3eyqht1j9Kbrbpg5is8LfTLuQ1RsW5r7s7ZjbDDFbDgy1tLrdc7Bj3itBGQkuGUQyfzKhFqbUNW2PqJPMSSzWoF2DGSvDSijCtJtYCSRsjSVLrwu5oHNbnPFvSEC4iRZPpU6B6nExRBTa'''; + +const kid = ''; const ES256Alg = 'ES256'; @@ -41,40 +43,7 @@ const ES256KAlg = 'ES256K'; const HS256Alg = 'HS256'; -const thumbprint = [ - 173, - 150, - 139, - 1, - 202, - 186, - 32, - 132, - 51, - 6, - 216, - 230, - 103, - 154, - 26, - 196, - 52, - 23, - 248, - 132, - 91, - 7, - 58, - 174, - 149, - 38, - 148, - 157, - 199, - 122, - 118, - 36, -]; +const thumbprint = 'rZaLAcq6IIQzBtjmZ5oaxDQX+IRbBzqulSaUncd6diQ'; const rfc7638Jwk = { 'e': 'AQAB', @@ -83,37 +52,5 @@ const rfc7638Jwk = { // ignore: lines_longer_than_80_chars '0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw', }; -const expectedThumbprintForrfc7638Jwk = [ - 55, - 54, - 203, - 177, - 120, - 124, - 184, - 48, - 156, - 119, - 238, - 140, - 55, - 5, - 197, - 225, - 111, - 251, - 158, - 133, - 151, - 21, - 144, - 31, - 30, - 76, - 89, - 177, - 17, - 130, - 245, - 123, -]; +const expectedThumbprintForrfc7638Jwk = + 'NzbLsXh8uDCcd+6MNwXF4W/7noWXFZAfHkxZsRGC9Xs'; diff --git a/packages/oidc4vc/test/src/helper_function_test.dart b/packages/oidc4vc/test/src/helper_function_test.dart new file mode 100644 index 000000000..f6fbdd488 --- /dev/null +++ b/packages/oidc4vc/test/src/helper_function_test.dart @@ -0,0 +1,21 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:oidc4vc/src/helper_function.dart'; + +void main() { + group('listToString', () { + test('converts list of strings to a single string with spaces', () { + final inputList = ['This', 'is', 'Bibash']; + expect(listToString(inputList), 'This is Bibash'); + }); + + test('converts list of numbers to a single string with spaces', () { + final inputList = [1, 2, 3, 4]; + expect(listToString(inputList), '1 2 3 4'); + }); + + test('returns empty string for empty input list', () { + final inputList = []; + expect(listToString(inputList), ''); + }); + }); +} diff --git a/packages/oidc4vc/test/src/issuer_token_parameters/issuer_token_parameters_class.dart b/packages/oidc4vc/test/src/issuer_token_parameters/issuer_token_parameters_class.dart index 5710dff77..bc4f2cbb5 100644 --- a/packages/oidc4vc/test/src/issuer_token_parameters/issuer_token_parameters_class.dart +++ b/packages/oidc4vc/test/src/issuer_token_parameters/issuer_token_parameters_class.dart @@ -1,41 +1,66 @@ -// import 'package:flutter_test/flutter_test.dart'; -// import 'package:oidc4vc/oidc4vc.dart'; - -// import '../const_values.dart'; -// import '../token_parameters/token_parameters_class.dart'; - -// class IssuerTokenParameterTest extends TokenParameterTest { -// final issuerTokenParameters = IssuerTokenParameters(privateKey, '', '', ''); - -// @override -// void publicKeyTest() { -// expect(issuerTokenParameters.publicJWK, publicJWK); -// } - -// @override -// void didTest() { -// expect(tokenParameters.did, didKey); -// } - -// @override -// void keyIdTest() { -// expect(tokenParameters.kid, kid); -// } - -// @override -// void algorithmIsES256KTest() { -// expect(tokenParameters.alg, ES256KAlg); -// } - -// @override -// void algorithmIsES256Test() { -// final tokenParameters = IssuerTokenParameters(privateKey2, '', '', ''); -// expect(tokenParameters.alg, ES256Alg); -// } - -// @override -// void algorithmIsNotNullTest() { -// final tokenParameters = IssuerTokenParameters(keyWithAlg, '', '', ''); -// expect(tokenParameters.alg, HS256Alg); -// } -// } +import 'package:flutter_test/flutter_test.dart'; +import 'package:oidc4vc/oidc4vc.dart'; + +import '../const_values.dart'; +import '../token_parameters/token_parameters_class.dart'; + +class IssuerTokenParameterTest extends TokenParameterTest { + final issuerTokenParameters = IssuerTokenParameters( + privateKey: privateKey, + clientId: clientId, + did: clientId, + clientType: ClientType.did, + mediaType: MediaType.proofOfOwnership, + proofHeaderType: ProofHeaderType.kid, + issuer: issuer, + kid: '', + ); + + @override + void publicKeyTest() { + expect(issuerTokenParameters.publicJWK, publicJWK); + } + + @override + void didTest() { + expect(tokenParameters.did, clientId); + } + + @override + void keyIdTest() { + expect(tokenParameters.kid, kid); + } + + @override + void algorithmIsES256KTest() { + expect(tokenParameters.alg, ES256KAlg); + } + + @override + void algorithmIsES256Test() { + final tokenParameters = IssuerTokenParameters( + privateKey: privateKey2, + clientId: clientId, + did: clientId, + clientType: ClientType.did, + mediaType: MediaType.proofOfOwnership, + proofHeaderType: ProofHeaderType.kid, + issuer: issuer, + ); + expect(tokenParameters.alg, ES256Alg); + } + + @override + void algorithmIsNotNullTest() { + final tokenParameters = IssuerTokenParameters( + privateKey: keyWithAlg, + clientId: clientId, + did: clientId, + clientType: ClientType.did, + mediaType: MediaType.proofOfOwnership, + proofHeaderType: ProofHeaderType.kid, + issuer: issuer, + ); + expect(tokenParameters.alg, HS256Alg); + } +} diff --git a/packages/oidc4vc/test/src/issuer_token_parameters/issuer_token_parameters_test.dart b/packages/oidc4vc/test/src/issuer_token_parameters/issuer_token_parameters_test.dart index edbdf1e0c..5de3e5960 100644 --- a/packages/oidc4vc/test/src/issuer_token_parameters/issuer_token_parameters_test.dart +++ b/packages/oidc4vc/test/src/issuer_token_parameters/issuer_token_parameters_test.dart @@ -1,47 +1,47 @@ -// import 'package:flutter_test/flutter_test.dart'; - -// import 'issuer_token_parameters_class.dart'; - -// void main() { -// group('override test', () { -// final issuerTokenParameterTest = IssuerTokenParameterTest(); - -// // test( -// // 'public key is P-256K private key without d parameter', -// // issuerTokenParameterTest.publicKeyTest, -// // ); - -// // test('did EBSI', issuerTokenParameterTest.didTest); - -// // test('kID EBSI', issuerTokenParameterTest.keyIdTest); - -// group('algorithm test', () { -// test( -// "algorithm is ES256K when key's curve is not P-256", -// issuerTokenParameterTest.algorithmIsES256KTest, -// ); - -// test( -// "algorithm is ES256 when key's curve is P-256", -// issuerTokenParameterTest.algorithmIsES256Test, -// ); - -// test( -// 'if alg is not null then return as it is', -// issuerTokenParameterTest.algorithmIsNotNullTest, -// ); -// }); - -// // group('thumbprint test', () { -// // test( -// // 'thumbprint of the public Key', -// // issuerTokenParameterTest.thumprintOfKey, -// // ); - -// // test( -// // 'thumbrprint of the Key from exemple in rfc 7638', -// // issuerTokenParameterTest.thumprintOfKeyForrfc7638, -// // ); -// // }); -// }); -// } +import 'package:flutter_test/flutter_test.dart'; + +import 'issuer_token_parameters_class.dart'; + +void main() { + group('override test', () { + final issuerTokenParameterTest = IssuerTokenParameterTest(); + + test( + 'public key is P-256K private key without d parameter', + issuerTokenParameterTest.publicKeyTest, + ); + + test('did EBSI', issuerTokenParameterTest.didTest); + + test('kID EBSI', issuerTokenParameterTest.keyIdTest); + + group('algorithm test', () { + test( + "algorithm is ES256K when key's curve is not P-256", + issuerTokenParameterTest.algorithmIsES256KTest, + ); + + test( + "algorithm is ES256 when key's curve is P-256", + issuerTokenParameterTest.algorithmIsES256Test, + ); + + test( + 'if alg is not null then return as it is', + issuerTokenParameterTest.algorithmIsNotNullTest, + ); + }); + + group('thumbprint test', () { + test( + 'thumbprint of the public Key', + issuerTokenParameterTest.thumprintOfKey, + ); + + test( + 'thumbrprint of the Key from exemple in rfc 7638', + issuerTokenParameterTest.thumprintOfKeyForrfc7638, + ); + }); + }); +} diff --git a/packages/oidc4vc/test/src/media_type_test.dart b/packages/oidc4vc/test/src/media_type_test.dart new file mode 100644 index 000000000..5237155c2 --- /dev/null +++ b/packages/oidc4vc/test/src/media_type_test.dart @@ -0,0 +1,13 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:oidc4vc/oidc4vc.dart'; + +void main() { + group('MediaTypeX', () { + test('typ', () { + expect(MediaType.proofOfOwnership.typ, 'openid4vci-proof+jwt'); + expect(MediaType.basic.typ, 'JWT'); + expect(MediaType.walletAttestation.typ, 'wiar+jwt'); + expect(MediaType.selectiveDisclosure.typ, 'kb+jwt'); + }); + }); +} diff --git a/packages/oidc4vc/test/src/oidc4vc_test.dart b/packages/oidc4vc/test/src/oidc4vc_test.dart index 16d13c8dd..a87776a2f 100644 --- a/packages/oidc4vc/test/src/oidc4vc_test.dart +++ b/packages/oidc4vc/test/src/oidc4vc_test.dart @@ -6,6 +6,8 @@ // license that can be found in the LICENSE file or at // https://opensource.org/licenses/MIT. +// ignore_for_file: unnecessary_string_escapes + import 'dart:convert'; import 'package:dio/dio.dart'; @@ -14,827 +16,916 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:http_mock_adapter/http_mock_adapter.dart'; import 'package:mocktail/mocktail.dart'; import 'package:oidc4vc/oidc4vc.dart'; - -class MockDio extends Mock implements Dio {} +import 'package:secure_storage/secure_storage.dart'; class MockPkcePair extends Mock implements PkcePair {} +class MockSecureStorage extends Mock implements SecureStorageProvider {} + void main() { - final client = Dio(); - final dioAdapter = - DioAdapter(dio: Dio(BaseOptions()), matcher: const UrlRequestMatcher()); - client.httpClientAdapter = dioAdapter; + group('OIDC4VC Test', () { + final client = Dio(); + final dioAdapter = + DioAdapter(dio: Dio(BaseOptions()), matcher: const UrlRequestMatcher()); - const mnemonic = - 'position taste mention august skin taste best air sure acoustic poet ritual'; + client.httpClientAdapter = dioAdapter; - test('OIDC4VC class can be instantiated', () { - final oidc4vc = OIDC4VC(); - expect(oidc4vc, isNotNull); - }); + const mnemonic = + 'position taste mention august skin taste best air sure acoustic poet ritual'; - group('EBSI DID and JWK', () { final oidc4vc = OIDC4VC(); - const seedBytes = [ - 179, - 252, - 27, - 232, - 71, - 245, - 106, - 183, - 70, - 177, - 62, - 72, - 151, - 165, - 139, - 70, - 244, - 61, - 102, - 237, - 37, - 167, - 178, - 54, - 57, - 92, - 45, - 205, - 62, - 98, - 66, - 154, - ]; - - const expectedECJwk = { - 'crv': 'secp256k1', - 'd': 's_wb6Ef1ardGsT5Il6WLRvQ9Zu0lp7I2OVwtzT5iQpo', - 'kty': 'EC', - 'x': 'qs4JLbsmA-7L-3o9V4BoNVrDtYoWE2OOZIvujoVJZ1U', - 'y': '8PLGROkTALZP3YHY5pm0yrMVCjQoctHM3uaxug70mq8', - }; - - const index = 0; - test('JWK from mnemonic', () async { - final jwk = await oidc4vc.privateKeyFromMnemonic( + final mockSecureStorage = MockSecureStorage(); + + setUpAll(() { + when(() => mockSecureStorage.delete(any())) + .thenAnswer((_) => Future.value()); + }); + + test('OIDC4VC class can be instantiated', () { + expect(oidc4vc, isNotNull); + }); + + group('OIDC4VC DID and JWK', () { + const seedBytes = [ + 179, + 252, + 27, + 232, + 71, + 245, + 106, + 183, + 70, + 177, + 62, + 72, + 151, + 165, + 139, + 70, + 244, + 61, + 102, + 237, + 37, + 167, + 178, + 54, + 57, + 92, + 45, + 205, + 62, + 98, + 66, + 154, + ]; + + const expectedECJwk = { + 'crv': 'secp256k1', + 'd': 's_wb6Ef1ardGsT5Il6WLRvQ9Zu0lp7I2OVwtzT5iQpo', + 'kty': 'EC', + 'x': 'qs4JLbsmA-7L-3o9V4BoNVrDtYoWE2OOZIvujoVJZ1U', + 'y': '8PLGROkTALZP3YHY5pm0yrMVCjQoctHM3uaxug70mq8', + }; + + const index = 0; + test('JWK from mnemonic', () { + final jwk = oidc4vc.privateKeyFromMnemonic( + mnemonic: mnemonic, + indexValue: index, + ); + expect(jsonDecode(jwk), expectedECJwk); + }); + + test('JWK from seeds', () { + final jwk = + oidc4vc.jwkFromSeed(seedBytes: Uint8List.fromList(seedBytes)); + expect(jwk, expectedECJwk); + }); + }); + + test('P256 JWK from mnemonics', () { + final jwk = oidc4vc.p256PrivateKeyFromMnemonics( mnemonic: mnemonic, - indexValue: index, + indexValue: 0, ); - expect(jsonDecode(jwk), expectedECJwk); + final expectedP256Jwk = { + 'kty': 'EC', + 'crv': 'P-256', + 'd': 's_wb6Ef1ardGsT5Il6WLRvQ9Zu0lp7I2OVwtzT5iQpo', + 'x': 'MZZjpNhZGGxqBcPXq499FVC2iu5FcZWwti5u8hgMUaI', + 'y': 'KD4zffV54PZUsQzTzVgoVlWHwKqogRF3JpKQnIGwIRM', + }; + + expect(jsonDecode(jwk), expectedP256Jwk); }); - // group('getPrivateKey', () { - // test('privateKey from mnemonic', () async { - // final jwk = await oidc4vc.getPrivateKey( - // mnemonic: mnemonic, - // indexValue: index, - // ); - // expect(jwk, expectedECJwk); - // }); - - // test('privateKey from key', () async { - // const key = { - // 'crv': 'secp256k1', - // 'd': 's_wb6Ef1ardGsT5Il6WLRvQ9Zu0lp7I2OVwtzT5iQpo', - // 'kty': 'EC', - // 'x': 'qs4JLbsmA-7L-3o9V4BoNVrDtYoWE2OOZIvujoVJZ1U', - // 'y': '8PLGROkTALZP3YHY5pm0yrMVCjQoctHM3uaxug70mq8', - // }; - // final jwk = await oidc4vc.getPrivateKey( - // privateKey: jsonEncode(key), - // indexValue: index, - // ); - // expect(jwk, expectedECJwk); - // }); - // }); - - test('JWK from seeds', () { - final jwk = oidc4vc.jwkFromSeed(seedBytes: Uint8List.fromList(seedBytes)); - expect(jwk, expectedECJwk); + group('edSSA sign and verify test', () { + const privateKey = { + 'kty': 'OKP', + 'crv': 'Ed25519', + 'd': 'lIxdBQu5EHleLsQRF8JOXAImgNu4FXrUs5SOcyrqvO0=', + 'x': 'r_HVGgBwEcVShl1Xt0C_Anc7Qhs4mS5ZUxsR4kq7Qe4=', + }; + + const payload = { + 'name': 'Bibash', + 'surname': 'Shrestha', + }; + + const kid = '3623b877bbb24b08ba390f3585418f53'; + + test('sign and verify with edDSA', () async { + final token = oidc4vc.generateTokenEdDSA( + payload: payload, + privateKey: privateKey, + kid: kid, + ); + + final value = oidc4vc.verifyTokenEdDSA( + token: token, + publicKey: privateKey, // public is private key with d + ); + + expect(value, true); + }); }); - }); - test('P256 JWK from mnemonics', () { - final oidc4vc = OIDC4VC(); - final jwk = oidc4vc.p256PrivateKeyFromMnemonics( - mnemonic: mnemonic, - indexValue: 0, - ); - final expectedP256Jwk = { - 'kty': 'EC', - 'crv': 'P-256', - 'd': 'cFCdgT569Shto8jEVbyKqdtEck0EcUSwGz_vqrclTC8', - 'x': 'vhoSh07qpGqiCJNGQJmY8YaARnxuHUgv403c5TmrABQ', - 'y': 'E04Ate9RceryoHlz1x3BVIbN9LZR74TWRCeeMNY-oew', - }; - - expect(jsonDecode(jwk), expectedP256Jwk); - }); + group('selective disclosure', () { + const content = + '["Qg_O64zqAxe412a108iroA", "phone_number", "+81-80-1234-5678"]'; - group('edSSA sign and verify test', () { - const privateKey = { - 'kty': 'OKP', - 'crv': 'Ed25519', - 'd': 'lIxdBQu5EHleLsQRF8JOXAImgNu4FXrUs5SOcyrqvO0=', - 'x': 'r_HVGgBwEcVShl1Xt0C_Anc7Qhs4mS5ZUxsR4kq7Qe4=', - }; + const expectedDisclosure = + 'WyJRZ19PNjR6cUF4ZTQxMmExMDhpcm9BIiwgInBob25lX251bWJlciIsICIrODEtODAtMTIzNC01Njc4Il0'; - const payload = { - 'name': 'Bibash', - 'surname': 'Shrestha', - }; + const expectedHash = 's0BKYsLWxQQeU8tVlltM7MKsIRTrEIa1PkJmqxBBf5U'; - const kid = '3623b877bbb24b08ba390f3585418f53'; + test('get disclosure', () { + final disclosure = oidc4vc.getDisclosure(content); + expect(disclosure, expectedDisclosure); + }); - final oidc4vc = OIDC4VC(); + test('sh256 hash of Disclosure test', () { + final sha256Hash = oidc4vc.sh256HashOfContent(content); + expect(sha256Hash, expectedHash); + }); + }); - test('sign and verify with edDSA', () async { - final token = oidc4vc.generateTokenEdDSA( - payload: payload, - privateKey: privateKey, - kid: kid, - ); + group('publicKeyBase58ToPublicJwk', () { + const publicKeyBase58 = '2S73k5pn5umfnaW31qx6dXFndEn6SmWw7LpgSjNNC5BF'; - final value = oidc4vc.verifyTokenEdDSA( - token: token, - publicKey: privateKey, // public is private key with d - ); + final expectedPublicJWK = { + 'crv': 'Ed25519', + 'kty': 'OKP', + 'x': 'FUoLewH4w4-KdaPH2cjZbL--CKYxQRWR05Yd_bIbhQo', + }; - expect(value, true); + test('convert publicKeyBase58 to PublicJwk', () { + final publicKey = oidc4vc.publicKeyBase58ToPublicJwk(publicKeyBase58); + expect(publicKey, expectedPublicJWK); + }); }); - }); - group('selective disclosure', () { - final oidc4vc = OIDC4VC(); + group('EBSI: getAuthorizationUriForIssuer', () { + const selectedCredentials = ['EmailPass']; + const clientId = + 'did:jwk:eyJjcnYiOiJQLTI1NiIsImt0eSI6IkVDIiwieCI6IjZka1U2Wk1GSzc5V3dpY3dKNXJieEUxM3pTdWtCWTJPb0VpVlVFanFNRWMiLCJ5IjoiUm5Iem55VmxyUFNNVDdpckRzMTVEOXd4Z01vamlTREFRcGZGaHFUa0xSWSJ9'; + const redirectUri = 'https://app.altme.io/app/download/oidc4vc'; - const content = - '["Qg_O64zqAxe412a108iroA", "phone_number", "+81-80-1234-5678"]'; + const issuer = 'https://talao.co/issuer/mfyttabosy'; - const expectedDisclosure = - 'WyJRZ19PNjR6cUF4ZTQxMmExMDhpcm9BIiwgInBob25lX251bWJlciIsICIrODEtODAtMTIzNC01Njc4Il0'; + const issuerState = 'test7'; + const nonce = '8b60e2fb-87f3-4401-8107-0f0128ea01ab'; - const expectedHash = 's0BKYsLWxQQeU8tVlltM7MKsIRTrEIa1PkJmqxBBf5U'; + const pkcePair = PkcePair( + 'Pzy4U_sJ0J7VdIAR6JCwL5hbecv30egmJVP81VDFAnk', + '4KorCwmYyO-_t4i_hva7F3aHGpT_2WqqDh6erimepOA', + ); - test('get disclosure', () { - final disclosure = oidc4vc.getDisclosure(content); - expect(disclosure, expectedDisclosure); - }); + const state = + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb2RlVmVyaWZpZXIiOiI0S29yQ3dtWXlPLV90NGlfaHZhN0YzYUhHcFRfMldxcURoNmVyaW1lcE9BIiwiY3JlZGVudGlhbHMiOlsiRW1haWxQYXNzIl0sImlzc3VlciI6Imh0dHBzOi8vdGFsYW8uY28vaXNzdWVyL21meXR0YWJvc3kiLCJpc0VCU0lWMyI6ZmFsc2UsImNsaWVudF9pZCI6ImRpZDpqd2s6ZXlKamNuWWlPaUpRTFRJMU5pSXNJbXQwZVNJNklrVkRJaXdpZUNJNklqWmthMVUyV2sxR1N6YzVWM2RwWTNkS05YSmllRVV4TTNwVGRXdENXVEpQYjBWcFZsVkZhbkZOUldNaUxDSjVJam9pVW01SWVtNTVWbXh5VUZOTlZEZHBja1J6TVRWRU9YZDRaMDF2YW1sVFJFRlJjR1pHYUhGVWEweFNXU0o5IiwiaWF0IjoxNzE0NTQ5OTUxfQ.JJtv8H52NTvPzR3IPET1sXGALdt0yXaQBQbGvDLKNlM'; + + const authorizationEndPoint = + 'https://app.altme.io/app/download/authorize'; + + const openIdConfiguration = + '{"authorization_server":null,"credential_endpoint":"https://talao.co/issuer/mfyttabosy/credential","credential_issuer":"https://talao.co/issuer/mfyttabosy","subject_syntax_types_supported":["urn:ietf:params:oauth:jwk-thumbprint","did:key","did:ebsi","did:tz","did:pkh","did:hedera","did:key","did:ethr","did:web","did:jwk"],"token_endpoint":"https://talao.co/issuer/mfyttabosy/token","batch_endpoint":null,"authorization_endpoint":"https://talao.co/issuer/mfyttabosy/authorize","subject_trust_frameworks_supported":["ebsi"],"credentials_supported":null,"credential_configurations_supported":{"DBCGuest":{"credential_definition":{"type":["VerifiableCredential","DBCGuest"]},"display":[{"background_color":"#3B6F6D","background_image":{"alt_text":"Connected open cubes in blue with one orange cube as a background of the card","url":"https://i.ibb.co/CHqjxrJ/dbc-card-hig-res.png"},"description":"The DBC Guest credential is a DIIP example.","logo":{"alt_text":"An orange block shape, with the text Dutch Blockchain Coalition next to it, portraying the logo of the Dutch Blockchain Coalition.","url":"https://dutchblockchaincoalition.org/assets/images/icons/Logo-DBC.png"},"name":"DBC Guest (DIIP)","text_color":"#FFFFFF"},{"background_color":"#3B6F6D","background_image":{"alt_text":"Connected open cubes in blue with one orange cube as a background of the card","url":"https://i.ibb.co/CHqjxrJ/dbc-card-hig-res.png"},"description":"The DBC guest credential is a DIIP example.","locale":"en-US","logo":{"alt_text":"An orange block shape, with the text Dutch Blockchain Coalition next to it, portraying the logo of the Dutch Blockchain Coalition.","url":"https://dutchblockchaincoalition.org/assets/images/icons/Logo-DBC.png"},"name":"DBC Guest (DIIP)","text_color":"#FFFFFF"},{"background_color":"#3B6F6D","background_image":{"alt_text":"Connected open cubes in blue with one orange cube as a background of the card","url":"https://i.ibb.co/CHqjxrJ/dbc-card-hig-res.png"},"description":"De DBC gast credential is een DIIP voorbeeld.","locale":"nl-NL","logo":{"alt_text":"Aaneengesloten open blokken in de kleur blauw, met een blok in de kleur oranje, die tesamen de achtergrond van de kaart vormen.","url":"https://dutchblockchaincoalition.org/assets/images/icons/Logo-DBC.png"},"name":"DBC gast (DIIP)","text_color":"#FFFFFF"}],"format":"jwt_vc_json","scope":"DBCGuest_scope"},"EmailPass":{"credential_definition":{"type":["VerifiableCredential","EmailPass"]},"credential_signing_alg_values_supported":["ES256K","ES256","ES384","RS256"],"cryptographic_binding_methods_supported":["DID","jwk"],"display":[{"locale":"en-GB","name":"Proof of Email"}],"format":"jwt_vc_json","scope":"EmailPass_scope"},"EmployeeCredential":{"credential_definition":{"type":["VerifiableCredential","EmployeeCredential"]},"credential_signing_alg_values_supported":["ES256K","ES256","ES384","RS256"],"cryptographic_binding_methods_supported":["DID","jwk"],"display":[{"background_color":"#12107c","locale":"en-US","logo":{"alt_text":"a square logo of a university","url":"https://exampleuniversity.com/public/logo.png"},"name":"Employee Credential","text_color":"#FFFFFF"}],"format":"jwt_vc_json","scope":"EmployeeCredential_scope"},"Over18":{"credential_definition":{"type":["VerifiableCredential","Over18"]},"credential_signing_alg_values_supported":["ES256K","ES256","ES384","RS256"],"cryptographic_binding_methods_supported":["DID","jwk"],"display":[{"locale":"en-GB","name":"Over 18yo proof"},{"locale":"fr-GB","name":"Preuve de majorité"}],"format":"jwt_vc_json","scope":"Over18_scope"},"PhoneProof":{"credential_definition":{"type":["VerifiableCredential","PhoneProof"]},"credential_signing_alg_values_supported":["ES256K","ES256","ES384","RS256"],"cryptographic_binding_methods_supported":["DID","jwk"],"display":[{"locale":"en-GB","name":"Proof of phone number"}],"format":"jwt_vc_json","scope":"PhoneProof_scope"},"VerifiableId":{"credential_definition":{"credentialSubject":{"dateIssued":{"display":[{"locale":"en-US","name":"Issuance date"},{"locale":"fr-FR","name":"Délivré le"}],"mandatory":true},"dateOfBirth":{"display":[{"locale":"en-US","name":"Date of birth"},{"locale":"fr-FR","name":"Né(e) le"}],"mandatory":true},"email":{"display":[{"locale":"en-US","name":"Email"},{"locale":"fr-FR","name":"Email"}],"mandatory":true},"familyName":{"display":[{"locale":"en-US","name":"Family name"},{"locale":"fr-FR","name":"Nom"}],"mandatory":true},"firstName":{"display":[{"locale":"en-US","name":"First name"},{"locale":"fr-FR","name":"Prénom(s)"}],"mandatory":true},"gender":{"display":[{"locale":"en-US","name":"Gender"},{"locale":"fr-FR","name":"Sexe"}],"mandatory":true},"issuing_country":{"display":[{"locale":"en-US","name":"Issuing country"},{"locale":"fr-FR","name":"Délivré par"}],"mandatory":true},"phone_number":{"display":[{"locale":"en-US","name":"Phone number"},{"locale":"fr-FR","name":"Téléphone"}],"mandatory":true}},"order":["firstName","familyName","dateOfBirth","gender","dateIssued","issuing_country","email","phone_number"],"type":["VerifiableCredential","VerifiableId"]},"credential_signing_alg_values_supported":["ES256K","ES256","ES384","RS256"],"cryptographic_binding_methods_supported":["DID","jwk"],"display":[{"background_color":"#12107c","locale":"en-US","name":"Verifiable Id","text_color":"#FFFFFF"}],"format":"jwt_vc_json","scope":"VerifiableId_scope"}},"deferred_credential_endpoint":"https://talao.co/issuer/mfyttabosy/deferred","service_documentation":null,"credential_manifest":null,"credential_manifests":null,"issuer":null,"jwks_uri":"https://talao.co/issuer/mfyttabosy/jwks","grant_types_supported":["authorization_code","urn:ietf:params:oauth:grant-type:pre-authorized_code"]}'; + + const credentialOfferJson = { + 'credential_offer': { + 'credential_issuer': 'https://talao.co/issuer/pcbrwbvrsi', + 'credential_configuration_ids': ['Pid'], + 'grants': { + 'authorization_code': { + 'issuer_state': 'test11', + 'authorization_server': 'https://talao.co/issuer/pcbrwbvrsi', + }, + }, + }, + }; + + test( + 'given Url of openid request we return Uri for authentication endpoint', + () async { + const expectedAuthorizationEndpoint = + 'https://talao.co/issuer/mfyttabosy/authorize'; + + const expectedAuthorizationRequestParemeters = { + 'response_type': 'code', + 'redirect_uri': 'https://app.altme.io/app/download/oidc4vc', + 'state': + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb2RlVmVyaWZpZXIiOiI0S29yQ3dtWXlPLV90NGlfaHZhN0YzYUhHcFRfMldxcURoNmVyaW1lcE9BIiwiY3JlZGVudGlhbHMiOlsiRW1haWxQYXNzIl0sImlzc3VlciI6Imh0dHBzOi8vdGFsYW8uY28vaXNzdWVyL21meXR0YWJvc3kiLCJpc0VCU0lWMyI6ZmFsc2UsImNsaWVudF9pZCI6ImRpZDpqd2s6ZXlKamNuWWlPaUpRTFRJMU5pSXNJbXQwZVNJNklrVkRJaXdpZUNJNklqWmthMVUyV2sxR1N6YzVWM2RwWTNkS05YSmllRVV4TTNwVGRXdENXVEpQYjBWcFZsVkZhbkZOUldNaUxDSjVJam9pVW01SWVtNTVWbXh5VUZOTlZEZHBja1J6TVRWRU9YZDRaMDF2YW1sVFJFRlJjR1pHYUhGVWEweFNXU0o5IiwiaWF0IjoxNzE0NTQ5OTUxfQ.JJtv8H52NTvPzR3IPET1sXGALdt0yXaQBQbGvDLKNlM', + 'nonce': '8b60e2fb-87f3-4401-8107-0f0128ea01ab', + 'code_challenge': '4KorCwmYyO-_t4i_hva7F3aHGpT_2WqqDh6erimepOA', + 'code_challenge_method': 'S256', + 'issuer_state': 'test7', + 'client_metadata': + '{\"authorization_endpoint\":\"https://app.altme.io/app/download/authorize\",\"scopes_supported\":[\"openid\"],\"response_types_supported\":[\"vp_token\",\"id_token\"],\"client_id_schemes_supported\":[\"redirect_uri\",\"did\"],\"grant_types_supported\":[\"authorization_code\",\"pre-authorized_code\"],\"subject_types_supported\":[\"public\"],\"id_token_signing_alg_values_supported\":[\"ES256\",\"ES256K\"],\"request_object_signing_alg_values_supported\":[\"ES256\",\"ES256K\"],\"request_parameter_supported\":true,\"request_uri_parameter_supported\":true,\"request_authentication_methods_supported\":{\"authorization_endpoint\":[\"request_object\"]},\"vp_formats_supported\":{\"jwt_vp\":{\"alg_values_supported\":[\"ES256\",\"ES256K\"]},\"jwt_vc\":{\"alg_values_supported\":[\"ES256\",\"ES256K\"]}},\"subject_syntax_types_supported\":[\"urn:ietf:params:oauth:jwk-thumbprint\",\"did:key\",\"did:pkh\",\"did:key\",\"did:polygonid\"],\"subject_syntax_types_discriminations\":[\"did:key:jwk_jcs-pub\",\"did:ebsi:v1\"],\"subject_trust_frameworks_supported\":[\"ebsi\"],\"id_token_types_supported\":[\"subject_signed_id_token\"],\"token_endpoint_auth_method\":\"client_id\"}', + 'client_id': + 'did:jwk:eyJjcnYiOiJQLTI1NiIsImt0eSI6IkVDIiwieCI6IjZka1U2Wk1GSzc5V3dpY3dKNXJieEUxM3pTdWtCWTJPb0VpVlVFanFNRWMiLCJ5IjoiUm5Iem55VmxyUFNNVDdpckRzMTVEOXd4Z01vamlTREFRcGZGaHFUa0xSWSJ9', + 'scope': 'openid', + 'authorization_details': + '[{\"type\":\"openid_credential\",\"credential_configuration_id\":\"EmailPass\"}]', + }; + + dioAdapter.onGet( + 'https://talao.co/issuer/mfyttabosy/.well-known/openid-credential-issuer', + (request) => request.reply(200, jsonDecode(openIdConfiguration)), + ); + final (authorizationEndpoint, authorizationRequestParemeters, _) = + await oidc4vc.getAuthorizationData( + selectedCredentials: selectedCredentials, + clientId: clientId, + clientSecret: null, + redirectUri: redirectUri, + issuerState: issuerState, + nonce: nonce, + pkcePair: pkcePair, + state: state, + authorizationEndPoint: authorizationEndPoint, + scope: false, + clientAuthentication: ClientAuthentication.clientId, + oidc4vciDraftType: OIDC4VCIDraftType.draft13, + vcFormatType: VCFormatType.jwtVcJson, + clientAssertion: null, + secureAuthorizedFlow: false, + issuer: issuer, + dio: client, + credentialOfferJson: credentialOfferJson, + secureStorage: mockSecureStorage, + ); + + expect(authorizationEndpoint, expectedAuthorizationEndpoint); + expect( + authorizationRequestParemeters, + expectedAuthorizationRequestParemeters, + ); + }, + ); - test('sh256 hash of Disclosure test', () { - final sha256Hash = oidc4vc.sh256HashOfContent(content); - expect(sha256Hash, expectedHash); + test( + 'throw Exception with when request is not valid', + () async { + expect( + () async => oidc4vc.getAuthorizationData( + selectedCredentials: [], + clientId: '', + issuer: '', + issuerState: '', + nonce: '', + state: '', + pkcePair: const PkcePair( + '', + '', + ), + authorizationEndPoint: '', + clientAssertion: '', + clientAuthentication: ClientAuthentication.clientId, + clientSecret: '', + oidc4vciDraftType: OIDC4VCIDraftType.draft11, + redirectUri: '', + scope: false, + secureAuthorizedFlow: false, + vcFormatType: VCFormatType.jwtVc, + credentialOfferJson: credentialOfferJson, + dio: client, + ), + throwsA( + isA().having( + (p0) => p0.toString(), + 'toString()', + 'Exception: NOT_A_VALID_OPENID_URL', + ), + ), + ); + }, + ); + + test( + 'given correct authorization request parameter', + () async { + const expectedAuthorizationRequestParemeters = + r'{"response_type":"code","redirect_uri":"https://app.altme.io/app/download/oidc4vc","state":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb2RlVmVyaWZpZXIiOiI0S29yQ3dtWXlPLV90NGlfaHZhN0YzYUhHcFRfMldxcURoNmVyaW1lcE9BIiwiY3JlZGVudGlhbHMiOlsiRW1haWxQYXNzIl0sImlzc3VlciI6Imh0dHBzOi8vdGFsYW8uY28vaXNzdWVyL21meXR0YWJvc3kiLCJpc0VCU0lWMyI6ZmFsc2UsImNsaWVudF9pZCI6ImRpZDpqd2s6ZXlKamNuWWlPaUpRTFRJMU5pSXNJbXQwZVNJNklrVkRJaXdpZUNJNklqWmthMVUyV2sxR1N6YzVWM2RwWTNkS05YSmllRVV4TTNwVGRXdENXVEpQYjBWcFZsVkZhbkZOUldNaUxDSjVJam9pVW01SWVtNTVWbXh5VUZOTlZEZHBja1J6TVRWRU9YZDRaMDF2YW1sVFJFRlJjR1pHYUhGVWEweFNXU0o5IiwiaWF0IjoxNzE0NTQ5OTUxfQ.JJtv8H52NTvPzR3IPET1sXGALdt0yXaQBQbGvDLKNlM","nonce":"8b60e2fb-87f3-4401-8107-0f0128ea01ab","code_challenge":"4KorCwmYyO-_t4i_hva7F3aHGpT_2WqqDh6erimepOA","code_challenge_method":"S256","issuer_state":"test7","client_metadata":"{\"authorization_endpoint\":\"https://app.altme.io/app/download/authorize\",\"scopes_supported\":[\"openid\"],\"response_types_supported\":[\"vp_token\",\"id_token\"],\"client_id_schemes_supported\":[\"redirect_uri\",\"did\"],\"grant_types_supported\":[\"authorization_code\",\"pre-authorized_code\"],\"subject_types_supported\":[\"public\"],\"id_token_signing_alg_values_supported\":[\"ES256\",\"ES256K\"],\"request_object_signing_alg_values_supported\":[\"ES256\",\"ES256K\"],\"request_parameter_supported\":true,\"request_uri_parameter_supported\":true,\"request_authentication_methods_supported\":{\"authorization_endpoint\":[\"request_object\"]},\"vp_formats_supported\":{\"jwt_vp\":{\"alg_values_supported\":[\"ES256\",\"ES256K\"]},\"jwt_vc\":{\"alg_values_supported\":[\"ES256\",\"ES256K\"]}},\"subject_syntax_types_supported\":[\"urn:ietf:params:oauth:jwk-thumbprint\",\"did:key\",\"did:pkh\",\"did:key\",\"did:polygonid\"],\"subject_syntax_types_discriminations\":[\"did:key:jwk_jcs-pub\",\"did:ebsi:v1\"],\"subject_trust_frameworks_supported\":[\"ebsi\"],\"id_token_types_supported\":[\"subject_signed_id_token\"],\"token_endpoint_auth_method\":\"client_id\"}","client_id":"did:jwk:eyJjcnYiOiJQLTI1NiIsImt0eSI6IkVDIiwieCI6IjZka1U2Wk1GSzc5V3dpY3dKNXJieEUxM3pTdWtCWTJPb0VpVlVFanFNRWMiLCJ5IjoiUm5Iem55VmxyUFNNVDdpckRzMTVEOXd4Z01vamlTREFRcGZGaHFUa0xSWSJ9","scope":"openid","authorization_details":"[{\"type\":\"openid_credential\",\"credential_configuration_id\":\"EmailPass\"}]"}'; + + final authorizationRequestParemeters = + oidc4vc.getAuthorizationRequestParemeters( + selectedCredentials: selectedCredentials, + clientId: clientId, + authorizationEndPoint: authorizationEndPoint, + clientAssertion: null, + scope: false, + clientAuthentication: ClientAuthentication.clientId, + oidc4vciDraftType: OIDC4VCIDraftType.draft13, + vcFormatType: VCFormatType.jwtVcJson, + secureAuthorizedFlow: false, + issuer: issuer, + issuerState: issuerState, + nonce: nonce, + state: state, + pkcePair: pkcePair, + clientSecret: null, + openIdConfiguration: OpenIdConfiguration.fromJson( + jsonDecode(openIdConfiguration) as Map, + ), + redirectUri: redirectUri, + ); + + expect( + jsonEncode(authorizationRequestParemeters), + expectedAuthorizationRequestParemeters, + ); + }, + ); }); - }); - group('publicKeyBase58ToPublicJwk', () { - final oidc4vc = OIDC4VC(); + group('draft 13: getAuthorizationUriForIssuer', () { + const selectedCredentials = ['EmailPass']; + const clientId = + 'did:jwk:eyJjcnYiOiJQLTI1NiIsImt0eSI6IkVDIiwieCI6IjZka1U2Wk1GSzc5V3dpY3dKNXJieEUxM3pTdWtCWTJPb0VpVlVFanFNRWMiLCJ5IjoiUm5Iem55VmxyUFNNVDdpckRzMTVEOXd4Z01vamlTREFRcGZGaHFUa0xSWSJ9'; + const redirectUri = 'https://app.altme.io/app/download/oidc4vc'; - const publicKeyBase58 = '2S73k5pn5umfnaW31qx6dXFndEn6SmWw7LpgSjNNC5BF'; + const issuer = 'https://talao.co/issuer/mfyttabosy'; - final expectedPublicJWK = { - 'crv': 'Ed25519', - 'kty': 'OKP', - 'x': 'FUoLewH4w4-KdaPH2cjZbL--CKYxQRWR05Yd_bIbhQo', - }; + const issuerState = 'test7'; + const nonce = '8b60e2fb-87f3-4401-8107-0f0128ea01ab'; - test('convert publicKeyBase58 to PublicJwk', () { - final publicKey = oidc4vc.publicKeyBase58ToPublicJwk(publicKeyBase58); - expect(publicKey, expectedPublicJWK); - }); - }); + const pkcePair = PkcePair( + 'Pzy4U_sJ0J7VdIAR6JCwL5hbecv30egmJVP81VDFAnk', + '4KorCwmYyO-_t4i_hva7F3aHGpT_2WqqDh6erimepOA', + ); - group('EBSI: getAuthorizationUriForIssuer', () { - const issuer = 'https://talao.co/sandbox/ebsi/issuer/pcbrwbvrsi'; + const state = + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb2RlVmVyaWZpZXIiOiI0S29yQ3dtWXlPLV90NGlfaHZhN0YzYUhHcFRfMldxcURoNmVyaW1lcE9BIiwiY3JlZGVudGlhbHMiOlsiRW1haWxQYXNzIl0sImlzc3VlciI6Imh0dHBzOi8vdGFsYW8uY28vaXNzdWVyL21meXR0YWJvc3kiLCJpc0VCU0lWMyI6ZmFsc2UsImNsaWVudF9pZCI6ImRpZDpqd2s6ZXlKamNuWWlPaUpRTFRJMU5pSXNJbXQwZVNJNklrVkRJaXdpZUNJNklqWmthMVUyV2sxR1N6YzVWM2RwWTNkS05YSmllRVV4TTNwVGRXdENXVEpQYjBWcFZsVkZhbkZOUldNaUxDSjVJam9pVW01SWVtNTVWbXh5VUZOTlZEZHBja1J6TVRWRU9YZDRaMDF2YW1sVFJFRlJjR1pHYUhGVWEweFNXU0o5IiwiaWF0IjoxNzE0NTQ5OTUxfQ.JJtv8H52NTvPzR3IPET1sXGALdt0yXaQBQbGvDLKNlM'; + + const authorizationEndPoint = + 'https://app.altme.io/app/download/authorize'; + + const openIdConfiguration = + '{"authorization_server":null,"credential_endpoint":"https://talao.co/issuer/mfyttabosy/credential","credential_issuer":"https://talao.co/issuer/mfyttabosy","subject_syntax_types_supported":["urn:ietf:params:oauth:jwk-thumbprint","did:key","did:ebsi","did:tz","did:pkh","did:hedera","did:key","did:ethr","did:web","did:jwk"],"token_endpoint":"https://talao.co/issuer/mfyttabosy/token","batch_endpoint":null,"authorization_endpoint":"https://talao.co/issuer/mfyttabosy/authorize","subject_trust_frameworks_supported":["ebsi"],"credentials_supported":null,"credential_configurations_supported":{"DBCGuest":{"credential_definition":{"type":["VerifiableCredential","DBCGuest"]},"display":[{"background_color":"#3B6F6D","background_image":{"alt_text":"Connected open cubes in blue with one orange cube as a background of the card","url":"https://i.ibb.co/CHqjxrJ/dbc-card-hig-res.png"},"description":"The DBC Guest credential is a DIIP example.","logo":{"alt_text":"An orange block shape, with the text Dutch Blockchain Coalition next to it, portraying the logo of the Dutch Blockchain Coalition.","url":"https://dutchblockchaincoalition.org/assets/images/icons/Logo-DBC.png"},"name":"DBC Guest (DIIP)","text_color":"#FFFFFF"},{"background_color":"#3B6F6D","background_image":{"alt_text":"Connected open cubes in blue with one orange cube as a background of the card","url":"https://i.ibb.co/CHqjxrJ/dbc-card-hig-res.png"},"description":"The DBC guest credential is a DIIP example.","locale":"en-US","logo":{"alt_text":"An orange block shape, with the text Dutch Blockchain Coalition next to it, portraying the logo of the Dutch Blockchain Coalition.","url":"https://dutchblockchaincoalition.org/assets/images/icons/Logo-DBC.png"},"name":"DBC Guest (DIIP)","text_color":"#FFFFFF"},{"background_color":"#3B6F6D","background_image":{"alt_text":"Connected open cubes in blue with one orange cube as a background of the card","url":"https://i.ibb.co/CHqjxrJ/dbc-card-hig-res.png"},"description":"De DBC gast credential is een DIIP voorbeeld.","locale":"nl-NL","logo":{"alt_text":"Aaneengesloten open blokken in de kleur blauw, met een blok in de kleur oranje, die tesamen de achtergrond van de kaart vormen.","url":"https://dutchblockchaincoalition.org/assets/images/icons/Logo-DBC.png"},"name":"DBC gast (DIIP)","text_color":"#FFFFFF"}],"format":"jwt_vc_json","scope":"DBCGuest_scope"},"EmailPass":{"credential_definition":{"type":["VerifiableCredential","EmailPass"]},"credential_signing_alg_values_supported":["ES256K","ES256","ES384","RS256"],"cryptographic_binding_methods_supported":["DID","jwk"],"display":[{"locale":"en-GB","name":"Proof of Email"}],"format":"jwt_vc_json","scope":"EmailPass_scope"},"EmployeeCredential":{"credential_definition":{"type":["VerifiableCredential","EmployeeCredential"]},"credential_signing_alg_values_supported":["ES256K","ES256","ES384","RS256"],"cryptographic_binding_methods_supported":["DID","jwk"],"display":[{"background_color":"#12107c","locale":"en-US","logo":{"alt_text":"a square logo of a university","url":"https://exampleuniversity.com/public/logo.png"},"name":"Employee Credential","text_color":"#FFFFFF"}],"format":"jwt_vc_json","scope":"EmployeeCredential_scope"},"Over18":{"credential_definition":{"type":["VerifiableCredential","Over18"]},"credential_signing_alg_values_supported":["ES256K","ES256","ES384","RS256"],"cryptographic_binding_methods_supported":["DID","jwk"],"display":[{"locale":"en-GB","name":"Over 18yo proof"},{"locale":"fr-GB","name":"Preuve de majorité"}],"format":"jwt_vc_json","scope":"Over18_scope"},"PhoneProof":{"credential_definition":{"type":["VerifiableCredential","PhoneProof"]},"credential_signing_alg_values_supported":["ES256K","ES256","ES384","RS256"],"cryptographic_binding_methods_supported":["DID","jwk"],"display":[{"locale":"en-GB","name":"Proof of phone number"}],"format":"jwt_vc_json","scope":"PhoneProof_scope"},"VerifiableId":{"credential_definition":{"credentialSubject":{"dateIssued":{"display":[{"locale":"en-US","name":"Issuance date"},{"locale":"fr-FR","name":"Délivré le"}],"mandatory":true},"dateOfBirth":{"display":[{"locale":"en-US","name":"Date of birth"},{"locale":"fr-FR","name":"Né(e) le"}],"mandatory":true},"email":{"display":[{"locale":"en-US","name":"Email"},{"locale":"fr-FR","name":"Email"}],"mandatory":true},"familyName":{"display":[{"locale":"en-US","name":"Family name"},{"locale":"fr-FR","name":"Nom"}],"mandatory":true},"firstName":{"display":[{"locale":"en-US","name":"First name"},{"locale":"fr-FR","name":"Prénom(s)"}],"mandatory":true},"gender":{"display":[{"locale":"en-US","name":"Gender"},{"locale":"fr-FR","name":"Sexe"}],"mandatory":true},"issuing_country":{"display":[{"locale":"en-US","name":"Issuing country"},{"locale":"fr-FR","name":"Délivré par"}],"mandatory":true},"phone_number":{"display":[{"locale":"en-US","name":"Phone number"},{"locale":"fr-FR","name":"Téléphone"}],"mandatory":true}},"order":["firstName","familyName","dateOfBirth","gender","dateIssued","issuing_country","email","phone_number"],"type":["VerifiableCredential","VerifiableId"]},"credential_signing_alg_values_supported":["ES256K","ES256","ES384","RS256"],"cryptographic_binding_methods_supported":["DID","jwk"],"display":[{"background_color":"#12107c","locale":"en-US","name":"Verifiable Id","text_color":"#FFFFFF"}],"format":"jwt_vc_json","scope":"VerifiableId_scope"}},"deferred_credential_endpoint":"https://talao.co/issuer/mfyttabosy/deferred","service_documentation":null,"credential_manifest":null,"credential_manifests":null,"issuer":null,"jwks_uri":"https://talao.co/issuer/mfyttabosy/jwks","grant_types_supported":["authorization_code","urn:ietf:params:oauth:grant-type:pre-authorized_code"]}'; + + const credentialOfferJson = { + 'credential_offer': { + 'credential_issuer': 'https://talao.co/issuer/pcbrwbvrsi', + 'credential_configuration_ids': ['Pid'], + 'grants': { + 'authorization_code': { + 'issuer_state': 'test11', + 'authorization_server': 'https://talao.co/issuer/pcbrwbvrsi', + }, + }, + }, + }; + test( + 'given Url of openid request we return Uri for authentication endpoint', + () async { + const expectedAuthorizationEndpoint = + 'https://talao.co/issuer/mfyttabosy/authorize'; + + const expectedAuthorizationRequestParemeters = { + 'response_type': 'code', + 'redirect_uri': 'https://app.altme.io/app/download/oidc4vc', + 'state': + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjb2RlVmVyaWZpZXIiOiI0S29yQ3dtWXlPLV90NGlfaHZhN0YzYUhHcFRfMldxcURoNmVyaW1lcE9BIiwiY3JlZGVudGlhbHMiOlsiRW1haWxQYXNzIl0sImlzc3VlciI6Imh0dHBzOi8vdGFsYW8uY28vaXNzdWVyL21meXR0YWJvc3kiLCJpc0VCU0lWMyI6ZmFsc2UsImNsaWVudF9pZCI6ImRpZDpqd2s6ZXlKamNuWWlPaUpRTFRJMU5pSXNJbXQwZVNJNklrVkRJaXdpZUNJNklqWmthMVUyV2sxR1N6YzVWM2RwWTNkS05YSmllRVV4TTNwVGRXdENXVEpQYjBWcFZsVkZhbkZOUldNaUxDSjVJam9pVW01SWVtNTVWbXh5VUZOTlZEZHBja1J6TVRWRU9YZDRaMDF2YW1sVFJFRlJjR1pHYUhGVWEweFNXU0o5IiwiaWF0IjoxNzE0NTQ5OTUxfQ.JJtv8H52NTvPzR3IPET1sXGALdt0yXaQBQbGvDLKNlM', + 'nonce': '8b60e2fb-87f3-4401-8107-0f0128ea01ab', + 'code_challenge': '4KorCwmYyO-_t4i_hva7F3aHGpT_2WqqDh6erimepOA', + 'code_challenge_method': 'S256', + 'issuer_state': 'test7', + 'client_metadata': + '{\"authorization_endpoint\":\"https://app.altme.io/app/download/authorize\",\"scopes_supported\":[\"openid\"],\"response_types_supported\":[\"vp_token\",\"id_token\"],\"client_id_schemes_supported\":[\"redirect_uri\",\"did\"],\"grant_types_supported\":[\"authorization_code\",\"pre-authorized_code\"],\"subject_types_supported\":[\"public\"],\"id_token_signing_alg_values_supported\":[\"ES256\",\"ES256K\"],\"request_object_signing_alg_values_supported\":[\"ES256\",\"ES256K\"],\"request_parameter_supported\":true,\"request_uri_parameter_supported\":true,\"request_authentication_methods_supported\":{\"authorization_endpoint\":[\"request_object\"]},\"vp_formats_supported\":{\"jwt_vp\":{\"alg_values_supported\":[\"ES256\",\"ES256K\"]},\"jwt_vc\":{\"alg_values_supported\":[\"ES256\",\"ES256K\"]}},\"subject_syntax_types_supported\":[\"urn:ietf:params:oauth:jwk-thumbprint\",\"did:key\",\"did:pkh\",\"did:key\",\"did:polygonid\"],\"subject_syntax_types_discriminations\":[\"did:key:jwk_jcs-pub\",\"did:ebsi:v1\"],\"subject_trust_frameworks_supported\":[\"ebsi\"],\"id_token_types_supported\":[\"subject_signed_id_token\"],\"token_endpoint_auth_method\":\"client_id\"}', + 'client_id': + 'did:jwk:eyJjcnYiOiJQLTI1NiIsImt0eSI6IkVDIiwieCI6IjZka1U2Wk1GSzc5V3dpY3dKNXJieEUxM3pTdWtCWTJPb0VpVlVFanFNRWMiLCJ5IjoiUm5Iem55VmxyUFNNVDdpckRzMTVEOXd4Z01vamlTREFRcGZGaHFUa0xSWSJ9', + 'scope': 'openid', + 'authorization_details': + '[{\"type\":\"openid_credential\",\"credential_configuration_id\":\"EmailPass\"}]', + }; + + dioAdapter.onGet( + 'https://talao.co/issuer/mfyttabosy/.well-known/openid-credential-issuer', + (request) => request.reply(200, jsonDecode(openIdConfiguration)), + ); + final (authorizationEndpoint, authorizationRequestParemeters, _) = + await oidc4vc.getAuthorizationData( + selectedCredentials: selectedCredentials, + clientId: clientId, + clientSecret: null, + redirectUri: redirectUri, + issuerState: issuerState, + nonce: nonce, + pkcePair: pkcePair, + state: state, + authorizationEndPoint: authorizationEndPoint, + scope: false, + clientAuthentication: ClientAuthentication.clientId, + oidc4vciDraftType: OIDC4VCIDraftType.draft13, + vcFormatType: VCFormatType.jwtVcJson, + clientAssertion: null, + secureAuthorizedFlow: false, + issuer: issuer, + dio: client, + credentialOfferJson: credentialOfferJson, + secureStorage: mockSecureStorage, + ); + + expect(authorizationEndpoint, expectedAuthorizationEndpoint); + expect( + authorizationRequestParemeters, + expectedAuthorizationRequestParemeters, + ); + }, + ); + }); - const openIdConfiguration = - '{"authorization_server":"https://talao.co/sandbox/ebsi/issuer/pcbrwbvrsi/authorize_server","credential_endpoint":"https://talao.co/sandbox/ebsi/issuer/pcbrwbvrsi/credential","credential_issuer":"https://talao.co/sandbox/ebsi/issuer/pcbrwbvrsi","credential_supported":[{"display":[{"locale":"en-GB","name":"Verifiable diploma"}],"format":"jwt_vc","trust_framework":{"name":"ebsi","type":"Accreditation","uri":"TIR link towards accreditation"},"types":["VerifiableCredential","VerifiableAttestation","VerifiableDiploma"]},{"display":[{"locale":"en-GB","name":"Email proof"}],"format":"jwt_vc","trust_framework":{"name":"ebsi","type":"Accreditation","uri":"TIR link towards accreditation"},"types":["VerifiableCredential","EmailPass"]},{"display":[{"locale":"en-GB","name":"Verifiable Id"}],"format":"jwt_vc","id":"VerifiableId","trust_framework":{"name":"ebsi","type":"Accreditation","uri":"TIR link towards accreditation"},"types":["VerifiableCredential","VerifiableAttestation","VerifiableId"]}],"credentials_supported":[{"display":[{"locale":"en-GB","name":"Verifiable diploma"}],"format":"jwt_vc","trust_framework":{"name":"ebsi","type":"Accreditation","uri":"TIR link towards accreditation"},"types":["VerifiableCredential","VerifiableAttestation","VerifiableDiploma"]},{"display":[{"locale":"en-GB","name":"Email proof"}],"format":"jwt_vc","trust_framework":{"name":"ebsi","type":"Accreditation","uri":"TIR link towards accreditation"},"types":["VerifiableCredential","EmailPass"]},{"display":[{"locale":"en-GB","name":"Verifiable Id"}],"format":"jwt_vc","id":"VerifiableId","trust_framework":{"name":"ebsi","type":"Accreditation","uri":"TIR link towards accreditation"},"types":["VerifiableCredential","VerifiableAttestation","VerifiableId"]}],"deferred_credential_endpoint":"https://talao.co/sandbox/ebsi/issuer/pcbrwbvrsi/deferred","service_documentation":"New environment for V3 compliance test, use specific did:key"}'; + group('OIC4VC request credential', () { + const issuer = 'https://talao.co/issuer/zxhaokccsi'; - const selectedCredentials = [ - { + const credential = { 'format': 'jwt_vc', 'types': [ 'VerifiableCredential', 'VerifiableAttestation', - 'VerifiableDiploma', + 'VerifiableDiploma2', ], - } - ]; - const clientId = - 'did:key:zBhBLmYmyihtomRdJJNEKzbPj51o4a3GYFeZoRHSABKUwqdjiQPY2fjSoTWRD2nvVmaLnLHU8GXeGFmL1ed8ZAoPGoDeBMwcp4XFchNeTw917v2evpUjyst2gxZuRrVDSxDCb6G2Z1Tbz8kWHbjfUKgLVQd7CexS5GvPHfSQGdsLdjj4cNZvvZa'; - const webLink = 'https://app.altme.io/app/download/oidc4vc'; - const schema = - 'openid-credential-offer://?credential_offer_uri=https://talao.co/sandbox/ebsi/issuer/credential_offer_uri/a572f0a6-56a9-11ee-ac4f-0a1628958560'; - const issuerState = 'a53d709e-56a9-11ee-828d-0a1628958560'; - const nonce = '6044cc7d-2bd9-4804-82ae-c9950d8eedd8'; - const options = '[0]'; - const state = '4eee52c0-e524-4b62-a005-629e97f82dc5'; - const pkcePair = PkcePair( - 'l-NEmG-JlH-VwUxNoNmv8NPD47K_2Pu0hEY6tAHg9pE', - 'E0BAjRGdb3bspwyNsGnRDcV1zHp4CyCB7_2EQUsB4Ls', - ); - // test( - // 'given Url of openid request we return Uri for authentication endpoint', - // () async { - // final expectedAuthorizationEndpointdUri = Uri.parse( - // 'https://talao.co/sandbox/ebsi/issuer/pcbrwbvrsi/authorize?response_type=code&client_id=did%3Akey%3AzBhBLmYmyihtomRdJJNEKzbPj51o4a3GYFeZoRHSABKUwqdjiQPY2fjSoTWRD2nvVmaLnLHU8GXeGFmL1ed8ZAoPGoDeBMwcp4XFchNeTw917v2evpUjyst2gxZuRrVDSxDCb6G2Z1Tbz8kWHbjfUKgLVQd7CexS5GvPHfSQGdsLdjj4cNZvvZa&redirect_uri=https%3A%2F%2Fapp.altme.io%2Fapp%2Fdownload%2Foidc4vc%3Furi%3Dopenid-credential-offer%3A%2F%2F%3Fcredential_offer_uri%3Dhttps%3A%2F%2Ftalao.co%2Fsandbox%2Febsi%2Fissuer%2Fcredential_offer_uri%2Fa572f0a6-56a9-11ee-ac4f-0a1628958560%26code_verifier%3Dl-NEmG-JlH-VwUxNoNmv8NPD47K_2Pu0hEY6tAHg9pE%26options%3D%5B0%5D&scope=openid&issuer_state=a53d709e-56a9-11ee-828d-0a1628958560&state=4eee52c0-e524-4b62-a005-629e97f82dc5&nonce=6044cc7d-2bd9-4804-82ae-c9950d8eedd8&code_challenge=E0BAjRGdb3bspwyNsGnRDcV1zHp4CyCB7_2EQUsB4Ls&code_challenge_method=S256&authorization_details=%5B%7B%22type%22%3A%22openid_credential%22%2C%22locations%22%3A%5B%22https%3A%2F%2Ftalao.co%2Fsandbox%2Febsi%2Fissuer%2Fpcbrwbvrsi%22%5D%2C%22format%22%3A%22jwt_vc%22%2C%22types%22%3A%5B%22VerifiableCredential%22%2C%22VerifiableAttestation%22%2C%22VerifiableDiploma%22%5D%7D%5D&client_metadata=%7B%22authorization_endpoint%22%3A%22https%3A%2F%2Ftalao.co%2Fsandbox%2Febsi%2Fissuer%2Fpcbrwbvrsi%2Fauthorize%22%2C%22scopes_supported%22%3A%5B%22openid%22%5D%2C%22response_types_supported%22%3A%5B%22vp_token%22%2C%22id_token%22%5D%2C%22subject_types_supported%22%3A%5B%22public%22%5D%2C%22id_token_signing_alg_values_supported%22%3A%5B%22ES256%22%5D%2C%22request_object_signing_alg_values_supported%22%3A%5B%22ES256%22%5D%2C%22vp_formats_supported%22%3A%7B%22jwt_vp%22%3A%7B%22alg_values_supported%22%3A%5B%22ES256%22%5D%7D%2C%22jwt_vc%22%3A%7B%22alg_values_supported%22%3A%5B%22ES256%22%5D%7D%7D%2C%22subject_syntax_types_supported%22%3A%5B%22urn%3Aietf%3Aparams%3Aoauth%3Ajwk-thumbprint%22%2C%22did%F0%9F%94%91jwk_jcs-pub%22%5D%2C%22id_token_types_supported%22%3A%5B%22subject_signed_id_token%22%5D%7D'); - - // dioAdapter.onGet( - // issuer, - // (request) => request.reply(200, jsonDecode(openIdConfiguration)), - // ); - - // final oidc4vc = OIDC4VC(); - - // final authorizationEndpointdUri = - // await oidc4vc.getAuthorizationUriForIssuer( - // selectedCredentials: selectedCredentials, - // clientId: clientId, - // webLink: webLink, - // schema: schema, - // issuer: issuer, - // issuerState: issuerState, - // nonce: nonce, - // options: options, - // state: state, - // pkcePair: pkcePair, - // ); - - // expect( - // authorizationEndpointdUri.toString(), - // expectedAuthorizationEndpointdUri.toString(), - // ); - // }, - // ); - - test( - 'throw Exception with when request is not valid', - () async { - final oidc4vc = OIDC4VC(); - - // expect( - // () async => oidc4vc.getAuthorizationUriForIssuer( - // selectedCredentials: [], - // clientId: '', - // webLink: '', - // schema: '', - // issuer: '', - // issuerState: '', - // nonce: '', - // options: '', - // state: '', - // pkcePair: const PkcePair( - // '', - // '', - // ), - // ), - // throwsA( - // isA().having( - // (p0) => p0.toString(), - // 'toString()', - // 'Exception: Not a valid openid url to initiate issuance', - // ), - // ), - // ); - }, - ); + }; + + const did = + 'did:key:z2dmzD81cgPx8Vki7JbuuMmFYrWPgYoytykUZ3eyqht1j9Kbrbpg5is8LfTLuQ1RsW5r7s7ZjbDDFbDgy1tLrdc7Bj3itBGQkuGUQyfzKhFqbUNW2PqJPMSSzWoF2DGSvDSijCtJtYCSRsjSVLrwu5oHNbnPFvSEC4iRZPpU6B6nExRBTa'; + + const kid = + 'did:key:z2dmzD81cgPx8Vki7JbuuMmFYrWPgYoytykUZ3eyqht1j9Kbrbpg5is8LfTLuQ1RsW5r7s7ZjbDDFbDgy1tLrdc7Bj3itBGQkuGUQyfzKhFqbUNW2PqJPMSSzWoF2DGSvDSijCtJtYCSRsjSVLrwu5oHNbnPFvSEC4iRZPpU6B6nExRBTa#z2dmzD81cgPx8Vki7JbuuMmFYrWPgYoytykUZ3eyqht1j9Kbrbpg5is8LfTLuQ1RsW5r7s7ZjbDDFbDgy1tLrdc7Bj3itBGQkuGUQyfzKhFqbUNW2PqJPMSSzWoF2DGSvDSijCtJtYCSRsjSVLrwu5oHNbnPFvSEC4iRZPpU6B6nExRBTa'; + + const privateKey = + '{"kty":"EC","crv":"P-256","d":"amrwK13ZiYoJ5g0fc6MvXc86RB9ID8VuK_dMowU68FE","x":"fJQ2c9P_YDep3jzidwykcSlyoC4omqBvd9RHP1nz0cw","y":"K7VxrW-S1ONuX5cxrWIltF36ac1K8kj9as_o5cyc2zk"}'; + + const openIdConfiguration = + '{"authorization_server":"https://talao.co/issuer/zxhaokccsi","credential_endpoint":"https://talao.co/issuer/zxhaokccsi/credential","credential_issuer":"https://talao.co/issuer/zxhaokccsi","subject_syntax_types_supported":null,"token_endpoint":null,"batch_endpoint":null,"authorization_endpoint":null,"subject_trust_frameworks_supported":null,"credentials_supported":[{"display":[{"locale":"en-US","name":"EU Diploma","description":"This the official EBSI VC Diploma","text_color":"#FFFFFF","background_color":"#3B6F6D","background_image":{"url":"https://i.ibb.co/CHqjxrJ/dbc-card-hig-res.png","alt_text":"Connected open cubes in blue with one orange cube as a background of the card"},"logo":{"url":"https://dutchblockchaincoalition.org/assets/images/icons/Logo-DBC.png","alt_text":"An orange block shape, with the text Dutch Blockchain Coalition next to it, portraying the logo of the Dutch Blockchain Coalition."}}],"format":"jwt_vc","trust_framework":{"name":"ebsi","type":"Accreditation","uri":"TIR link towards accreditation"},"types":["VerifiableCredential","VerifiableAttestation","VerifiableDiploma2"],"id":null,"scope":null,"credentialSubject":{"dateOfBirth":{"display":[{"locale":"en-US","name":"Birth Date"},{"locale":"fr-FR","name":"Date de naissance"}]},"familyName":{"display":[{"locale":"en-US","name":"Family Name"},{"locale":"fr-FR","name":"Nom"}]},"givenNames":{"display":[{"locale":"en-US","name":"First Name"},{"locale":"fr-FR","name":"Prénom"}]}}},{"display":[{"locale":"en-US","name":"Individual attestation","description":"This is the EBSI Individual Verifiable Attestation","text_color":"#FFFFFF","background_color":"#3B6F6D","background_image":null,"logo":null}],"format":"jwt_vc","trust_framework":{"name":"ebsi","type":"Accreditation","uri":"TIR link towards accreditation"},"types":["VerifiableCredential","VerifiableAttestation","IndividualVerifiableAttestation"],"id":null,"scope":null,"credentialSubject":{"dateOfBirth":{"display":[{"locale":"en-US","name":"Birth Date"},{"locale":"fr-FR","name":"Date de naissance"}]},"familyName":{"display":[{"locale":"en-US","name":"Family Name"},{"locale":"fr-FR","name":"Nom"}]},"firstName":{"display":[{"locale":"en-US","name":"First Name"},{"locale":"fr-FR","name":"Prénom"}]},"issuing_country":{"display":[{"locale":"en-US","name":"Issued by"},{"locale":"fr-FR","name":"Délivré par"}]},"placeOfBirth":{"display":[{"locale":"en-US","name":"Birth Place"},{"locale":"fr-FR","name":"Lieu de naissance"}]}}},{"display":[{"locale":"en-GB","name":"Email proof","description":"This is a verifiable credential","text_color":null,"background_color":null,"background_image":null,"logo":null}],"format":"jwt_vc","trust_framework":{"name":"ebsi","type":"Accreditation","uri":"TIR link towards accreditation"},"types":["VerifiableCredential","EmailPass"],"id":null,"scope":null,"credentialSubject":null},{"display":[{"locale":"en-GB","name":"Verifiable Id","description":"This is a verifiable credential","text_color":null,"background_color":null,"background_image":null,"logo":null}],"format":"jwt_vc","trust_framework":{"name":"ebsi","type":"Accreditation","uri":"TIR link towards accreditation"},"types":["VerifiableCredential","VerifiableAttestation","VerifiableId"],"id":null,"scope":null,"credentialSubject":null}],"credential_configurations_supported":null,"deferred_credential_endpoint":"https://talao.co/issuer/zxhaokccsi/deferred","service_documentation":null,"credential_manifest":null,"credential_manifests":null,"issuer":null,"jwks_uri":null,"grant_types_supported":null}'; + + const accessToken = '0f0119c2-0867-11ef-8bfa-0a1628958560'; + + const nonce = '0f011beb-0867-11ef-817f-0a1628958560'; + + const credentialRequestUrl = + 'https://talao.co/issuer/zxhaokccsi/credential'; + + const expecedCredentialResponse = + '{"credential":"eyJhbGciOiJFUzI1NiIsImtpZCI6InFsM2g2Z3Jqem5iaGNSRzVPRWk3V1B6dHNkZ1FLaGhiLXBOU1laSWgtdk0iLCJ0eXAiOiJKV1QifQ.eyJleHAiOjE3NDYxODE3NDYsImlhdCI6MTcxNDY0NTc0NiwiaXNzIjoiaHR0cHM6Ly90YWxhby5jby9pc3N1ZXIvenhoYW9rY2NzaSIsImp0aSI6InVybjp1dWlkOmNkZGE4MWYyLTA4NmUtMTFlZi05ODE3LTBhMTYyODk1ODU2MCIsIm5iZiI6MTcxNDY0NTc0Niwibm9uY2UiOiJjOWZkMzJiYS0wODZlLTExZWYtOTQ5Yi0wYTE2Mjg5NTg1NjAiLCJzdWIiOiJkaWQ6a2V5OnoyZG16RDgxY2dQeDhWa2k3SmJ1dU1tRllyV1BnWW95dHlrVVozZXlxaHQxajlLYnJicGc1aXM4TGZUTHVRMVJzVzVyN3M3WmpiRERGYkRneTF0THJkYzdCajNpdEJHUWt1R1VReWZ6S2hGcWJVTlcyUHFKUE1TU3pXb0YyREdTdkRTaWpDdEp0WUNTUnNqU1ZMcnd1NW9ITmJuUEZ2U0VDNGlSWlBwVTZCNm5FeFJCVGEiLCJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSJdLCJjcmVkZW50aWFsU2NoZW1hIjp7ImlkIjoiaHR0cHM6Ly9hcGkucHJlcHJvZC5lYnNpLmV1L3RydXN0ZWQtc2NoZW1hcy1yZWdpc3RyeS92MS9zY2hlbWFzLzB4YmY3OGZjMDhhN2E5ZjI4ZjU0NzlmNThkZWEyNjlkMzY1N2Y1NGYxM2NhMzdkMzgwY2Q0ZTkyMjM3ZmI2OTFkZCIsInR5cGUiOiJKc29uU2NoZW1hVmFsaWRhdG9yMjAxOCJ9LCJjcmVkZW50aWFsU3RhdHVzIjpbeyJpZCI6Imh0dHBzOi8vdGFsYW8uY28vc2FuZGJveC9pc3N1ZXIvYml0c3RyaW5nc3RhdHVzbGlzdC8xIzczMDE5Iiwic3RhdHVzTGlzdENyZWRlbnRpYWwiOiJodHRwczovL3RhbGFvLmNvL3NhbmRib3gvaXNzdWVyL2JpdHN0cmluZ3N0YXR1c2xpc3QvMSIsInN0YXR1c0xpc3RJbmRleCI6IjczMDE5Iiwic3RhdHVzUHVycG9zZSI6InJldm9jYXRpb24iLCJ0eXBlIjoiQml0c3RyaW5nU3RhdHVzTGlzdEVudHJ5In1dLCJjcmVkZW50aWFsU3ViamVjdCI6eyJhd2FyZGluZ09wcG9ydHVuaXR5Ijp7ImF3YXJkaW5nQm9keSI6eyJlaWRhc0xlZ2FsSWRlbnRpZmllciI6IlVua25vd24iLCJob21lcGFnZSI6Imh0dHBzOi8vbGVhc3Rvbi5iY2RpcGxvbWEuY29tLyIsImlkIjoiZGlkOmVic2k6emRSdnZLYlhoVlZCc1hoYXRqdWlCaHMiLCJwcmVmZXJyZWROYW1lIjoiTGVhc3RvbiBVbml2ZXJzaXR5IiwicmVnaXN0cmF0aW9uIjoiMDU5NzA2NUoifSwiZW5kZWRBdFRpbWUiOiIyMDIwLTA2LTI2VDAwOjAwOjAwWiIsImlkIjoiaHR0cHM6Ly9sZWFzdG9uLmJjZGlwbG9tYS5jb20vbGF3LWVjb25vbWljcy1tYW5hZ2VtZW50I0F3YXJkaW5nT3Bwb3J0dW5pdHkiLCJpZGVudGlmaWVyIjoiaHR0cHM6Ly9jZXJ0aWZpY2F0ZS1kZW1vLmJjZGlwbG9tYS5jb20vY2hlY2svODdFRDJGMjI3MEU2QzQxNDU2RTk0Qjg2QjlEOTExNUI0RTM1QkNDQUQyMDBBNDlCODQ2NTkyQzE0Rjc5Qzg2QlYxRm5ibGx0YTBOWlRuSmtSM2xEV2xSbVREbFNSVUpFVkZaSVNtTm1ZekpoVVU1c1pVSjVaMkZKU0hwV2JtWloiLCJsb2NhdGlvbiI6IkZSQU5DRSIsInN0YXJ0ZWRBdFRpbWUiOiIyMDE5LTA5LTAyVDAwOjAwOjAwWiJ9LCJkYXRlT2ZCaXJ0aCI6IjE5OTMtMDQtMDgiLCJmYW1pbHlOYW1lIjoiRE9FIiwiZ2l2ZW5OYW1lcyI6IkphbmUiLCJncmFkaW5nU2NoZW1lIjp7ImlkIjoiaHR0cHM6Ly9sZWFzdG9uLmJjZGlwbG9tYS5jb20vbGF3LWVjb25vbWljcy1tYW5hZ2VtZW50I0dyYWRpbmdTY2hlbWUiLCJ0aXRsZSI6IjIgeWVhciBmdWxsLXRpbWUgcHJvZ3JhbW1lIC8gNCBzZW1lc3RlcnMifSwiaWRlbnRpZmllciI6IjA5MDQwMDgwODRIIiwibGVhcm5pbmdBY2hpZXZlbWVudCI6eyJhZGRpdGlvbmFsTm90ZSI6WyJESVNUUklCVVRJT04gTUFOQUdFTUVOVCJdLCJkZXNjcmlwdGlvbiI6IlRoZSBNYXN0ZXIgaW4gSW5mb3JtYXRpb24gYW5kIENvbXB1dGVyIFNjaWVuY2VzIChNSUNTKSBhdCB0aGUgVW5pdmVyc2l0eSBvZiBMdXhlbWJvdXJnIGVuYWJsZXMgc3R1ZGVudHMgdG8gYWNxdWlyZSBkZWVwZXIga25vd2xlZGdlIGluIGNvbXB1dGVyIHNjaWVuY2UgYnkgdW5kZXJzdGFuZGluZyBpdHMgYWJzdHJhY3QgYW5kIGludGVyZGlzY2lwbGluYXJ5IGZvdW5kYXRpb25zLCBmb2N1c2luZyBvbiBwcm9ibGVtIHNvbHZpbmcgYW5kIGRldmVsb3BpbmcgbGlmZWxvbmcgbGVhcm5pbmcgc2tpbGxzLiIsImlkIjoiaHR0cHM6Ly9sZWFzdG9uLmJjZGlwbG9tYS5jb20vbGF3LWVjb25vbWljcy1tYW5hZ2VtZW50I0xlYXJuaW5nQWNoaWV2bWVudCIsInRpdGxlIjoiTWFzdGVyIGluIEluZm9ybWF0aW9uIGFuZCBDb21wdXRlciBTY2llbmNlcyJ9LCJsZWFybmluZ1NwZWNpZmljYXRpb24iOnsiZWN0c0NyZWRpdFBvaW50cyI6MTIwLCJlcWZMZXZlbCI6NywiaWQiOiJodHRwczovL2xlYXN0b24uYmNkaXBsb21hLmNvbS9sYXctZWNvbm9taWNzLW1hbmFnZW1lbnQjTGVhcm5pbmdTcGVjaWZpY2F0aW9uIiwiaXNjZWRmQ29kZSI6WyI3Il0sIm5xZkxldmVsIjpbIjciXX0sInR5cGUiOiJWZXJpZmlhYmxlRGlwbG9tYTIifSwiZXZpZGVuY2UiOnsiZG9jdW1lbnRQcmVzZW5jZSI6WyJQaHlzaWNhbCJdLCJldmlkZW5jZURvY3VtZW50IjpbIlBhc3Nwb3J0Il0sImlkIjoiaHR0cHM6Ly9lc3NpZi5ldXJvcGEuZXUvdHNyLXZhL2V2aWRlbmNlL2YyYWVlYzk3LWZjMGQtNDJiZi04Y2E3LTA1NDgxOTJkNTY3OCIsInN1YmplY3RQcmVzZW5jZSI6IlBoeXNpY2FsIiwidHlwZSI6WyJEb2N1bWVudFZlcmlmaWNhdGlvbiJdLCJ2ZXJpZmllciI6ImRpZDplYnNpOjI5NjJmYjc4NGRmNjFiYWEyNjdjODEzMjQ5NzUzOWY4YzY3NGIzN2MxMjQ0YTdhIn0sInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiLCJWZXJpZmlhYmxlQXR0ZXN0YXRpb24iLCJWZXJpZmlhYmxlRGlwbG9tYSJdfX0.eU1nRdOMklOK6kKvJk-0iCdng5gXZ7quZV1ob_kr2c3_7wSsOEhlgikZzTkkZAOuxmkkdSnWRsGMoA0M4YEi1Q","c_nonce":"cddafe06-086e-11ef-b80b-0a1628958560","c_nonce_expires_in":5000,"format":"jwt_vc"}'; + dioAdapter.onPost( + credentialRequestUrl, + (request) => request.reply( + 200, + expecedCredentialResponse, + ), + ); + + test('When getCredentialType receive url it returns json response', + () async { + final (credentialResponseData, deferredCredentialEndpoint, format) = + await oidc4vc.getCredential( + issuer: issuer, + credential: credential, + did: did, + clientId: did, + kid: kid, + privateKey: privateKey, + cryptoHolderBinding: true, + clientType: ClientType.did, + proofHeaderType: ProofHeaderType.kid, + oidc4vciDraftType: OIDC4VCIDraftType.draft11, + clientAuthentication: ClientAuthentication.clientId, + proofType: ProofType.jwt, + openIdConfiguration: OpenIdConfiguration.fromJson( + jsonDecode(openIdConfiguration) as Map, + ), + accessToken: accessToken, + cnonce: nonce, + dio: client, + ); + + expect(credentialResponseData, [expecedCredentialResponse]); + expect( + deferredCredentialEndpoint, + 'https://talao.co/issuer/zxhaokccsi/deferred', + ); + expect(format, 'jwt_vc'); + }); + + test('throw Exception when token is not verified', () { + expect( + () async { + await oidc4vc.getCredential( + issuer: '', + credential: null, + did: '', + clientId: null, + kid: '', + privateKey: '', + cryptoHolderBinding: true, + clientType: ClientType.did, + proofHeaderType: ProofHeaderType.kid, + oidc4vciDraftType: OIDC4VCIDraftType.draft11, + clientAuthentication: ClientAuthentication.clientId, + proofType: ProofType.jwt, + openIdConfiguration: OpenIdConfiguration.fromJson( + jsonDecode(openIdConfiguration) as Map, + ), + accessToken: '', + cnonce: null, + dio: client, + ); + }, + throwsA(isA()), + ); + }); + }); + + group('build token data', () { + const redirectUri = 'https://app.altme.io/app/download/callback'; + const preAuthorizedCode = + 'eyJhbGciOiJFUzI1NiIsImtpZCI6ImRpZDplYnNpOjEyMzQja2V5LTEiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJodHRwczovL3RhbGFvLmNvL3NhbmRib3gvZWJzaS9pc3N1ZXIvenhoYW9rY2NzaSIsImNsaWVudF9pZCI6Imh0dHBzOi8vc2VsZi1pc3N1ZWQubWUvdjIiLCJleHAiOjE3MTQ2MzU4NDIsImlhdCI6MTcxNDYzNDg0MiwiaXNzIjoiaHR0cHM6Ly90YWxhby5jby9zYW5kYm94L2Vic2kvaXNzdWVyL3p4aGFva2Njc2kiLCJub25jZSI6IjZhMGJkZWUxLTA4NTUtMTFlZi04MzJlLTBhMTYyODk1ODU2MCIsInN1YiI6Imh0dHBzOi8vc2VsZi1pc3N1ZWQubWUvdjIifQ.ViX87lulUM6WZ0lNj5XMEz-Ty5q8nIcI7b-bIYa7VRsqo1wcR_en-8hzN_Q_sp8hqi8lKX80n4jM-DqXqvJk5g'; + + test('get token data with credentialRequestUri for preAuthorizedCode', + () async { + const clientId = + 'did:key:z2dmzD81cgPx8Vki7JbuuMmFYrWPgYoytykUZ3eyqht1j9Kbpog7BZb9wdCJCjHfWMTpjcviuoFJ2fd9AiwsWGMFvhNJ5gVMA2mzHSFqkrLMXdHNeePjiaTP15sw8uaWDfyAxehGHKj7YsxymgVnEhcEJgKsLRJHgJZXAiXJGyRxWPGEYC'; + + const expectedTokenData = + '{"pre-authorized_code":"eyJhbGciOiJFUzI1NiIsImtpZCI6ImRpZDplYnNpOjEyMzQja2V5LTEiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJodHRwczovL3RhbGFvLmNvL3NhbmRib3gvZWJzaS9pc3N1ZXIvenhoYW9rY2NzaSIsImNsaWVudF9pZCI6Imh0dHBzOi8vc2VsZi1pc3N1ZWQubWUvdjIiLCJleHAiOjE3MTQ2MzU4NDIsImlhdCI6MTcxNDYzNDg0MiwiaXNzIjoiaHR0cHM6Ly90YWxhby5jby9zYW5kYm94L2Vic2kvaXNzdWVyL3p4aGFva2Njc2kiLCJub25jZSI6IjZhMGJkZWUxLTA4NTUtMTFlZi04MzJlLTBhMTYyODk1ODU2MCIsInN1YiI6Imh0dHBzOi8vc2VsZi1pc3N1ZWQubWUvdjIifQ.ViX87lulUM6WZ0lNj5XMEz-Ty5q8nIcI7b-bIYa7VRsqo1wcR_en-8hzN_Q_sp8hqi8lKX80n4jM-DqXqvJk5g","grant_type":"urn:ietf:params:oauth:grant-type:pre-authorized_code","client_id":"did:key:z2dmzD81cgPx8Vki7JbuuMmFYrWPgYoytykUZ3eyqht1j9Kbpog7BZb9wdCJCjHfWMTpjcviuoFJ2fd9AiwsWGMFvhNJ5gVMA2mzHSFqkrLMXdHNeePjiaTP15sw8uaWDfyAxehGHKj7YsxymgVnEhcEJgKsLRJHgJZXAiXJGyRxWPGEYC"}'; + final tokenData = oidc4vc.buildTokenData( + redirectUri: redirectUri, + preAuthorizedCode: preAuthorizedCode, + clientId: clientId, + ); + + expect(jsonEncode(tokenData), expectedTokenData); + }); + + test('get token data with credentialRequestUri - authorization flow', () { + const clientId = + 'did:jwk:eyJjcnYiOiJQLTI1NiIsImt0eSI6IkVDIiwieCI6IjZka1U2Wk1GSzc5V3dpY3dKNXJieEUxM3pTdWtCWTJPb0VpVlVFanFNRWMiLCJ5IjoiUm5Iem55VmxyUFNNVDdpckRzMTVEOXd4Z01vamlTREFRcGZGaHFUa0xSWSJ9'; + const expectedTokenData = + '{"code":"6486b7c9-0858-11ef-a82c-0a1628958560","grant_type":"authorization_code","code_verifier":"qZNF2gMjTQf7pJN2NMai1TS9Y81z8xzfPQtbmyVG-Gk","redirect_uri":"https://app.altme.io/app/download/callback","client_id":"did:jwk:eyJjcnYiOiJQLTI1NiIsImt0eSI6IkVDIiwieCI6IjZka1U2Wk1GSzc5V3dpY3dKNXJieEUxM3pTdWtCWTJPb0VpVlVFanFNRWMiLCJ5IjoiUm5Iem55VmxyUFNNVDdpckRzMTVEOXd4Z01vamlTREFRcGZGaHFUa0xSWSJ9"}'; + final tokenData = oidc4vc.buildTokenData( + redirectUri: redirectUri, + clientId: clientId, + code: '6486b7c9-0858-11ef-a82c-0a1628958560', + codeVerifier: 'qZNF2gMjTQf7pJN2NMai1TS9Y81z8xzfPQtbmyVG-Gk', + ); + expect(jsonEncode(tokenData), expectedTokenData); + }); + }); + + group('getIssuer', () { + test('get issuer with credentialRequestUri', () async { + const clientId = + 'did:key:z2dmzD81cgPx8Vki7JbuuMmFYrWPgYoytykUZ3eyqht1j9Kbrbpg5is8LfTLuQ1RsW5r7s7ZjbDDFbDgy1tLrdc7Bj3itBGQkuGUQyfzKhFqbUNW2PqJPMSSzWoF2DGSvDSijCtJtYCSRsjSVLrwu5oHNbnPFvSEC4iRZPpU6B6nExRBTa'; + + const expectedIssuerJwt = + 'eyJhbGciOiJFUzI1NiIsInR5cCI6Im9wZW5pZDR2Y2ktcHJvb2Yrand0Iiwia2lkIjoidmNEV1FrRzBmbXhmRm90clhKZFRZMEhzQklobTQ2UDZYYzNqcXdVbjRVWSJ9.eyJpc3MiOiJkaWQ6a2V5OnoyZG16RDgxY2dQeDhWa2k3SmJ1dU1tRllyV1BnWW95dHlrVVozZXlxaHQxajlLYnJicGc1aXM4TGZUTHVRMVJzVzVyN3M3WmpiRERGYkRneTF0THJkYzdCajNpdEJHUWt1R1VReWZ6S2hGcWJVTlcyUHFKUE1TU3pXb0YyREdTdkRTaWpDdEp0WUNTUnNqU1ZMcnd1NW9ITmJuUEZ2U0VDNGlSWlBwVTZCNm5FeFJCVGEiLCJpYXQiOjE3MTQ3MTU1NDQsImF1ZCI6Imh0dHBzOi8vdGFsYW8uY28vaXNzdWVyL3p4aGFva2Njc2kiLCJub25jZSI6IjJkYTJkNTA2LTA5MTAtMTFlZi05ZTQ5LTBhMTYyODk1ODU2MCJ9.kmRv9RfhwGmAArHiqsZl7HkxE32vO9hyiVuI-lcMmpBPsgJ_eqPSXvkhSrIoUoKCtWAS3gaTT1hRZ5O0_fk9fA'; + + final tokenParameters = IssuerTokenParameters( + privateKey: { + 'kty': 'EC', + 'crv': 'P-256', + 'd': 'amrwK13ZiYoJ5g0fc6MvXc86RB9ID8VuK_dMowU68FE', + 'x': 'fJQ2c9P_YDep3jzidwykcSlyoC4omqBvd9RHP1nz0cw', + 'y': 'K7VxrW-S1ONuX5cxrWIltF36ac1K8kj9as_o5cyc2zk', + }, + did: clientId, + mediaType: MediaType.proofOfOwnership, + proofHeaderType: ProofHeaderType.kid, + clientType: ClientType.did, + clientId: clientId, + issuer: 'https://talao.co/issuer/zxhaokccsi', + ); + final issuerJwt = await oidc4vc.getIssuerJwt( + tokenParameters: tokenParameters, + clientAuthentication: ClientAuthentication.clientId, + iss: clientId, + cnonce: '2da2d506-0910-11ef-9e49-0a1628958560', + ); + + expect(issuerJwt.startsWith('ey'), expectedIssuerJwt.startsWith('ey')); + }); + }); + + test('get readTokenEndPoint with openidConfigurationResponse', () async { + const issuer = 'https://talao.co/issuer/zxhaokccsi'; + const openidConfigurationResponse = + '{"authorization_server":"https://talao.co/issuer/zxhaokccsi","credential_endpoint":"https://talao.co/issuer/zxhaokccsi/credential","credential_issuer":"https://talao.co/issuer/zxhaokccsi","subject_syntax_types_supported":null,"token_endpoint":null,"batch_endpoint":null,"authorization_endpoint":null,"subject_trust_frameworks_supported":null,"credentials_supported":[{"display":[{"locale":"en-US","name":"EU Diploma","description":"This the official EBSI VC Diploma","text_color":"#FFFFFF","background_color":"#3B6F6D","background_image":{"url":"https://i.ibb.co/CHqjxrJ/dbc-card-hig-res.png","alt_text":"Connected open cubes in blue with one orange cube as a background of the card"},"logo":{"url":"https://dutchblockchaincoalition.org/assets/images/icons/Logo-DBC.png","alt_text":"An orange block shape, with the text Dutch Blockchain Coalition next to it, portraying the logo of the Dutch Blockchain Coalition."}}],"format":"jwt_vc","trust_framework":{"name":"ebsi","type":"Accreditation","uri":"TIR link towards accreditation"},"types":["VerifiableCredential","VerifiableAttestation","VerifiableDiploma2"],"id":null,"scope":null,"credentialSubject":{"dateOfBirth":{"display":[{"locale":"en-US","name":"Birth Date"},{"locale":"fr-FR","name":"Date de naissance"}]},"familyName":{"display":[{"locale":"en-US","name":"Family Name"},{"locale":"fr-FR","name":"Nom"}]},"givenNames":{"display":[{"locale":"en-US","name":"First Name"},{"locale":"fr-FR","name":"Prénom"}]}}},{"display":[{"locale":"en-US","name":"Individual attestation","description":"This is the EBSI Individual Verifiable Attestation","text_color":"#FFFFFF","background_color":"#3B6F6D","background_image":null,"logo":null}],"format":"jwt_vc","trust_framework":{"name":"ebsi","type":"Accreditation","uri":"TIR link towards accreditation"},"types":["VerifiableCredential","VerifiableAttestation","IndividualVerifiableAttestation"],"id":null,"scope":null,"credentialSubject":{"dateOfBirth":{"display":[{"locale":"en-US","name":"Birth Date"},{"locale":"fr-FR","name":"Date de naissance"}]},"familyName":{"display":[{"locale":"en-US","name":"Family Name"},{"locale":"fr-FR","name":"Nom"}]},"firstName":{"display":[{"locale":"en-US","name":"First Name"},{"locale":"fr-FR","name":"Prénom"}]},"issuing_country":{"display":[{"locale":"en-US","name":"Issued by"},{"locale":"fr-FR","name":"Délivré par"}]},"placeOfBirth":{"display":[{"locale":"en-US","name":"Birth Place"},{"locale":"fr-FR","name":"Lieu de naissance"}]}}},{"display":[{"locale":"en-GB","name":"Email proof","description":"This is a verifiable credential","text_color":null,"background_color":null,"background_image":null,"logo":null}],"format":"jwt_vc","trust_framework":{"name":"ebsi","type":"Accreditation","uri":"TIR link towards accreditation"},"types":["VerifiableCredential","EmailPass"],"id":null,"scope":null,"credentialSubject":null},{"display":[{"locale":"en-GB","name":"Verifiable Id","description":"This is a verifiable credential","text_color":null,"background_color":null,"background_image":null,"logo":null}],"format":"jwt_vc","trust_framework":{"name":"ebsi","type":"Accreditation","uri":"TIR link towards accreditation"},"types":["VerifiableCredential","VerifiableAttestation","VerifiableId"],"id":null,"scope":null,"credentialSubject":null}],"credential_configurations_supported":null,"deferred_credential_endpoint":"https://talao.co/issuer/zxhaokccsi/deferred","service_documentation":null,"credential_manifest":null,"credential_manifests":null,"issuer":null,"jwks_uri":null,"grant_types_supported":null}'; + const openidConfigurationResponse2 = + '{"authorization_endpoint":"https://talao.co/issuer/zxhaokccsi/authorize","grant_types_supported":["authorization_code","urn:ietf:params:oauth:grant-type:pre-authorized_code"],"id_token_signing_alg_values_supported":["ES256","ES256K","EdDSA","RS256"],"id_token_types_supported":["subject_signed_id_token"],"jwks_uri":"https://talao.co/issuer/zxhaokccsi/jwks","pushed_authorization_request_endpoint":"https://talao.co/issuer/zxhaokccsi/authorize/par","request_authentication_methods_supported":{"authorization_endpoint":["request_object"]},"request_object_signing_alg_values_supported":["ES256","ES256K","EdDSA","RS256"],"request_parameter_supported":true,"request_uri_parameter_supported":true,"response_modes_supported":["query"],"response_types_supported":["vp_token","id_token"],"scopes_supported":["openid"],"subject_syntax_types_discriminations":["did:key:jwk_jcs-pub","did:ebsi:v1"],"subject_syntax_types_supported":["urn:ietf:params:oauth:jwk-thumbprint","did:key","did:ebsi","did:tz","did:pkh","did:hedera","did:key","did:ethr","did:web","did:jwk"],"subject_trust_frameworks_supported":["ebsi"],"subject_types_supported":["public","pairwise"],"token_endpoint":"https://talao.co/issuer/zxhaokccsi/token","token_endpoint_auth_methods_supported":["client_secret_basic","client_secret_post","client_secret_jwt","none"]}'; + + const expectedTokenEndpoint = 'https://talao.co/issuer/zxhaokccsi/token'; + + dioAdapter.onPost( + '$issuer/.well-known/openid-configuration', + (request) => request.reply( + 200, + jsonDecode(openidConfigurationResponse2), + ), + ); + + final tokenEndpoint = await oidc4vc.readTokenEndPoint( + dio: client, + issuer: issuer, + oidc4vciDraftType: OIDC4VCIDraftType.draft11, + openIdConfiguration: OpenIdConfiguration.fromJson( + jsonDecode(openidConfigurationResponse) as Map, + ), + secureStorage: mockSecureStorage, + ); + + expect(tokenEndpoint, expectedTokenEndpoint); + }); + + test('get issuer did with openidConfigurationResponse', () { + const openidConfigurationResponse = + r'{"authorization_endpoint":"https://talao.co/sandbox/ebsi/issuer/vgvghylozl/authorize","batch_credential_endpoint":null,"credential_endpoint":"https://talao.co/sandbox/ebsi/issuer/vgvghylozl/credential","credential_issuer":"https://talao.co/sandbox/ebsi/issuer/vgvghylozl","credential_manifests":[{"id":"VerifiableDiploma_1","issuer":{"id":"did:ebsi:zhSw5rPXkcHjvquwnVcTzzB","name":"Test EBSILUX"},"output_descriptors":[{"display":{"description":{"fallback":"This card is a proof that you passed this diploma successfully. You can use this card when you need to prove this information to services that have adopted EU EBSI framework.","path":[],"schema":{"type":"string"}},"properties":[{"fallback":"Unknown","label":"First name","path":["$.credentialSubject.firstName"],"schema":{"type":"string"}},{"fallback":"Unknown","label":"Last name","path":["$.credentialSubject.familyName"],"schema":{"type":"string"}},{"fallback":"Unknown","label":"Birth date","path":["$.credentialSubject.dateOfBirth"],"schema":{"format":"date","type":"string"}},{"fallback":"Unknown","label":"Grading scheme","path":["$.credentialSubject.gradingScheme.title"],"schema":{"type":"string"}},{"fallback":"Unknown","label":"Title","path":["$.credentialSubject.learningAchievement.title"],"schema":{"type":"string"}},{"fallback":"Unknown","label":"Description","path":["$.credentialSubject.learningAchievement.description"],"schema":{"type":"string"}},{"fallback":"Unknown","label":"ECTS Points","path":["$.credentialSubject.learningSpecification.ectsCreditPoints"],"schema":{"type":"number"}},{"fallback":"Unknown","label":"Issue date","path":["$.issuanceDate"],"schema":{"format":"date","type":"string"}},{"fallback":"Unknown","label":"Issued by","path":["$.credentialSubject.awardingOpportunity.awardingBody.preferredName"],"schema":{"type":"string"}},{"fallback":"Unknown","label":"Registration","path":["$.credentialSubject.awardingOpportunity.awardingBody.registration"],"schema":{"type":"string"}},{"fallback":"Unknown","label":"Website","path":["$.credentialSubject.awardingOpportunity.awardingBody.homepage"],"schema":{"format":"uri","type":"string"}}],"subtitle":{"fallback":"EBSI Verifiable diploma","path":[],"schema":{"type":"string"}},"title":{"fallback":"Diploma","path":[],"schema":{"type":"string"}}},"id":"diploma_01","schema":"https://api.preprod.oidc4vc.eu/trusted-schemas-registry/v1/schemas/0xbf78fc08a7a9f28f5479f58dea269d3657f54f13ca37d380cd4e92237fb691dd"}],"spec_version":"https://identity.foundation/credential-manifest/spec/v1.0.0/"}],"credential_supported":[{"cryptographic_binding_methods_supported":["did"],"cryptographic_suites_supported":["ES256K","ES256","ES384","ES512","RS256"],"display":[{"locale":"en-US","name":"Issuer Talao"}],"format":"jwt_vc","id":"VerifiableDiploma","types":"https://api.preprod.oidc4vc.eu/trusted-schemas-registry/v1/schemas/0xbf78fc08a7a9f28f5479f58dea269d3657f54f13ca37d380cd4e92237fb691dd"},{"id":"VerifiableDiploma_1","issuer":{"id":"did:ebsi:zhSw5rPXkcHjvquwnVcTzzB","name":"Test EBSILUX"},"output_descriptors":[{"display":{"description":{"fallback":"This card is a proof that you passed this diploma successfully. You can use this card when you need to prove this information to services that have adopted EU EBSI framework.","path":[],"schema":{"type":"string"}},"properties":[{"fallback":"Unknown","label":"First name","path":["$.credentialSubject.firstName"],"schema":{"type":"string"}},{"fallback":"Unknown","label":"Last name","path":["$.credentialSubject.familyName"],"schema":{"type":"string"}},{"fallback":"Unknown","label":"Birth date","path":["$.credentialSubject.dateOfBirth"],"schema":{"format":"date","type":"string"}},{"fallback":"Unknown","label":"Grading scheme","path":["$.credentialSubject.gradingScheme.title"],"schema":{"type":"string"}},{"fallback":"Unknown","label":"Title","path":["$.credentialSubject.learningAchievement.title"],"schema":{"type":"string"}},{"fallback":"Unknown","label":"Description","path":["$.credentialSubject.learningAchievement.description"],"schema":{"type":"string"}},{"fallback":"Unknown","label":"ECTS Points","path":["$.credentialSubject.learningSpecification.ectsCreditPoints"],"schema":{"type":"number"}},{"fallback":"Unknown","label":"Issue date","path":["$.issuanceDate"],"schema":{"format":"date","type":"string"}},{"fallback":"Unknown","label":"Issued by","path":["$.credentialSubject.awardingOpportunity.awardingBody.preferredName"],"schema":{"type":"string"}},{"fallback":"Unknown","label":"Registration","path":["$.credentialSubject.awardingOpportunity.awardingBody.registration"],"schema":{"type":"string"}},{"fallback":"Unknown","label":"Website","path":["$.credentialSubject.awardingOpportunity.awardingBody.homepage"],"schema":{"format":"uri","type":"string"}}],"subtitle":{"fallback":"EBSI Verifiable diploma","path":[],"schema":{"type":"string"}},"title":{"fallback":"Diploma","path":[],"schema":{"type":"string"}}},"id":"diploma_01","schema":"https://api.preprod.oidc4vc.eu/trusted-schemas-registry/v1/schemas/0xbf78fc08a7a9f28f5479f58dea269d3657f54f13ca37d380cd4e92237fb691dd"}],"spec_version":"https://identity.foundation/credential-manifest/spec/v1.0.0/"}],"credential_supported":[{"cryptographic_binding_methods_supported":["did"],"cryptographic_suites_supported":["ES256K","ES256","ES384","ES512","RS256"],"display":[{"locale":"en-US","name":"Issuer Talao"}],"format":"jwt_vc","id":"VerifiableDiploma","types":"https://api.preprod.oidc4vc.eu/trusted-schemas-registry/v1/schemas/0xbf78fc08a7a9f28f5479f58dea269d3657f54f13ca37d380cd4e92237fb691dd"}],"pre-authorized_grant_anonymous_access_supported":true,"subject_syntax_types_supported":["did:ebsi"],"token_endpoint":"https://talao.co/sandbox/ebsi/issuer/vgvghylozl/token"}'; + + final issuer = oidc4vc.readIssuerDid( + Response( + requestOptions: RequestOptions(), + data: jsonDecode(openidConfigurationResponse) as Map, + ), + ); + expect(issuer, 'did:ebsi:zhSw5rPXkcHjvquwnVcTzzB'); + }); + + test('get publicKey did with didDocumentResponse', () { + const didDocument = + '{"keys":[{"crv":"Ed25519","kid":"AegXN9J71CIQWw7TjhM-eHYZW45TfP0uC5xJduiH_w0","kty":"OKP","x":"FUoLewH4w4-KdaPH2cjZbL--CKYxQRWR05Yd_bIbhQo"}]}'; + const issuerDid = 'https://talao.co/sandbox/issuer/statuslist'; + const holderKid = 'AegXN9J71CIQWw7TjhM-eHYZW45TfP0uC5xJduiH_w0'; + + const expectedPublicKey = + '{"crv":"Ed25519","kid":"AegXN9J71CIQWw7TjhM-eHYZW45TfP0uC5xJduiH_w0","kty":"OKP","x":"FUoLewH4w4-KdaPH2cjZbL--CKYxQRWR05Yd_bIbhQo"}'; + + final publicKey = oidc4vc.readPublicKeyJwk( + issuer: issuerDid, + holderKid: holderKid, + didDocument: jsonDecode(didDocument) as Map, + ); + expect(jsonEncode(publicKey), expectedPublicKey); + }); + + group('verify encoded data', () { + const issuer = 'did:web:talao.co'; + const issuerKid1 = 'did:web:talao.co#key-2'; + const issuerKid2 = 'did:web:talao.co#key-222'; + const issuerKid3 = 'did:web:talao.co#key-21'; + const issuerKid4 = 'did:web:talao.co#key-3'; + + const universal = 'https://unires:test@unires.talao.co/1.0/identifiers'; + + const jwt = + 'eyJhbGciOiJFUzI1NiIsImtpZCI6ImRpZDp3ZWI6dGFsYW8uY28ja2V5LTIiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjE3NDYyNTUzMDYsImlhdCI6MTcxNDcxOTMwNiwiaXNzIjoiZGlkOndlYjp0YWxhby5jbyIsImp0aSI6InVybjp1dWlkOjEyZjdmNmI1LTA5MWEtMTFlZi04MWU1LTBhMTYyODk1ODU2MCIsIm5iZiI6MTcxNDcxOTMwNiwibm9uY2UiOiIxMWVhZTg1YS0wOTFhLTExZWYtODJkNC0wYTE2Mjg5NTg1NjAiLCJzdWIiOiJkaWQ6andrOmV5SmpjbllpT2lKUUxUSTFOaUlzSW10MGVTSTZJa1ZESWl3aWVDSTZJa1pIYTFScWFrUXhPR1l6VVRsc2FteG9WblEyVnpaMWJqSmtjbFJ1WjJoSU1XOVhSM1JRUmpOZk16Z2lMQ0o1SWpvaVkwWkNhWHBYZURoNE5rbE1XbWt5T1ZkRVFtNHpialU0WldWbGNra3ROVmMxWWxOeGVHaFFVR05RWXlKOSIsInZjIjp7IkBjb250ZXh0IjpbImh0dHBzOi8vd3d3LnczLm9yZy8yMDE4L2NyZWRlbnRpYWxzL3YxIl0sImNyZWRlbnRpYWxTY2hlbWEiOnsiaWQiOiJodHRwczovL2FwaS1jb25mb3JtYW5jZS5lYnNpLmV1L3RydXN0ZWQtc2NoZW1hcy1yZWdpc3RyeS92Mi9zY2hlbWFzL3oyMlpBTWRRdE5Md2k1MVQydmRaWEdHWmFZeWpyc3VQMXl6V3lYWmlyQ0FIdiIsInR5cGUiOiJGdWxsSnNvblNjaGVtYVZhbGlkYXRvcjIwMjEifSwiY3JlZGVudGlhbFN0YXR1cyI6W3siaWQiOiJodHRwczovL3RhbGFvLmNvL3NhbmRib3gvaXNzdWVyL2JpdHN0cmluZ3N0YXR1c2xpc3QvMSM1ODg5NyIsInN0YXR1c0xpc3RDcmVkZW50aWFsIjoiaHR0cHM6Ly90YWxhby5jby9zYW5kYm94L2lzc3Vlci9iaXRzdHJpbmdzdGF0dXNsaXN0LzEiLCJzdGF0dXNMaXN0SW5kZXgiOiI1ODg5NyIsInN0YXR1c1B1cnBvc2UiOiJyZXZvY2F0aW9uIiwidHlwZSI6IkJpdHN0cmluZ1N0YXR1c0xpc3RFbnRyeSJ9XSwiY3JlZGVudGlhbFN1YmplY3QiOnsiZGF0ZUlzc3VlZCI6IjIwMjItMTItMjAiLCJkYXRlT2ZCaXJ0aCI6IjE5MzAtMTAtMDEiLCJmYW1pbHlOYW1lIjoiQ2FzdGFmaW9yaSIsImZpcnN0TmFtZSI6IkJpYW5jYSIsImdlbmRlciI6IkYiLCJ0eXBlIjoiVmVyaWZpYWJsZUlkIn0sImV4cGlyYXRpb25EYXRlIjoiMjAyNS0wNS0wM1QwNjo1NDo1M1oiLCJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIiwiVmVyaWZpYWJsZUlkIl0sInZhbGlkRnJvbSI6IjIwMjUtMDUtMDNUMDY6NTQ6NTNaIn19.5DJwR_gUbu-GDpSF7hwXcpmHg-wYmKU_AxOvR4Psimefk0H4JUbX803svm3QhxIK2i4GgMhRWmgqhvML_x7nTw'; + + const didDocument = + r'{"@context":"https://w3id.org/did-resolution/v1","didDocument":{"@context":["https://www.w3.org/ns/did/v1",{"@id":"https://w3id.org/security#publicKeyJwk","@type":"@json"}],"id":"did:web:talao.co","verificationMethod":[{"id":"did:web:talao.co#key-1","type":"JwsVerificationKey2020","controller":"did:web:talao.co","publicKeyJwk":{"e":"AQAB","kid":"did:web:talao.co#key-1","kty":"RSA","n":"pPocyKreTAn3YrmGyPYXHklYqUiSSQirGACwJSYYs-ksfw4brtA3SZCmA2sdAO8a2DXfqADwFgVSxJFtJ3GkHLV2ZvOIOnZCX6MF6NIWHB9c64ydrYNJbEy72oyG_-v-sE6rb0x-D-uJe9DFYIURzisyBlNA7imsiZPQniOjPLv0BUgED0vdO5HijFe7XbpVhoU-2oTkHHQ4CadmBZhelCczACkXpOU7mwcImGj9h1__PsyT5VBLi_92-93NimZjechPaaTYEU2u0rfnfVW5eGDYNAynO4Q2bhpFPRTXWZ5Lhnhnq7M76T6DGA3GeAu_MOzB0l4dxpFMJ6wHnekdkQ"}},{"id":"did:web:talao.co#key-2","type":"JwsVerificationKey2020","controller":"did:web:talao.co","publicKeyJwk":{"crv":"P-256","kty":"EC","x":"Bls7WaGu_jsharYBAzakvuSERIV_IFR2tS64e5p_Y_Q","y":"haeKjXQ9uzyK4Ind1W4SBUkR_9udjjx1OmKK4vl1jko"}},{"id":"did:web:talao.co#key-21","type":"JwsVerificationKey2020","controller":"did:web:talao.co","publicKeyJwk":{"crv":"P-256","kty":"EC","x":"J4vQtLUyrVUiFIXRrtEq4xurmBZp2eq9wJmXkIA_stI","y":"HNh3aF4zQsnf_sFXUVaSzrQF85veDoVxhPQ-163wUYM"}},{"id":"did:web:talao.co#key-3","type":"JwsVerificationKey2020","controller":"did:web:talao.co","publicKeyJwk":{"crv":"Ed25519","kty":"OKP","x":"FUoLewH4w4-KdaPH2cjZbL--CKYxQRWR05Yd_bIbhQo"}},{"id":"did:web:talao.co#key-4","type":"Ed25519VerificationKey2018","controller":"did:web:talao.co","publicKeyBase58":"2S73k5pn5umfnaW31qx6dXFndEn6SmWw7LpgSjNNC5BF"}],"authentication":["did:web:talao.co#key-1","did:web:talao.co#key-2","did:web:talao.co#key-3","did:web:talao.co#key-4"],"assertionMethod":["did:web:talao.co#key-1","did:web:talao.co#key-2","did:web:talao.co#key-3","did:web:talao.co#key-4"],"keyAgreement":["did:web:talao.co#key-3","did:web:talao.co#key-4"],"capabilityInvocation":["did:web:talao.co#key-1","did:web:talao.co#key-4"],"service":[{"id":"did:web:talao.co#domain-1","type":"LinkedDomains","serviceEndpoint":"https://talao.co"}]},"didResolutionMetadata":{"contentType":"application/did+ld+json","pattern":"^(did:web:.+)$","driverUrl":"http://uni-resolver-driver-did-uport:8081/1.0/identifiers/","duration":42,"did":{"didString":"did:web:talao.co","methodSpecificId":"talao.co","method":"web"}},"didDocumentMetadata":{}}'; + + dioAdapter.onGet( + '$universal/$issuer', + (request) => request.reply(200, jsonDecode(didDocument)), + ); + + test('returns VerificationType.verified', () async { + final isVerified = await oidc4vc.verifyEncodedData( + issuer: issuer, + jwt: jwt, + issuerKid: issuerKid1, + dio: client, + fromStatusList: false, + isCachingEnabled: false, + publicJwk: null, + ); + expect(isVerified, VerificationType.verified); + }); + + test('returns VerificationType.unKnown', () async { + final isVerified = await oidc4vc.verifyEncodedData( + issuer: issuer, + jwt: jwt, + issuerKid: issuerKid2, + dio: client, + fromStatusList: false, + isCachingEnabled: false, + publicJwk: null, + ); + expect(isVerified, VerificationType.unKnown); + }); + + test('returns VerificationType.notVerified', () async { + const vcJwt = + 'eyJhbGciOiJIUzI1NiJ9.eyJSb2xlIjoiQWRtaW4iLCJJc3N1ZXIiOiJJc3N1ZXIiLCJVc2VybmFtZSI6IkphdmFJblVzZSIsImV4cCI6MTcxNDcyMjY3NiwiaWF0IjoxNzE0NzIyNjc2fQ.mVQhu1VmyA6LlcA77NmdhUvTrOoawL_VxhrMzkkh7BE'; + final isVerified = await oidc4vc.verifyEncodedData( + issuer: issuer, + jwt: vcJwt, + issuerKid: issuerKid3, + dio: client, + fromStatusList: false, + isCachingEnabled: false, + publicJwk: null, + ); + expect(isVerified, VerificationType.notVerified); + }); + + test('returns VerificationType.notVerified for OKP', () async { + const vcJwt = 'eyJraWQiOiItMTkwOTU3MjI1NyIsImFsZyI6IkVkRFNBIn0.' + 'eyJqdGkiOiIyMjkxNmYzYy05MDkzLTQ4MTMtODM5Ny1mMTBlNmI3MDRiNjgiLCJkZWxlZ2F0aW9uSWQiOiJiNGFlNDdhNy02MjVhLTQ2MzAtOTcyNy00NTc2NGE3MTJjY2UiLCJleHAiOjE2NTUyNzkxMDksIm5iZiI6MTY1NTI3ODgwOSwic2NvcGUiOiJyZWFkIG9wZW5pZCIsImlzcyI6Imh0dHBzOi8vaWRzdnIuZXhhbXBsZS5jb20iLCJzdWIiOiJ1c2VybmFtZSIsImF1ZCI6ImFwaS5leGFtcGxlLmNvbSIsImlhdCI6MTY1NTI3ODgwOSwicHVycG9zZSI6ImFjY2Vzc190b2tlbiJ9.' + 'rjeE8D_e4RYzgvpu-nOwwx7PWMiZyDZwkwO6RiHR5t8g4JqqVokUKQt-oST1s45wubacfeDSFogOrIhe3UHDAg'; + + final isVerified = await oidc4vc.verifyEncodedData( + issuer: issuer, + jwt: vcJwt, + issuerKid: issuerKid4, + dio: client, + fromStatusList: false, + isCachingEnabled: false, + publicJwk: null, + ); + expect(isVerified, VerificationType.notVerified); + }); + }); + + group('get token', () { + const tokenEndPoint = 'https://talao.co/issuer/zarbjrqrzj/token'; + const tokenData = { + 'pre-authorized_code': '5cdf5508-0923-11ef-90b1-0a1628958560', + 'grant_type': 'urn:ietf:params:oauth:grant-type:pre-authorized_code', + 'client_id': + 'did:key:zDnaeoAcB8wFcSWqLeiJbCg663C3qAKkEfuuTM9rGWx2NFWCt', + 'user_pin': '4444', + 'tx_code': '4444', + }; + + test('get correct token ', () async { + const response = + '{"access_token":"ac9e48a7-0923-11ef-95a3-0a1628958560","c_nonce":"ac9e4a9c-0923-11ef-a47c-0a1628958560","token_type":"Bearer","expires_in":10000,"c_nonce_expires_in":1704466725,"refresh_token":"ac9e49f2-0923-11ef-91ef-0a1628958560"}'; + + dioAdapter.onPost( + tokenEndPoint, + (request) => request.reply(200, jsonDecode(response)), + ); + + const expectedValue = + '{"access_token":"ac9e48a7-0923-11ef-95a3-0a1628958560","c_nonce":"ac9e4a9c-0923-11ef-a47c-0a1628958560","token_type":"Bearer","expires_in":10000,"c_nonce_expires_in":1704466725,"refresh_token":"ac9e49f2-0923-11ef-91ef-0a1628958560"}'; + final token = await oidc4vc.getToken( + dio: client, + tokenData: tokenData, + tokenEndPoint: tokenEndPoint, + authorization: null, + ); + + expect(jsonEncode(token), expectedValue); + }); + + test('throw expection when invalid value is sent', () async { + expect( + () async { + dioAdapter.onPost( + tokenEndPoint, + (request) => request.throws( + 401, + DioException( + requestOptions: RequestOptions(path: tokenEndPoint), + ), + ), + ); + + await oidc4vc.getToken( + dio: client, + tokenData: tokenData, + tokenEndPoint: tokenEndPoint, + authorization: null, + ); + }, + throwsA(isA()), + ); + }); + }); - test( - 'given correct authorization request parameter', - () async { - const openIdConfigurationResponse = - '{"authorization_server":"https://talao.co/sandbox/ebsi/issuer/pcbrwbvrsi/authorize_server","credential_endpoint":"https://talao.co/sandbox/ebsi/issuer/pcbrwbvrsi/credential","credential_issuer":"https://talao.co/sandbox/ebsi/issuer/pcbrwbvrsi","credentials_supported":[{"display":[{"locale":"en-GB","name":"Verifiable diploma"}],"format":"jwt_vc","trust_framework":{"name":"ebsi","type":"Accreditation","uri":"TIR link towards accreditation"},"types":["VerifiableCredential","VerifiableAttestation","VerifiableDiploma"]},{"display":[{"locale":"en-GB","name":"Email proof"}],"format":"jwt_vc","trust_framework":{"name":"ebsi","type":"Accreditation","uri":"TIR link towards accreditation"},"types":["VerifiableCredential","EmailPass"]},{"display":[{"locale":"en-GB","name":"Verifiable Id"}],"format":"jwt_vc","id":"VerifiableId","trust_framework":{"name":"ebsi","type":"Accreditation","uri":"TIR link towards accreditation"},"types":["VerifiableCredential","VerifiableAttestation","VerifiableId"]}],"deferred_credential_endpoint":"https://talao.co/sandbox/ebsi/issuer/pcbrwbvrsi/deferred","service_documentation":"New environment for V3 compliance test, use specific did:key"}'; - const expectedAuthorizationRequestParemeters = - r'{"response_type":"code","client_id":"did:key:zBhBLmYmyihtomRdJJNEKzbPj51o4a3GYFeZoRHSABKUwqdjiQPY2fjSoTWRD2nvVmaLnLHU8GXeGFmL1ed8ZAoPGoDeBMwcp4XFchNeTw917v2evpUjyst2gxZuRrVDSxDCb6G2Z1Tbz8kWHbjfUKgLVQd7CexS5GvPHfSQGdsLdjj4cNZvvZa","redirect_uri":"https://app.altme.io/app/download/oidc4vc?uri=openid-credential-offer://?credential_offer_uri=https://talao.co/sandbox/ebsi/issuer/credential_offer_uri/a572f0a6-56a9-11ee-ac4f-0a1628958560&code_verifier=l-NEmG-JlH-VwUxNoNmv8NPD47K_2Pu0hEY6tAHg9pE&options=[0]","scope":"openid","issuer_state":"a53d709e-56a9-11ee-828d-0a1628958560","state":"4eee52c0-e524-4b62-a005-629e97f82dc5","nonce":"6044cc7d-2bd9-4804-82ae-c9950d8eedd8","code_challenge":"E0BAjRGdb3bspwyNsGnRDcV1zHp4CyCB7_2EQUsB4Ls","code_challenge_method":"S256","authorization_details":"[{\"type\":\"openid_credential\",\"locations\":[\"https://talao.co/sandbox/ebsi/issuer/pcbrwbvrsi\"],\"format\":\"jwt_vc\",\"types\":[\"VerifiableCredential\",\"VerifiableAttestation\",\"VerifiableDiploma\"]}]","client_metadata":"{\"authorization_endpoint\":\"https://talao.co/sandbox/ebsi/issuer/pcbrwbvrsi/authorize\",\"scopes_supported\":[\"openid\"],\"response_types_supported\":[\"vp_token\",\"id_token\"],\"subject_types_supported\":[\"public\"],\"id_token_signing_alg_values_supported\":[\"ES256\"],\"request_object_signing_alg_values_supported\":[\"ES256\"],\"vp_formats_supported\":{\"jwt_vp\":{\"alg_values_supported\":[\"ES256\"]},\"jwt_vc\":{\"alg_values_supported\":[\"ES256\"]}},\"subject_syntax_types_supported\":[\"urn:ietf:params:oauth:jwk-thumbprint\",\"did🔑jwk_jcs-pub\"],\"id_token_types_supported\":[\"subject_signed_id_token\"]}"}'; - final oidc4vc = OIDC4VC(); - - // final authorizationRequestParemeters = - // oidc4vc.getAuthorizationRequestParemeters( - // selectedCredentials: selectedCredentials, - // clientId: clientId, - // webLink: webLink, - // schema: schema, - // issuer: issuer, - // issuerState: issuerState, - // nonce: nonce, - // options: options, - // state: state, - // pkcePair: pkcePair, - // openidConfigurationResponse: - // jsonDecode(openIdConfigurationResponse) as Map, - // ); - - // expect( - // jsonEncode(authorizationRequestParemeters), - // expectedAuthorizationRequestParemeters, - // ); + group( + 'get did document', + () { + test('get corresponding did document', () async { + const issuer = 'did:web:talao.co'; + const universal = + 'https://unires:test@unires.talao.co/1.0/identifiers'; + + const didDocument = + r'{"@context":"https://w3id.org/did-resolution/v1","didDocument":{"@context":["https://www.w3.org/ns/did/v1",{"@id":"https://w3id.org/security#publicKeyJwk","@type":"@json"}],"id":"did:web:talao.co","verificationMethod":[{"id":"did:web:talao.co#key-1","type":"JwsVerificationKey2020","controller":"did:web:talao.co","publicKeyJwk":{"e":"AQAB","kid":"did:web:talao.co#key-1","kty":"RSA","n":"pPocyKreTAn3YrmGyPYXHklYqUiSSQirGACwJSYYs-ksfw4brtA3SZCmA2sdAO8a2DXfqADwFgVSxJFtJ3GkHLV2ZvOIOnZCX6MF6NIWHB9c64ydrYNJbEy72oyG_-v-sE6rb0x-D-uJe9DFYIURzisyBlNA7imsiZPQniOjPLv0BUgED0vdO5HijFe7XbpVhoU-2oTkHHQ4CadmBZhelCczACkXpOU7mwcImGj9h1__PsyT5VBLi_92-93NimZjechPaaTYEU2u0rfnfVW5eGDYNAynO4Q2bhpFPRTXWZ5Lhnhnq7M76T6DGA3GeAu_MOzB0l4dxpFMJ6wHnekdkQ"}},{"id":"did:web:talao.co#key-2","type":"JwsVerificationKey2020","controller":"did:web:talao.co","publicKeyJwk":{"crv":"P-256","kty":"EC","x":"Bls7WaGu_jsharYBAzakvuSERIV_IFR2tS64e5p_Y_Q","y":"haeKjXQ9uzyK4Ind1W4SBUkR_9udjjx1OmKK4vl1jko"}},{"id":"did:web:talao.co#key-21","type":"JwsVerificationKey2020","controller":"did:web:talao.co","publicKeyJwk":{"crv":"P-256","kty":"EC","x":"J4vQtLUyrVUiFIXRrtEq4xurmBZp2eq9wJmXkIA_stI","y":"HNh3aF4zQsnf_sFXUVaSzrQF85veDoVxhPQ-163wUYM"}},{"id":"did:web:talao.co#key-3","type":"JwsVerificationKey2020","controller":"did:web:talao.co","publicKeyJwk":{"crv":"Ed25519","kty":"OKP","x":"FUoLewH4w4-KdaPH2cjZbL--CKYxQRWR05Yd_bIbhQo"}},{"id":"did:web:talao.co#key-4","type":"Ed25519VerificationKey2018","controller":"did:web:talao.co","publicKeyBase58":"2S73k5pn5umfnaW31qx6dXFndEn6SmWw7LpgSjNNC5BF"}],"authentication":["did:web:talao.co#key-1","did:web:talao.co#key-2","did:web:talao.co#key-3","did:web:talao.co#key-4"],"assertionMethod":["did:web:talao.co#key-1","did:web:talao.co#key-2","did:web:talao.co#key-3","did:web:talao.co#key-4"],"keyAgreement":["did:web:talao.co#key-3","did:web:talao.co#key-4"],"capabilityInvocation":["did:web:talao.co#key-1","did:web:talao.co#key-4"],"service":[{"id":"did:web:talao.co#domain-1","type":"LinkedDomains","serviceEndpoint":"https://talao.co"}]},"didResolutionMetadata":{"contentType":"application/did+ld+json","pattern":"^(did:web:.+)$","driverUrl":"http://uni-resolver-driver-did-uport:8081/1.0/identifiers/","duration":42,"did":{"didString":"did:web:talao.co","methodSpecificId":"talao.co","method":"web"}},"didDocumentMetadata":{}}'; + + dioAdapter.onGet( + '$universal/$issuer', + (request) => request.reply(200, jsonDecode(didDocument)), + ); + + final value = await oidc4vc.getDidDocument( + didKey: issuer, + fromStatusList: false, + isCachingEnabled: false, + dio: client, + ); + + expect(value, jsonDecode(didDocument)); + }); + + test( + 'use public resolver as fallback and get corresponding did document', + () async { + const issuer = 'did:web:talao.co'; + const universal = + 'https://unires:test@unires.talao.co/1.0/identifiers'; + + const public = 'https://dev.uniresolver.io/1.0/identifiers'; + + const didDocument = + r'{"@context":"https://w3id.org/did-resolution/v1","didDocument":{"@context":["https://www.w3.org/ns/did/v1",{"@id":"https://w3id.org/security#publicKeyJwk","@type":"@json"}],"id":"did:web:talao.co","verificationMethod":[{"id":"did:web:talao.co#key-1","type":"JwsVerificationKey2020","controller":"did:web:talao.co","publicKeyJwk":{"e":"AQAB","kid":"did:web:talao.co#key-1","kty":"RSA","n":"pPocyKreTAn3YrmGyPYXHklYqUiSSQirGACwJSYYs-ksfw4brtA3SZCmA2sdAO8a2DXfqADwFgVSxJFtJ3GkHLV2ZvOIOnZCX6MF6NIWHB9c64ydrYNJbEy72oyG_-v-sE6rb0x-D-uJe9DFYIURzisyBlNA7imsiZPQniOjPLv0BUgED0vdO5HijFe7XbpVhoU-2oTkHHQ4CadmBZhelCczACkXpOU7mwcImGj9h1__PsyT5VBLi_92-93NimZjechPaaTYEU2u0rfnfVW5eGDYNAynO4Q2bhpFPRTXWZ5Lhnhnq7M76T6DGA3GeAu_MOzB0l4dxpFMJ6wHnekdkQ"}},{"id":"did:web:talao.co#key-2","type":"JwsVerificationKey2020","controller":"did:web:talao.co","publicKeyJwk":{"crv":"P-256","kty":"EC","x":"Bls7WaGu_jsharYBAzakvuSERIV_IFR2tS64e5p_Y_Q","y":"haeKjXQ9uzyK4Ind1W4SBUkR_9udjjx1OmKK4vl1jko"}},{"id":"did:web:talao.co#key-21","type":"JwsVerificationKey2020","controller":"did:web:talao.co","publicKeyJwk":{"crv":"P-256","kty":"EC","x":"J4vQtLUyrVUiFIXRrtEq4xurmBZp2eq9wJmXkIA_stI","y":"HNh3aF4zQsnf_sFXUVaSzrQF85veDoVxhPQ-163wUYM"}},{"id":"did:web:talao.co#key-3","type":"JwsVerificationKey2020","controller":"did:web:talao.co","publicKeyJwk":{"crv":"Ed25519","kty":"OKP","x":"FUoLewH4w4-KdaPH2cjZbL--CKYxQRWR05Yd_bIbhQo"}},{"id":"did:web:talao.co#key-4","type":"Ed25519VerificationKey2018","controller":"did:web:talao.co","publicKeyBase58":"2S73k5pn5umfnaW31qx6dXFndEn6SmWw7LpgSjNNC5BF"}],"authentication":["did:web:talao.co#key-1","did:web:talao.co#key-2","did:web:talao.co#key-3","did:web:talao.co#key-4"],"assertionMethod":["did:web:talao.co#key-1","did:web:talao.co#key-2","did:web:talao.co#key-3","did:web:talao.co#key-4"],"keyAgreement":["did:web:talao.co#key-3","did:web:talao.co#key-4"],"capabilityInvocation":["did:web:talao.co#key-1","did:web:talao.co#key-4"],"service":[{"id":"did:web:talao.co#domain-1","type":"LinkedDomains","serviceEndpoint":"https://talao.co"}]},"didResolutionMetadata":{"contentType":"application/did+ld+json","pattern":"^(did:web:.+)$","driverUrl":"http://uni-resolver-driver-did-uport:8081/1.0/identifiers/","duration":42,"did":{"didString":"did:web:talao.co","methodSpecificId":"talao.co","method":"web"}},"didDocumentMetadata":{}}'; + + dioAdapter + ..onGet( + '$universal/$issuer', + (request) => request.throws( + 500, + DioException.connectionError( + requestOptions: RequestOptions(path: '$universal/$issuer'), + reason: 'Internal Server Error', + ), + ), + ) + ..onGet( + '$public/$issuer', + (request) => request.reply(200, jsonDecode(didDocument)), + ); + + final value = await oidc4vc.getDidDocument( + didKey: issuer, + fromStatusList: false, + isCachingEnabled: false, + dio: client, + ); + + expect(value, jsonDecode(didDocument)); + }); + test('get corresponding did document', () async { + const issuer = 'https://talao.co/issuer/zxhaokccsi'; + const openidConfigurationResponse1 = + '{"authorization_server":"https://talao.co/issuer/zxhaokccsi","credential_endpoint":"https://talao.co/issuer/zxhaokccsi/credential","credential_issuer":"https://talao.co/issuer/zxhaokccsi","subject_syntax_types_supported":null,"token_endpoint":null,"batch_endpoint":null,"authorization_endpoint":null,"subject_trust_frameworks_supported":null,"credentials_supported":[{"display":[{"locale":"en-US","name":"EU Diploma","description":"This the official EBSI VC Diploma","text_color":"#FFFFFF","background_color":"#3B6F6D","background_image":{"url":"https://i.ibb.co/CHqjxrJ/dbc-card-hig-res.png","alt_text":"Connected open cubes in blue with one orange cube as a background of the card"},"logo":{"url":"https://dutchblockchaincoalition.org/assets/images/icons/Logo-DBC.png","alt_text":"An orange block shape, with the text Dutch Blockchain Coalition next to it, portraying the logo of the Dutch Blockchain Coalition."}}],"format":"jwt_vc","trust_framework":{"name":"ebsi","type":"Accreditation","uri":"TIR link towards accreditation"},"types":["VerifiableCredential","VerifiableAttestation","VerifiableDiploma2"],"id":null,"scope":null,"credentialSubject":{"dateOfBirth":{"display":[{"locale":"en-US","name":"Birth Date"},{"locale":"fr-FR","name":"Date de naissance"}]},"familyName":{"display":[{"locale":"en-US","name":"Family Name"},{"locale":"fr-FR","name":"Nom"}]},"givenNames":{"display":[{"locale":"en-US","name":"First Name"},{"locale":"fr-FR","name":"Prénom"}]}}},{"display":[{"locale":"en-US","name":"Individual attestation","description":"This is the EBSI Individual Verifiable Attestation","text_color":"#FFFFFF","background_color":"#3B6F6D","background_image":null,"logo":null}],"format":"jwt_vc","trust_framework":{"name":"ebsi","type":"Accreditation","uri":"TIR link towards accreditation"},"types":["VerifiableCredential","VerifiableAttestation","IndividualVerifiableAttestation"],"id":null,"scope":null,"credentialSubject":{"dateOfBirth":{"display":[{"locale":"en-US","name":"Birth Date"},{"locale":"fr-FR","name":"Date de naissance"}]},"familyName":{"display":[{"locale":"en-US","name":"Family Name"},{"locale":"fr-FR","name":"Nom"}]},"firstName":{"display":[{"locale":"en-US","name":"First Name"},{"locale":"fr-FR","name":"Prénom"}]},"issuing_country":{"display":[{"locale":"en-US","name":"Issued by"},{"locale":"fr-FR","name":"Délivré par"}]},"placeOfBirth":{"display":[{"locale":"en-US","name":"Birth Place"},{"locale":"fr-FR","name":"Lieu de naissance"}]}}},{"display":[{"locale":"en-GB","name":"Email proof","description":"This is a verifiable credential","text_color":null,"background_color":null,"background_image":null,"logo":null}],"format":"jwt_vc","trust_framework":{"name":"ebsi","type":"Accreditation","uri":"TIR link towards accreditation"},"types":["VerifiableCredential","EmailPass"],"id":null,"scope":null,"credentialSubject":null},{"display":[{"locale":"en-GB","name":"Verifiable Id","description":"This is a verifiable credential","text_color":null,"background_color":null,"background_image":null,"logo":null}],"format":"jwt_vc","trust_framework":{"name":"ebsi","type":"Accreditation","uri":"TIR link towards accreditation"},"types":["VerifiableCredential","VerifiableAttestation","VerifiableId"],"id":null,"scope":null,"credentialSubject":null}],"credential_configurations_supported":null,"deferred_credential_endpoint":"https://talao.co/issuer/zxhaokccsi/deferred","service_documentation":null,"credential_manifest":null,"credential_manifests":null,"issuer":null,"jwks_uri":null,"grant_types_supported":null}'; + const openidConfigurationResponse2 = + '{"authorization_server":null,"credential_endpoint":null,"credential_issuer":null,"subject_syntax_types_supported":null,"token_endpoint":null,"batch_endpoint":null,"authorization_endpoint":null,"subject_trust_frameworks_supported":null,"credentials_supported":null,"credential_configurations_supported":null,"deferred_credential_endpoint":null,"service_documentation":null,"credential_manifest":null,"credential_manifests":null,"issuer":"https://talao.co/sandbox/issuer/statuslist","jwks_uri":"https://talao.co/sandbox/issuer/statuslist/jwks","grant_types_supported":null}'; + const jwkUriResponse = + '{"keys":[{"crv":"Ed25519","kid":"AegXN9J71CIQWw7TjhM-eHYZW45TfP0uC5xJduiH_w0","kty":"OKP","x":"FUoLewH4w4-KdaPH2cjZbL--CKYxQRWR05Yd_bIbhQo"}]}'; + + const expectedDidDocument = + '{"keys":[{"crv":"Ed25519","kid":"AegXN9J71CIQWw7TjhM-eHYZW45TfP0uC5xJduiH_w0","kty":"OKP","x":"FUoLewH4w4-KdaPH2cjZbL--CKYxQRWR05Yd_bIbhQo"}]}'; + + dioAdapter + ..onGet( + '$issuer/.well-known/openid-credential-issuer', + (request) => + request.reply(200, jsonDecode(openidConfigurationResponse1)), + ) + ..onGet( + '$issuer/.well-known/openid-configuration', + (request) => + request.reply(200, jsonDecode(openidConfigurationResponse2)), + ) + ..onGet( + 'https://talao.co/sandbox/issuer/statuslist/jwks', + (request) => request.reply(200, jsonDecode(jwkUriResponse)), + ); + + final value = await oidc4vc.getDidDocument( + didKey: issuer, + fromStatusList: false, + isCachingEnabled: false, + dio: client, + secureStorage: mockSecureStorage, + ); + expect(value, jsonDecode(expectedDidDocument)); + }); }, ); }); - //group('OIC4VC request credential', () { - // final credentialRequest = Uri.parse( - // 'https://app.altme.io/app/download?credential_type=https://api.preprod.oidc4vc.eu/trusted-schemas-registry/v1/schemas/0xbf78fc08a7a9f28f5479f58dea269d3657f54f13ca37d380cd4e92237fb691dd&issuer=https://talao.co/sandbox/ebsi/issuer/vgvghylozl?code=cb803d46-9c88-11ed-bdb3-0a1628958560&state=0d189873-9c87-11ed-8dbf-0a1628958560', - // ); - - // final credentialRequestWithPreAuthorizedCode = Uri.parse( - // 'openid://initiate_issuance?issuer=https%3A%2F%2Ftalao.co%2Fsandbox%2Febsi%2Fissuer%2Fvgvghylozl&credential_type=https%3A%2F%2Fapi.preprod.oidc4vc.eu%2Ftrusted-schemas-registry%2Fv1%2Fschemas%2F0xbf78fc08a7a9f28f5479f58dea269d3657f54f13ca37d380cd4e92237fb691dd&op_state=test&pre-authorized_code=ff8e73c5-ae07-11ed-b1f7-0a1628958560&user_pin_required=False', - // ); - - // const issuerResponse = - // r'{"authorization_endpoint":"https://talao.co/sandbox/ebsi/issuer/vgvghylozl/authorize","batch_credential_endpoint":null,"credential_endpoint":"https://talao.co/sandbox/ebsi/issuer/vgvghylozl/credential","credential_issuer":"https://talao.co/sandbox/ebsi/issuer/vgvghylozl","credential_manifests":[{"id":"VerifiableDiploma_1","issuer":{"id":"did:ebsi:zhSw5rPXkcHjvquwnVcTzzB","name":"Test EBSILUX"},"output_descriptors":[{"display":{"description":{"fallback":"This card is a proof that you passed this diploma successfully. You can use this card when you need to prove this information to services that have adopted EU EBSI framework.","path":[],"schema":{"type":"string"}},"properties":[{"fallback":"Unknown","label":"First name","path":["$.credentialSubject.firstName"],"schema":{"type":"string"}},{"fallback":"Unknown","label":"Last name","path":["$.credentialSubject.familyName"],"schema":{"type":"string"}},{"fallback":"Unknown","label":"Birth date","path":["$.credentialSubject.dateOfBirth"],"schema":{"format":"date","type":"string"}},{"fallback":"Unknown","label":"Grading scheme","path":["$.credentialSubject.gradingScheme.title"],"schema":{"type":"string"}},{"fallback":"Unknown","label":"Title","path":["$.credentialSubject.learningAchievement.title"],"schema":{"type":"string"}},{"fallback":"Unknown","label":"Description","path":["$.credentialSubject.learningAchievement.description"],"schema":{"type":"string"}},{"fallback":"Unknown","label":"ECTS Points","path":["$.credentialSubject.learningSpecification.ectsCreditPoints"],"schema":{"type":"number"}},{"fallback":"Unknown","label":"Issue date","path":["$.issuanceDate"],"schema":{"format":"date","type":"string"}},{"fallback":"Unknown","label":"Issued by","path":["$.credentialSubject.awardingOpportunity.awardingBody.preferredName"],"schema":{"type":"string"}},{"fallback":"Unknown","label":"Registration","path":["$.credentialSubject.awardingOpportunity.awardingBody.registration"],"schema":{"type":"string"}},{"fallback":"Unknown","label":"Website","path":["$.credentialSubject.awardingOpportunity.awardingBody.homepage"],"schema":{"format":"uri","type":"string"}}],"subtitle":{"fallback":"EBSI Verifiable diploma","path":[],"schema":{"type":"string"}},"title":{"fallback":"Diploma","path":[],"schema":{"type":"string"}}},"id":"diploma_01","schema":"https://api.preprod.oidc4vc.eu/trusted-schemas-registry/v1/schemas/0xbf78fc08a7a9f28f5479f58dea269d3657f54f13ca37d380cd4e92237fb691dd"}],"spec_version":"https://identity.foundation/credential-manifest/spec/v1.0.0/"}],"credential_supported":[{"cryptographic_binding_methods_supported":["did"],"cryptographic_suites_supported":["ES256K","ES256","ES384","ES512","RS256"],"display":[{"locale":"en-US","name":"Issuer Talao"}],"format":"jwt_vc","id":"VerifiableDiploma","types":"https://api.preprod.oidc4vc.eu/trusted-schemas-registry/v1/schemas/0xbf78fc08a7a9f28f5479f58dea269d3657f54f13ca37d380cd4e92237fb691dd"}],"pre-authorized_grant_anonymous_access_supported":true,"subject_syntax_types_supported":["did:ebsi"],"token_endpoint":"https://talao.co/sandbox/ebsi/issuer/vgvghylozl/token"}'; - - // const tokenResponse = - // '{"access_token":"7a07dd19-a879-11ed-ad95-0a1628958560","c_nonce":"7a07de0f-a879-11ed-822b-0a1628958560","token_type":"Bearer","expires_in":1000}'; // ignore: lines_longer_than_80_chars - - // const credentialRequestUrl = - // 'https://talao.co/sandbox/ebsi/issuer/vgvghylozl/credential'; - - // const credentialRequestResponse = - // '{"format":"jwt_vc","credential":"eyJhbGciOiJFUzI1NksiLCJraWQiOiJkaWQ6ZWJzaTp6aFN3NXJQWGtjSGp2cXV3blZjVHp6QiM1MTVhOWM0MzZjMGYyYWQzYWI2NWQ2Y2VmYzVjMWYwNmMwNWI4YWRmY2Y1NGVlMDZkYzgwNTQzMjA0NzBmZmFmIiwidHlwIjoiSldUIn0.eyJleHAiOjE2NzU5NDcwNTguODkyNzc4LCJpYXQiOjE2NzU5NDYwNTguODkyNzcxLCJpc3MiOiJkaWQ6ZWJzaTp6aFN3NXJQWGtjSGp2cXV3blZjVHp6QiIsImp0aSI6InVybjp1dWlkOjZiMWQ4NDExLTllZDUtNDU2Ni05YzdmLTRjMjQxNjVmZjIzNiIsIm5iZiI6MTY3NTk0NjA1OC44OTI3NzYsIm5vbmNlIjoiMTFmNWY2MDAtYTg3Ni0xMWVkLTkwZjItMGExNjI4OTU4NTYwIiwic3ViIjoiZGlkOmVic2k6em94UkdWWlFuZFRmUWs1NEI3dEtkd3dOZGhhaTVnbTlGOE5hdjhlY2VOQUJhIiwidmMiOnsiQGNvbnRleHQiOlsiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvdjEiXSwiY3JlZGVudGlhbFNjaGVtYSI6eyJpZCI6Imh0dHBzOi8vYXBpLnByZXByb2QuZWJzaS5ldS90cnVzdGVkLXNjaGVtYXMtcmVnaXN0cnkvdjEvc2NoZW1hcy8weGJmNzhmYzA4YTdhOWYyOGY1NDc5ZjU4ZGVhMjY5ZDM2NTdmNTRmMTNjYTM3ZDM4MGNkNGU5MjIzN2ZiNjkxZGQiLCJ0eXBlIjoiSnNvblNjaGVtYVZhbGlkYXRvcjIwMTgifSwiY3JlZGVudGlhbFN0YXR1cyI6eyJpZCI6Imh0dHBzOi8vZXNzaWYuZXVyb3BhLmV1L3N0YXR1cy9lZHVjYXRpb24jaGlnaGVyRWR1Y2F0aW9uIzM5MmFjN2Y2LTM5OWEtNDM3Yi1hMjY4LTQ2OTFlYWQ4ZjE3NiIsInR5cGUiOiJDcmVkZW50aWFsU3RhdHVzTGlzdDIwMjAifSwiY3JlZGVudGlhbFN1YmplY3QiOnsiYXdhcmRpbmdPcHBvcnR1bml0eSI6eyJhd2FyZGluZ0JvZHkiOnsiZWlkYXNMZWdhbElkZW50aWZpZXIiOiJVbmtub3duIiwiaG9tZXBhZ2UiOiJodHRwczovL2xlYXN0b24uYmNkaXBsb21hLmNvbS8iLCJpZCI6ImRpZDplYnNpOnpkUnZ2S2JYaFZWQnNYaGF0anVpQmhzIiwicHJlZmVycmVkTmFtZSI6IkxlYXN0b24gVW5pdmVyc2l0eSIsInJlZ2lzdHJhdGlvbiI6IjA1OTcwNjVKIn0sImVuZGVkQXRUaW1lIjoiMjAyMC0wNi0yNlQwMDowMDowMFoiLCJpZCI6Imh0dHBzOi8vbGVhc3Rvbi5iY2RpcGxvbWEuY29tL2xhdy1lY29ub21pY3MtbWFuYWdlbWVudCNBd2FyZGluZ09wcG9ydHVuaXR5IiwiaWRlbnRpZmllciI6Imh0dHBzOi8vY2VydGlmaWNhdGUtZGVtby5iY2RpcGxvbWEuY29tL2NoZWNrLzg3RUQyRjIyNzBFNkM0MTQ1NkU5NEI4NkI5RDkxMTVCNEUzNUJDQ0FEMjAwQTQ5Qjg0NjU5MkMxNEY3OUM4NkJWMUZuYmxsdGEwTlpUbkprUjNsRFdsUm1URGxTUlVKRVZGWklTbU5tWXpKaFVVNXNaVUo1WjJGSlNIcFdibVpaIiwibG9jYXRpb24iOiJGUkFOQ0UiLCJzdGFydGVkQXRUaW1lIjoiMjAxOS0wOS0wMlQwMDowMDowMFoifSwiZGF0ZU9mQmlydGgiOiIxOTkzLTA0LTA4IiwiZmFtaWx5TmFtZSI6IkRPRSIsImdpdmVuTmFtZXMiOiJKYW5lIiwiZ3JhZGluZ1NjaGVtZSI6eyJpZCI6Imh0dHBzOi8vbGVhc3Rvbi5iY2RpcGxvbWEuY29tL2xhdy1lY29ub21pY3MtbWFuYWdlbWVudCNHcmFkaW5nU2NoZW1lIiwidGl0bGUiOiIyIHllYXIgZnVsbC10aW1lIHByb2dyYW1tZSAvIDQgc2VtZXN0ZXJzIn0sImlkIjoiZGlkOmVic2k6em94UkdWWlFuZFRmUWs1NEI3dEtkd3dOZGhhaTVnbTlGOE5hdjhlY2VOQUJhIiwiaWRlbnRpZmllciI6IjA5MDQwMDgwODRIIiwibGVhcm5pbmdBY2hpZXZlbWVudCI6eyJhZGRpdGlvbmFsTm90ZSI6WyJESVNUUklCVVRJT04gTUFOQUdFTUVOVCJdLCJkZXNjcmlwdGlvbiI6IlRoZSBNYXN0ZXIgaW4gSW5mb3JtYXRpb24gYW5kIENvbXB1dGVyIFNjaWVuY2VzIChNSUNTKSBhdCB0aGUgVW5pdmVyc2l0eSBvZiBMdXhlbWJvdXJnIGVuYWJsZXMgc3R1ZGVudHMgdG8gYWNxdWlyZSBkZWVwZXIga25vd2xlZGdlIGluIGNvbXB1dGVyIHNjaWVuY2UgYnkgdW5kZXJzdGFuZGluZyBpdHMgYWJzdHJhY3QgYW5kIGludGVyZGlzY2lwbGluYXJ5IGZvdW5kYXRpb25zLCBmb2N1c2luZyBvbiBwcm9ibGVtIHNvbHZpbmcgYW5kIGRldmVsb3BpbmcgbGlmZWxvbmcgbGVhcm5pbmcgc2tpbGxzLiIsImlkIjoiaHR0cHM6Ly9sZWFzdG9uLmJjZGlwbG9tYS5jb20vbGF3LWVjb25vbWljcy1tYW5hZ2VtZW50I0xlYXJuaW5nQWNoaWV2bWVudCIsInRpdGxlIjoiTWFzdGVyIGluIEluZm9ybWF0aW9uIGFuZCBDb21wdXRlciBTY2llbmNlcyJ9LCJsZWFybmluZ1NwZWNpZmljYXRpb24iOnsiZWN0c0NyZWRpdFBvaW50cyI6MTIwLCJlcWZMZXZlbCI6NywiaWQiOiJodHRwczovL2xlYXN0b24uYmNkaXBsb21hLmNvbS9sYXctZWNvbm9taWNzLW1hbmFnZW1lbnQjTGVhcm5pbmdTcGVjaWZpY2F0aW9uIiwiaXNjZWRmQ29kZSI6WyI3Il0sIm5xZkxldmVsIjpbIjciXX19LCJldmlkZW5jZSI6eyJkb2N1bWVudFByZXNlbmNlIjpbIlBoeXNpY2FsIl0sImV2aWRlbmNlRG9jdW1lbnQiOlsiUGFzc3BvcnQiXSwiaWQiOiJodHRwczovL2Vzc2lmLmV1cm9wYS5ldS90c3ItdmEvZXZpZGVuY2UvZjJhZWVjOTctZmMwZC00MmJmLThjYTctMDU0ODE5MmQ1Njc4Iiwic3ViamVjdFByZXNlbmNlIjoiUGh5c2ljYWwiLCJ0eXBlIjpbIkRvY3VtZW50VmVyaWZpY2F0aW9uIl0sInZlcmlmaWVyIjoiZGlkOmVic2k6Mjk2MmZiNzg0ZGY2MWJhYTI2N2M4MTMyNDk3NTM5ZjhjNjc0YjM3YzEyNDRhN2EifSwiaWQiOiJ1cm46dXVpZDo2YjFkODQxMS05ZWQ1LTQ1NjYtOWM3Zi00YzI0MTY1ZmYyMzYiLCJpc3N1YW5jZURhdGUiOiIyMDIzLTAyLTA5VDEyOjM0OjE4WiIsImlzc3VlZCI6IjIwMjMtMDItMDlUMTI6MzQ6MThaIiwiaXNzdWVyIjoiZGlkOmVic2k6emhTdzVyUFhrY0hqdnF1d25WY1R6ekIiLCJwcm9vZiI6eyJjcmVhdGVkIjoiMjAyMi0wNC0yN1QxMjoyNTowN1oiLCJjcmVhdG9yIjoiZGlkOmVic2k6emRSdnZLYlhoVlZCc1hoYXRqdWlCaHMiLCJkb21haW4iOiJodHRwczovL2FwaS5wcmVwcm9kLmVic2kuZXUiLCJqd3MiOiJleUppTmpRaU9tWmhiSE5sTENKamNtbDBJanBiSW1JMk5DSmRMQ0poYkdjaU9pSkZVekkxTmtzaWZRLi5tSUJuTThYRFFxU1lLUU5YX0x2YUpobXNieUNyNU9aNWNVMlprLVJlcUxwcjRkb0ZzZ21vb2JrTzUxMjh0WnktOEtpbVZqSmtHdzB3TDF1QlduTUxXUSIsIm5vbmNlIjoiM2VhNjhkYWUtZDA3YS00ZGFhLTkzMmItZmJiNThmNWMyMGM0IiwidHlwZSI6IkVjZHNhU2VjcDI1NmsxU2lnbmF0dXJlMjAxOSJ9LCJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIiwiVmVyaWZpYWJsZUF0dGVzdGF0aW9uIiwiVmVyaWZpYWJsZURpcGxvbWEiXSwidmFsaWRGcm9tIjoiMjAyMy0wMi0wOVQxMjozNDoxOFoifX0.uQK9sK-VtqmKjLJIw_v5Ff5xAMDAosZCtl1LFYxZhUolReD6a7O-NI1f5lcswBCZPLfJ-HJyb5iShehHObzFDA","c_nonce":"128952c8-a876-11ed-bbc4-0a1628958560","c_nonce_expires_in":1000}'; // ignore: lines_longer_than_80_chars - - // dioAdapter - // ..onGet( - // issuer, - // (request) => request.reply(200, jsonDecode(issuerResponse)), - // ) - // ..onPost( - // 'tokenUrl', - // (request) => request.reply( - // 200, - // jsonDecode(tokenResponse), - // ), - // ) - // ..onPost( - // credentialRequestUrl, - // (request) => request.reply( - // 200, - // jsonDecode(credentialRequestResponse), - // ), - // ); - - // test('When getCredentialType receive url it returns json response', - // () async { - // final oidc4vc = OIDC4VC(client); - // final credential = await oidc4vc.getCredential( - // credentialRequest, - // mnemonic, - // null, - // ); - // expect(jsonEncode(credential), credentialRequestResponse); - // }); - - //}); - // test('throw Exception when token is not verified', () { - // const issuerDid = 'did:ebsi:zhSw5rPXkcHjvquwnVcTzzB'; - - // const didDocumentUrl = - // 'https://api-pilot.oidc4vc.eu/did-registry/v3/identifiers/$issuerDid'; - - // const didDocumentResponse = - // '{"assertionMethod":["did:ebsi:zeFCExU2XAAshYkPCpjuahA#3623b877bbb24b08ba390f3585418f53"],"authentication":["did:ebsi:zeFCExU2XAAshYkPCpjuahA#3623b877bbb24b08ba390f3585418f53"],"@context":"https://www.w3.org/ns/did/v1","id":"did:ebsi:zeFCExU2XAAshYkPCpjuahA","verificationMethod":[{"controller":"did:ebsi:zeFCExU2XAAshYkPCpjuahA","id":"did:ebsi:zeFCExU2XAAshYkPCpjuahA#3623b877bbb24b08ba390f3585418f53","publicKeyJwk":{"crv":"P-521","kty":"EC","x":"AekpBQ8ST8a8VcfVOTNl353vSrDCLLJXmPk06wTjxrrjcBpXp5EOnYG_NjFZ6OvLFV1jSfS9tsz4qUxcWceqwQGk","y":"ADSmRA43Z1DSNx_RvcLI87cdL07l6jQyyBXMoxVg_l2Th-x3S1WDhjDly79ajL4Kkd0AZMaZmh9ubmf63e3kyMj2"},"type":"Ed25519VerificationKey2019"}]}'; - - // dioAdapter.onGet( - // didDocumentUrl, - // (request) => request.reply(200, jsonDecode(didDocumentResponse)), - // ); - - // final oidc4vc = OIDC4VC(client); - - // expect( - // () async { - // await oidc4vc.getCredential( - // credentialRequest, - // mnemonic, - // null, - // ); - // }, - // throwsA( - // isA().having( - // (p0) => p0.toString(), - // 'toString()', - // 'Exception: VERIFICATION_ISSUE', - // ), - // ), - // ); - // }); - - // group('build token data', () { - // final oidc4vc = OIDC4VC(client); - // test('get token data with credentialRequestUri', () async { - // const expectedTokenData = - // '{"code":"cb803d46-9c88-11ed-bdb3-0a1628958560","grant_type":"authorization_code"}'; // ignore: lines_longer_than_80_chars - // final tokenData = oidc4vc.buildTokenData( - // credentialRequest, - // ); - // expect(jsonEncode(tokenData), expectedTokenData); - // }); - - // test('get token data with credentialRequestUri', () { - // const expectedTokenData = - // '{"pre-authorized_code":"ff8e73c5-ae07-11ed-b1f7-0a1628958560","grant_type":"urn:ietf:params:oauth:grant-type:pre-authorized_code"}'; // ignore: lines_longer_than_80_chars - // final tokenData = - // oidc4vc.buildTokenData(credentialRequestWithPreAuthorizedCode); - // expect(jsonEncode(tokenData), expectedTokenData); - // }); - // }); - - // group('getIssuer', () { - // final oidc4vc = OIDC4VC(client); - // test('get issuer with credentialRequestUri', () { - // const expectedIssuer = - // 'https://talao.co/sandbox/ebsi/issuer/vgvghylozl'; - // final issuer = oidc4vc.getIssuer(credentialRequest); - // expect(expectedIssuer, issuer); - // }); - - // test( - // 'get issuer with credentialRequestUri when PreAuthorizedCode is given', // ignore: lines_longer_than_80_chars - // () { - // const expectedIssuer = - // 'https://talao.co/sandbox/ebsi/issuer/vgvghylozl'; - // final issuer = - // oidc4vc.getIssuer(credentialRequestWithPreAuthorizedCode); - // expect(expectedIssuer, issuer); - // }); - // }); - - // test('get readTokenEndPoint with openidConfigurationResponse', () async { - // const openidConfigurationResponse = - // r'{"authorization_endpoint":"https://talao.co/sandbox/ebsi/issuer/vgvghylozl/authorize","batch_credential_endpoint":null,"credential_endpoint":"https://talao.co/sandbox/ebsi/issuer/vgvghylozl/credential","credential_issuer":"https://talao.co/sandbox/ebsi/issuer/vgvghylozl","credential_manifests":[{"id":"VerifiableDiploma_1","issuer":{"id":"did:ebsi:zhSw5rPXkcHjvquwnVcTzzB","name":"Test EBSILUX"},"output_descriptors":[{"display":{"description":{"fallback":"This card is a proof that you passed this diploma successfully. You can use this card when you need to prove this information to services that have adopted EU EBSI framework.","path":[],"schema":{"type":"string"}},"properties":[{"fallback":"Unknown","label":"First name","path":["$.credentialSubject.firstName"],"schema":{"type":"string"}},{"fallback":"Unknown","label":"Last name","path":["$.credentialSubject.familyName"],"schema":{"type":"string"}},{"fallback":"Unknown","label":"Birth date","path":["$.credentialSubject.dateOfBirth"],"schema":{"format":"date","type":"string"}},{"fallback":"Unknown","label":"Grading scheme","path":["$.credentialSubject.gradingScheme.title"],"schema":{"type":"string"}},{"fallback":"Unknown","label":"Title","path":["$.credentialSubject.learningAchievement.title"],"schema":{"type":"string"}},{"fallback":"Unknown","label":"Description","path":["$.credentialSubject.learningAchievement.description"],"schema":{"type":"string"}},{"fallback":"Unknown","label":"ECTS Points","path":["$.credentialSubject.learningSpecification.ectsCreditPoints"],"schema":{"type":"number"}},{"fallback":"Unknown","label":"Issue date","path":["$.issuanceDate"],"schema":{"format":"date","type":"string"}},{"fallback":"Unknown","label":"Issued by","path":["$.credentialSubject.awardingOpportunity.awardingBody.preferredName"],"schema":{"type":"string"}},{"fallback":"Unknown","label":"Registration","path":["$.credentialSubject.awardingOpportunity.awardingBody.registration"],"schema":{"type":"string"}},{"fallback":"Unknown","label":"Website","path":["$.credentialSubject.awardingOpportunity.awardingBody.homepage"],"schema":{"format":"uri","type":"string"}}],"subtitle":{"fallback":"EBSI Verifiable diploma","path":[],"schema":{"type":"string"}},"title":{"fallback":"Diploma","path":[],"schema":{"type":"string"}}},"id":"diploma_01","schema":"https://api.preprod.oidc4vc.eu/trusted-schemas-registry/v1/schemas/0xbf78fc08a7a9f28f5479f58dea269d3657f54f13ca37d380cd4e92237fb691dd"}],"spec_version":"https://identity.foundation/credential-manifest/spec/v1.0.0/"}],"credential_supported":[{"cryptographic_binding_methods_supported":["did"],"cryptographic_suites_supported":["ES256K","ES256","ES384","ES512","RS256"],"display":[{"locale":"en-US","name":"Issuer Talao"}],"format":"jwt_vc","id":"VerifiableDiploma","types":"https://api.preprod.oidc4vc.eu/trusted-schemas-registry/v1/schemas/0xbf78fc08a7a9f28f5479f58dea269d3657f54f13ca37d380cd4e92237fb691dd"}],"pre-authorized_grant_anonymous_access_supported":true,"subject_syntax_types_supported":["did:ebsi"],"token_endpoint":"https://talao.co/sandbox/ebsi/issuer/vgvghylozl/token"}'; - - // final oidc4vc = OIDC4VC(client); - - // final issuer = oidc4vc.readTokenEndPoint( - // Response( - // requestOptions: RequestOptions(path: ''), - // data: jsonDecode(openidConfigurationResponse) as Map, - // ), - // ); - // expect(issuer, tokenUrl); - // }); - - // test('get issuer did with openidConfigurationResponse', () { - // const openidConfigurationResponse = - // r'{"authorization_endpoint":"https://talao.co/sandbox/ebsi/issuer/vgvghylozl/authorize","batch_credential_endpoint":null,"credential_endpoint":"https://talao.co/sandbox/ebsi/issuer/vgvghylozl/credential","credential_issuer":"https://talao.co/sandbox/ebsi/issuer/vgvghylozl","credential_manifests":[{"id":"VerifiableDiploma_1","issuer":{"id":"did:ebsi:zhSw5rPXkcHjvquwnVcTzzB","name":"Test EBSILUX"},"output_descriptors":[{"display":{"description":{"fallback":"This card is a proof that you passed this diploma successfully. You can use this card when you need to prove this information to services that have adopted EU EBSI framework.","path":[],"schema":{"type":"string"}},"properties":[{"fallback":"Unknown","label":"First name","path":["$.credentialSubject.firstName"],"schema":{"type":"string"}},{"fallback":"Unknown","label":"Last name","path":["$.credentialSubject.familyName"],"schema":{"type":"string"}},{"fallback":"Unknown","label":"Birth date","path":["$.credentialSubject.dateOfBirth"],"schema":{"format":"date","type":"string"}},{"fallback":"Unknown","label":"Grading scheme","path":["$.credentialSubject.gradingScheme.title"],"schema":{"type":"string"}},{"fallback":"Unknown","label":"Title","path":["$.credentialSubject.learningAchievement.title"],"schema":{"type":"string"}},{"fallback":"Unknown","label":"Description","path":["$.credentialSubject.learningAchievement.description"],"schema":{"type":"string"}},{"fallback":"Unknown","label":"ECTS Points","path":["$.credentialSubject.learningSpecification.ectsCreditPoints"],"schema":{"type":"number"}},{"fallback":"Unknown","label":"Issue date","path":["$.issuanceDate"],"schema":{"format":"date","type":"string"}},{"fallback":"Unknown","label":"Issued by","path":["$.credentialSubject.awardingOpportunity.awardingBody.preferredName"],"schema":{"type":"string"}},{"fallback":"Unknown","label":"Registration","path":["$.credentialSubject.awardingOpportunity.awardingBody.registration"],"schema":{"type":"string"}},{"fallback":"Unknown","label":"Website","path":["$.credentialSubject.awardingOpportunity.awardingBody.homepage"],"schema":{"format":"uri","type":"string"}}],"subtitle":{"fallback":"EBSI Verifiable diploma","path":[],"schema":{"type":"string"}},"title":{"fallback":"Diploma","path":[],"schema":{"type":"string"}}},"id":"diploma_01","schema":"https://api.preprod.oidc4vc.eu/trusted-schemas-registry/v1/schemas/0xbf78fc08a7a9f28f5479f58dea269d3657f54f13ca37d380cd4e92237fb691dd"}],"spec_version":"https://identity.foundation/credential-manifest/spec/v1.0.0/"}],"credential_supported":[{"cryptographic_binding_methods_supported":["did"],"cryptographic_suites_supported":["ES256K","ES256","ES384","ES512","RS256"],"display":[{"locale":"en-US","name":"Issuer Talao"}],"format":"jwt_vc","id":"VerifiableDiploma","types":"https://api.preprod.oidc4vc.eu/trusted-schemas-registry/v1/schemas/0xbf78fc08a7a9f28f5479f58dea269d3657f54f13ca37d380cd4e92237fb691dd"},{"id":"VerifiableDiploma_1","issuer":{"id":"did:ebsi:zhSw5rPXkcHjvquwnVcTzzB","name":"Test EBSILUX"},"output_descriptors":[{"display":{"description":{"fallback":"This card is a proof that you passed this diploma successfully. You can use this card when you need to prove this information to services that have adopted EU EBSI framework.","path":[],"schema":{"type":"string"}},"properties":[{"fallback":"Unknown","label":"First name","path":["$.credentialSubject.firstName"],"schema":{"type":"string"}},{"fallback":"Unknown","label":"Last name","path":["$.credentialSubject.familyName"],"schema":{"type":"string"}},{"fallback":"Unknown","label":"Birth date","path":["$.credentialSubject.dateOfBirth"],"schema":{"format":"date","type":"string"}},{"fallback":"Unknown","label":"Grading scheme","path":["$.credentialSubject.gradingScheme.title"],"schema":{"type":"string"}},{"fallback":"Unknown","label":"Title","path":["$.credentialSubject.learningAchievement.title"],"schema":{"type":"string"}},{"fallback":"Unknown","label":"Description","path":["$.credentialSubject.learningAchievement.description"],"schema":{"type":"string"}},{"fallback":"Unknown","label":"ECTS Points","path":["$.credentialSubject.learningSpecification.ectsCreditPoints"],"schema":{"type":"number"}},{"fallback":"Unknown","label":"Issue date","path":["$.issuanceDate"],"schema":{"format":"date","type":"string"}},{"fallback":"Unknown","label":"Issued by","path":["$.credentialSubject.awardingOpportunity.awardingBody.preferredName"],"schema":{"type":"string"}},{"fallback":"Unknown","label":"Registration","path":["$.credentialSubject.awardingOpportunity.awardingBody.registration"],"schema":{"type":"string"}},{"fallback":"Unknown","label":"Website","path":["$.credentialSubject.awardingOpportunity.awardingBody.homepage"],"schema":{"format":"uri","type":"string"}}],"subtitle":{"fallback":"EBSI Verifiable diploma","path":[],"schema":{"type":"string"}},"title":{"fallback":"Diploma","path":[],"schema":{"type":"string"}}},"id":"diploma_01","schema":"https://api.preprod.oidc4vc.eu/trusted-schemas-registry/v1/schemas/0xbf78fc08a7a9f28f5479f58dea269d3657f54f13ca37d380cd4e92237fb691dd"}],"spec_version":"https://identity.foundation/credential-manifest/spec/v1.0.0/"}],"credential_supported":[{"cryptographic_binding_methods_supported":["did"],"cryptographic_suites_supported":["ES256K","ES256","ES384","ES512","RS256"],"display":[{"locale":"en-US","name":"Issuer Talao"}],"format":"jwt_vc","id":"VerifiableDiploma","types":"https://api.preprod.oidc4vc.eu/trusted-schemas-registry/v1/schemas/0xbf78fc08a7a9f28f5479f58dea269d3657f54f13ca37d380cd4e92237fb691dd"}],"pre-authorized_grant_anonymous_access_supported":true,"subject_syntax_types_supported":["did:ebsi"],"token_endpoint":"https://talao.co/sandbox/ebsi/issuer/vgvghylozl/token"}'; - // final oidc4vc = OIDC4VC(client); - - // final issuer = oidc4vc.readIssuerDid( - // Response( - // requestOptions: RequestOptions(path: ''), - // data: jsonDecode(openidConfigurationResponse) as Map, - // ), - // ); - // expect(issuer, 'did:ebsi:zhSw5rPXkcHjvquwnVcTzzB'); - // }); - - // test('get publicKey did with didDocumentResponse', () { - // const didDocumentResponse = - // '{"assertionMethod":["did:ebsi:zeFCExU2XAAshYkPCpjuahA#3623b877bbb24b08ba390f3585418f53"],"authentication":["did:ebsi:zeFCExU2XAAshYkPCpjuahA#3623b877bbb24b08ba390f3585418f53"],"@context":"https://www.w3.org/ns/did/v1","id":"did:ebsi:zeFCExU2XAAshYkPCpjuahA","verificationMethod":[{"controller":"did:ebsi:zeFCExU2XAAshYkPCpjuahA","id":"did:ebsi:zeFCExU2XAAshYkPCpjuahA#3623b877bbb24b08ba390f3585418f53","publicKeyJwk":{"alg":"EdDSA","crv":"Ed25519","kid":"3623b877bbb24b08ba390f3585418f53","kty":"OKP","use":"sig","x":"pWgA8M3etXlLaqcRmgjEQkz7waseg3FKzMCzfm9Yeow"},"type":"Ed25519VerificationKey2019"}]}'; - // const issuerDid = 'did:ebsi:zeFCExU2XAAshYkPCpjuahA'; - - // const expectedPublicKey = - // '{"alg":"EdDSA","crv":"Ed25519","kid":"3623b877bbb24b08ba390f3585418f53","kty":"OKP","use":"sig","x":"pWgA8M3etXlLaqcRmgjEQkz7waseg3FKzMCzfm9Yeow"}'; // ignore: lines_longer_than_80_chars - - // final oidc4vc = OIDC4VC(client); - - // final publicKey = oidc4vc.readPublicKeyJwk( - // issuerDid, - // Response( - // requestOptions: RequestOptions(path: ''), - // data: jsonDecode(didDocumentResponse) as Map, - // ), - // ); - // expect(jsonEncode(publicKey), expectedPublicKey); - // }); - // }); - - // group('verify encoded data', () { - // const issuerDid1 = 'did:ebsi:zeFCExU2XAAshYkPCpjuahA'; - // const issuerDid2 = 'did:ebsi:zhSw5rPXkcHjvquwnVcTzzC'; - // const issuerDid3 = 'did:ebsi:zhSw5rPXkcHjvquwnVcTzzC'; - - // const issuerKid = - // 'did:ebsi:zeFCExU2XAAshYkPCpjuahA#3623b877bbb24b08ba390f3585418f53'; - - // const didDocumentUrl = - // 'https://api-pilot.oidc4vc.eu/did-registry/v3/identifiers/$issuerDid1'; - - // const didDocumentResponse = - // '{"assertionMethod":["did:ebsi:zeFCExU2XAAshYkPCpjuahA#3623b877bbb24b08ba390f3585418f53"],"authentication":["did:ebsi:zeFCExU2XAAshYkPCpjuahA#3623b877bbb24b08ba390f3585418f53"],"@context":"https://www.w3.org/ns/did/v1","id":"did:ebsi:zeFCExU2XAAshYkPCpjuahA","verificationMethod":[{"controller":"did:ebsi:zeFCExU2XAAshYkPCpjuahA","id":"did:ebsi:zeFCExU2XAAshYkPCpjuahA#3623b877bbb24b08ba390f3585418f53","publicKeyJwk":{"crv":"P-521","kty":"EC","x":"AekpBQ8ST8a8VcfVOTNl353vSrDCLLJXmPk06wTjxrrjcBpXp5EOnYG_NjFZ6OvLFV1jSfS9tsz4qUxcWceqwQGk","y":"ADSmRA43Z1DSNx_RvcLI87cdL07l6jQyyBXMoxVg_l2Th-x3S1WDhjDly79ajL4Kkd0AZMaZmh9ubmf63e3kyMj2"},"type":"Ed25519VerificationKey2019"}]}'; - - // const didDocumentUrl2 = - // 'https://api-pilot.oidc4vc.eu/did-registry/v3/identifiers/$issuerDid2'; - - // const didDocumentResponse2 = - // '{"assertionMethod":["did:ebsi:zeFCExU2XAAshYkPCpjuahA#3623b877bbb24b08ba390f3585418f53"],"authentication":["did:ebsi:zeFCExU2XAAshYkPCpjuahA#3623b877bbb24b08ba390f3585418f53"],"@context":"https://www.w3.org/ns/did/v1","id":"did:ebsi:zeFCExU2XAAshYkPCpjuahA","verificationMethod":[{"controller":"did:ebsi:zeFCExU2XAAshYkPCpjuahA","id":"did:ebsi:zeFCExU2XAAshYkPCpjuahA#3623b877bbb24b08ba390f3585418f53","publicKeyJwk":{"crv":"Ed25519","kty":"OKP","x":"AekpBQ8ST8a8VcfVOTNl353vSrDCLLJXmPk06wTjxrrjcBpXp5EOnYG_NjFZ6OvLFV1jSfS9tsz4qUxcWceqwQGk","y":"ADSmRA43Z1DSNx_RvcLI87cdL07l6jQyyBXMoxVg_l2Th-x3S1WDhjDly79ajL4Kkd0AZMaZmh9ubmf63e3kyMj2"},"type":"Ed25519VerificationKey2019"}]}'; - - // const didDocumentUrl3 = - // 'https://api-pilot.oidc4vc.eu/did-registry/v3/identifiers/$issuerDid3'; - - // const didDocumentResponse3 = - // '{"assertionMethod":["did:ebsi:zeFCExU2XAAshYkPCpjuahA#3623b877bbb24b08ba390f3585418f53"],"authentication":["did:ebsi:zeFCExU2XAAshYkPCpjuahA#3623b877bbb24b08ba390f3585418f53"],"@context":"https://www.w3.org/ns/did/v1","id":"did:ebsi:zeFCExU2XAAshYkPCpjuahA","verificationMethod":[{"controller":"did:ebsi:zeFCExU2XAAshYkPCpjuahA","id":"did:ebsi:zeFCExU2XAAshYkPCpjuahA#3623b877bbb24b08ba390f3585418f53","publicKeyJwk":{"alg":"EdDSA","crv":"Ed25519","kid":"-1909572257","kty":"OKP","x":"XWxGtApfcqmKI7p0OKnF5JSEWMVoLsytFXLEP7xZ_l8"},"type":"Ed25519VerificationKey2019"}]}'; - // dioAdapter - // ..onGet( - // didDocumentUrl, - // (request) => request.reply(200, jsonDecode(didDocumentResponse)), - // ) - // ..onGet( - // didDocumentUrl2, - // (request) => request.reply(200, jsonDecode(didDocumentResponse2)), - // ) - // ..onGet( - // didDocumentUrl3, - // (request) => request.reply(200, jsonDecode(didDocumentResponse3)), - // ); - - // final oidc4vc = OIDC4VC(client); - // test('returns VerificationType.verified', () async { - // const vcJwt = 'eyJhbGciOiJFUzUxMiJ9.' - // 'UGF5bG9hZA.' - // 'AdwMgeerwtHoh-l192l60hp9wAHZFVJbLfD_UxMi70cwnZOYaRI1bKPWROc-mZZq' - // 'wqT2SI-KGDKB34XO0aw_7XdtAG8GaSwFKdCAPZgoXD2YBJZCPEX3xKpRwcdOO8Kp' - // 'EHwJjyqOgzDO7iKvU8vcnwNrmxYbSW9ERBXukOXolLzeO_Jn'; - - // final isVerified = await oidc4vc.verifyEncodedData( - // issuerDid: issuerDid1, - // jwt: vcJwt, - // issuerKid: issuerKid, - // ); - - // expect(isVerified, VerificationType.verified); - // }); - - // test('returns VerificationType.notVerified', () async { - // const vcJwt = - // 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksiLCJqd2siOnsiY3J2IjoiUC0yNTZLIiwia3R5IjoiRUMiLCJ4IjoiSjR2UXRMVXlyVlVpRklYUnJ0RXE0eHVybUJacDJlcTl3Sm1Ya0lBX3N0SSIsInkiOiJFVVU2dlhvRzNCR1gyenp3alhyR0RjcjRFeUREMFZmazNfNWZnNWtTZ0tFIn0sImtpZCI6ImRpZDplYnNpOnpvOUZSMVlmQUtGUDNRNmR2cWh4Y1h4bmZlRGlKRFA5N2ttbnFoeUFVU0FDaiNDZ2NnMXk5eGo5dVdGdzU2UE1jMjlYQmQ5RVJlaXh6dm5mdEJ6OEp3UUZpQiJ9.eyJpc3MiOiJkaWQ6ZWJzaTp6bzlGUjFZZkFLRlAzUTZkdnFoeGNYeG5mZURpSkRQOTdrbW5xaHlBVVNBQ2oiLCJub25jZSI6IjdhMDdkZTBmLWE4NzktMTFlZC04MjJiLTBhMTYyODk1ODU2MCIsImlhdCI6MTY3NzA1MDc0MDEyMzIzNSwiYXVkIjoiaHR0cHM6Ly90YWxhby5jby9zYW5kYm94L2Vic2kvaXNzdWVyL3ZndmdoeWxvemwifQ.htjRCpFWbRwanAyQcAq9XZ4vxCXyFbzaaN3yPbPxWIcKFFzDDcA4QCHTUl-L4vzWq0R3LSgQFXQ9bo5D9uCm4w'; // ignore: lines_longer_than_80_chars - - // final isVerified = await oidc4vc.verifyEncodedData( - // issuerDid: issuerDid1, - // jwt: vcJwt, - // issuerKid: issuerKid, - // ); - - // expect(isVerified, VerificationType.notVerified); - // }); - - // test('returns VerificationType.unKnown', () async { - // const vcJwt = 'random'; - - // final isVerified = await oidc4vc.verifyEncodedData( - // issuerDid: issuerDid2, - // jwt: vcJwt, - // issuerKid: issuerKid, - // ); - // expect(isVerified, VerificationType.unKnown); - // }); - - // test('returns VerificationType.notVerified for OKP', () async { - // const vcJwt = 'eyJraWQiOiItMTkwOTU3MjI1NyIsImFsZyI6IkVkRFNBIn0.' - // 'eyJqdGkiOiIyMjkxNmYzYy05MDkzLTQ4MTMtODM5Ny1mMTBlNmI3MDRiNjgiLCJkZWxlZ2F0aW9uSWQiOiJiNGFlNDdhNy02MjVhLTQ2MzAtOTcyNy00NTc2NGE3MTJjY2UiLCJleHAiOjE2NTUyNzkxMDksIm5iZiI6MTY1NTI3ODgwOSwic2NvcGUiOiJyZWFkIG9wZW5pZCIsImlzcyI6Imh0dHBzOi8vaWRzdnIuZXhhbXBsZS5jb20iLCJzdWIiOiJ1c2VybmFtZSIsImF1ZCI6ImFwaS5leGFtcGxlLmNvbSIsImlhdCI6MTY1NTI3ODgwOSwicHVycG9zZSI6ImFjY2Vzc190b2tlbiJ9.' // ignore: lines_longer_than_80_chars - // 'rjeE8D_e4RYzgvpu-nOwwx7PWMiZyDZwkwO6RiHR5t8g4JqqVokUKQt-oST1s45wubacfeDSFogOrIhe3UHDAg'; // ignore: lines_longer_than_80_chars - - // final isVerified = await oidc4vc.verifyEncodedData( - // issuerDid: issuerDid3, - // jwt: vcJwt, - // issuerKid: issuerKid, - // ); - // expect(isVerified, VerificationType.verified); - // }); - // }); - - // group('getCredentialRequest', () { - // final oidc4vc = OIDC4VC(client); - - // test('extract correct credential type url from openId', () async { - // final url = oidc4vc.getCredentialRequest(givenOpenIdRequest); - // expect( - // url, - // 'https://api.preprod.oidc4vc.eu/trusted-schemas-registry/v1/schemas/0xbf78fc08a7a9f28f5479f58dea269d3657f54f13ca37d380cd4e92237fb691dd', - // ); - // }); - - // test('credential type url is empty when url is not OK', () async { - // final url = oidc4vc.getCredentialRequest('www.example.com'); - // expect(url, ''); - // }); - // }); - - // test('extract correct credential type url from openId', () async { - // final oidc4vc = OIDC4VC(client); - // final url = oidc4vc.getCredentialRequest(givenOpenIdRequest); - // expect( - // url, - // 'https://api.preprod.oidc4vc.eu/trusted-schemas-registry/v1/schemas/0xbf78fc08a7a9f28f5479f58dea269d3657f54f13ca37d380cd4e92237fb691dd', - // ); - // }); - - // group('get token', () { - // test('get correct token ', () async { - // final oidc4vc = OIDC4VC(client); - - // final json = { - // 'code': 'cb803d46-9c88-11ed-bdb3-0a1628958560', - // 'grant_type': 'authorization_code' - // }; - - // const expectedValue = - // '{"access_token":"7a07dd19-a879-11ed-ad95-0a1628958560","c_nonce":"7a07de0f-a879-11ed-822b-0a1628958560","token_type":"Bearer","expires_in":1000}'; // ignore: lines_longer_than_80_chars - - // final token = await oidc4vc.getToken(tokenUrl, json); - // expect(jsonEncode(token), expectedValue); - // }); - - // test('throw expection when invalid value is sent', () async { - // expect( - // () async { - // dioAdapter.onPost( - // tokenUrl, - // (request) => request.throws( - // 401, - // DioException(requestOptions: RequestOptions(path: tokenUrl)), - // ), - // ); - // final oidc4vc = OIDC4VC(client); - - // final json = {}; - - // await oidc4vc.getToken(tokenUrl, json); - // }, - // throwsA(isA()), - // ); - // }); - // }); - - // group('get did', () { - // final oidc4vc = OIDC4VC(client); - - // const expectedDidP256K = - // 'did:ebsi:zo9FR1YfAKFP3Q6dvqhxcXxnfeDiJDP97kmnqhyAUSACj'; - // test('from mnemonic ', () async { - // final did = await oidc4vc.getDidFromMnemonic(mnemonic, null); - // expect(did, expectedDidP256K); - // }); - - // test('from P-256K privateKey ', () async { - // const key = { - // 'crv': 'P-256K', - // 'd': 'ccWWNSjGiv1iWlNh4kfhWvwG3yyQMe8o31Du0uKRzrs', - // 'kty': 'EC', - // 'x': 'J4vQtLUyrVUiFIXRrtEq4xurmBZp2eq9wJmXkIA_stI', - // 'y': 'EUU6vXoG3BGX2zzwjXrGDcr4EyDD0Vfk3_5fg5kSgKE' - // }; - // final did = await oidc4vc.getDidFromMnemonic(null, jsonEncode(key)); - // expect(did, expectedDidP256K); - // }); - // test('from secp256k1 privateKey ', () async { - // const expectedDidsecp256k1 = - // 'did:ebsi:zfuA5yVRZVaKna9pRCHv28yyCYWiXWHZXXvtp3AbTdB4p'; - // const key = { - // 'crv': 'secp256k1', - // 'd': 'jMudzhP9YFNywIeIYYJbHtyizeaXOa1PWdX-qBjPTg8', - // 'kty': 'EC', - // 'x': 'yEC4JxUbpYx2-tKExh2NrLpETx-nnudZfcg4AcyL1to', - // 'y': 'HNh3aF4zQsnf_sFXUVaSzrQF85veDoVxhPQ-163wUYM' - // }; - // final did = await oidc4vc.getDidFromMnemonic(null, jsonEncode(key)); - // expect(did, expectedDidsecp256k1); - // }); - - // group('send Presentation', () { - // const url = - // 'https://talao.co/sandbox/ebsi/login/endpoint/f1722b9e-ae19-11ed-ac9f-0a1628958560'; - - // final uri = Uri.parse( - // 'openid://?scope=openid&response_type=id_token&client_id=xjcqarovuv&redirect_uri=https%3A%2F%2Ftalao.co%2Fsandbox%2Febsi%2Flogin%2Fendpoint%2Ff1722b9e-ae19-11ed-ac9f-0a1628958560&claims=%7B%27id_token%27%3A+%7B%27email%27%3A+None%7D%2C+%27vp_token%27%3A+%7B%27presentation_definition%27%3A+%7B%27id%27%3A+%27f17247aa-ae19-11ed-9241-0a1628958560%27%2C+%27input_descriptors%27%3A+%5B%7B%27constraints%27%3A+%7B%27fields%27%3A+%5B%7B%27path%27%3A+%5B%27%24.credentialSchema.id%27%5D%2C+%27filter%27%3A+%7B%27type%27%3A+%27string%27%2C+%27pattern%27%3A+%27https%3A%2F%2Fapi.preprod.oidc4vc.eu%2Ftrusted-schemas-registry%2Fv1%2Fschemas%2F0xbf78fc08a7a9f28f5479f58dea269d3657f54f13ca37d380cd4e92237fb691dd%27%7D%7D%5D%7D%2C+%27id%27%3A+%27f1724e6c-ae19-11ed-a3cf-0a1628958560%27%2C+%27name%27%3A+%27Input+descriptor+1%27%2C+%27purpose%27%3A+%27+%27%7D%5D%2C+%27format%27%3A+%7B%27jwt_vp%27%3A+%7B%27alg%27%3A+%5B%27ES256K%27%2C+%27ES256%27%2C+%27PS256%27%2C+%27RS256%27%5D%7D%7D%7D%7D%7D&nonce=f17239d6-ae19-11ed-8550-0a1628958560', - // ); - - // final credentialToBePresented = [ - // r'{"id":"urn:uuid:6b1d8411-9ed5-4566-9c7f-4c24165ff236","receivedId":"urn:uuid:6b1d8411-9ed5-4566-9c7f-4c24165ff236","image":null,"data":{"@context":["https://www.w3.org/2018/credentials/v1"],"credentialSchema":{"id":"https://api.preprod.oidc4vc.eu/trusted-schemas-registry/v1/schemas/0xbf78fc08a7a9f28f5479f58dea269d3657f54f13ca37d380cd4e92237fb691dd","type":"JsonSchemaValidator2018"},"credentialStatus":{"id":"https://essif.europa.eu/status/education#higherEducation#392ac7f6-399a-437b-a268-4691ead8f176","type":"CredentialStatusList2020"},"credentialSubject":{"awardingOpportunity":{"awardingBody":{"eidasLegalIdentifier":"Unknown","homepage":"https://leaston.bcdiploma.com/","id":"did:ebsi:zdRvvKbXhVVBsXhatjuiBhs","preferredName":"Leaston University","registration":"0597065J"},"endedAtTime":"2020-06-26T00:00:00Z","id":"https://leaston.bcdiploma.com/law-economics-management#AwardingOpportunity","identifier":"https://certificate-demo.bcdiploma.com/check/87ED2F2270E6C41456E94B86B9D9115B4E35BCCAD200A49B846592C14F79C86BV1Fnbllta0NZTnJkR3lDWlRmTDlSRUJEVFZISmNmYzJhUU5sZUJ5Z2FJSHpWbmZZ","location":"FRANCE","startedAtTime":"2019-09-02T00:00:00Z"},"dateOfBirth":"1993-04-08","familyName":"DOE","givenNames":"Jane","gradingScheme":{"id":"https://leaston.bcdiploma.com/law-economics-management#GradingScheme","title":"2 year full-time programme / 4 semesters"},"id":"did:ebsi:zhFWcvr8DFL3cAVdheCpjHg3sPn1WUh9Gynm6hevPFzpw","identifier":"0904008084H","learningAchievement":{"additionalNote":["DISTRIBUTION MANAGEMENT"],"description":"The Master in Information and Computer Sciences (MICS) at the University of Luxembourg enables students to acquire deeper knowledge in computer science by understanding its abstract and interdisciplinary foundations, focusing on problem solving and developing lifelong learning skills.","id":"https://leaston.bcdiploma.com/law-economics-management#LearningAchievment","title":"Master in Information and Computer Sciences"},"learningSpecification":{"ectsCreditPoints":120,"eqfLevel":7,"id":"https://leaston.bcdiploma.com/law-economics-management#LearningSpecification","iscedfCode":["7"],"nqfLevel":["7"]},"type":"https://api.preprod.oidc4vc.eu/trusted-schemas-registry/v1/schemas/0xbf78fc08a7a9f28f5479f58dea269d3657f54f13ca37d380cd4e92237fb691dd"},"evidence":{"documentPresence":["Physical"],"evidenceDocument":["Passport"],"id":"https://essif.europa.eu/tsr-va/evidence/f2aeec97-fc0d-42bf-8ca7-0548192d5678","subjectPresence":"Physical","type":["DocumentVerification"],"verifier":"did:ebsi:2962fb784df61baa267c8132497539f8c674b37c1244a7a"},"id":"urn:uuid:6b1d8411-9ed5-4566-9c7f-4c24165ff236","issuanceDate":"2023-02-16T12:04:19Z","issued":"2023-02-16T12:04:19Z","issuer":"did:ebsi:zhSw5rPXkcHjvquwnVcTzzB","proof":{"created":"2022-04-27T12:25:07Z","creator":"did:ebsi:zdRvvKbXhVVBsXhatjuiBhs","domain":"https://api.preprod.oidc4vc.eu","jws":"eyJiNjQiOmZhbHNlLCJjcml0IjpbImI2NCJdLCJhbGciOiJFUzI1NksifQ..mIBnM8XDQqSYKQNX_LvaJhmsbyCr5OZ5cU2Zk-ReqLpr4doFsgmoobkO5128tZy-8KimVjJkGw0wL1uBWnMLWQ","nonce":"3ea68dae-d07a-4daa-932b-fbb58f5c20c4","type":"EcdsaSecp256k1Signature2019"},"type":["VerifiableCredential","VerifiableAttestation","VerifiableDiploma"],"validFrom":"2023-02-16T12:04:19Z"},"shareLink":"","credentialPreview":{"id":"urn:uuid:6b1d8411-9ed5-4566-9c7f-4c24165ff236","type":["VerifiableCredential","VerifiableAttestation","VerifiableDiploma"],"issuer":"did:ebsi:zhSw5rPXkcHjvquwnVcTzzB","description":[],"name":[],"issuanceDate":"2023-02-16T12:04:19Z","proof":[{"type":"EcdsaSecp256k1Signature2019","proofPurpose":null,"verificationMethod":null,"created":"2022-04-27T12:25:07Z","jws":"eyJiNjQiOmZhbHNlLCJjcml0IjpbImI2NCJdLCJhbGciOiJFUzI1NksifQ..mIBnM8XDQqSYKQNX_LvaJhmsbyCr5OZ5cU2Zk-ReqLpr4doFsgmoobkO5128tZy-8KimVjJkGw0wL1uBWnMLWQ"}],"credentialSubject":{"id":"did:ebsi:zhFWcvr8DFL3cAVdheCpjHg3sPn1WUh9Gynm6hevPFzpw","type":"https://api.preprod.oidc4vc.eu/trusted-schemas-registry/v1/schemas/0xbf78fc08a7a9f28f5479f58dea269d3657f54f13ca37d380cd4e92237fb691dd","issuedBy":{"name":""},"expires":"","awardingOpportunity":{"awardingBody":{"eidasLegalIdentifier":"Unknown","homepage":"https://leaston.bcdiploma.com/","id":"did:ebsi:zdRvvKbXhVVBsXhatjuiBhs","preferredName":"Leaston University","registration":"0597065J"},"endedAtTime":"2020-06-26T00:00:00Z","id":"https://leaston.bcdiploma.com/law-economics-management#AwardingOpportunity","identifier":"https://certificate-demo.bcdiploma.com/check/87ED2F2270E6C41456E94B86B9D9115B4E35BCCAD200A49B846592C14F79C86BV1Fnbllta0NZTnJkR3lDWlRmTDlSRUJEVFZISmNmYzJhUU5sZUJ5Z2FJSHpWbmZZ","location":"FRANCE","startedAtTime":"2019-09-02T00:00:00Z"},"dateOfBirth":"1993-04-08","familyName":"DOE","givenNames":"Jane","gradingScheme":{"id":"https://leaston.bcdiploma.com/law-economics-management#GradingScheme","title":"2 year full-time programme / 4 semesters"},"identifier":"0904008084H","learningAchievement":{"additionalNote":["DISTRIBUTION MANAGEMENT"],"description":"The Master in Information and Computer Sciences (MICS) at the University of Luxembourg enables students to acquire deeper knowledge in computer science by understanding its abstract and interdisciplinary foundations, focusing on problem solving and developing lifelong learning skills.","id":"https://leaston.bcdiploma.com/law-economics-management#LearningAchievment","title":"Master in Information and Computer Sciences"},"learningSpecification":{"ectsCreditPoints":120,"eqfLevel":7,"id":"https://leaston.bcdiploma.com/law-economics-management#LearningSpecification","iscedfCode":["7"],"nqfLevel":["7"]}},"evidence":[{"id":"https://essif.europa.eu/tsr-va/evidence/f2aeec97-fc0d-42bf-8ca7-0548192d5678","type":["DocumentVerification"]}],"credentialStatus":{"id":"https://essif.europa.eu/status/education#higherEducation#392ac7f6-399a-437b-a268-4691ead8f176","type":"CredentialStatusList2020","revocationListIndex":"","revocationListCredential":""}},"display":{"backgroundColor":"","icon":"","nameFallback":"","descriptionFallback":""},"expirationDate":null,"credential_manifest":{"id":"VerifiableDiploma_1","issuer":{"id":"did:ebsi:zhSw5rPXkcHjvquwnVcTzzB","name":"Test EBSILUX"},"output_descriptors":[{"id":"diploma_01","schema":"https://api.preprod.oidc4vc.eu/trusted-schemas-registry/v1/schemas/0xbf78fc08a7a9f28f5479f58dea269d3657f54f13ca37d380cd4e92237fb691dd","name":null,"description":null,"styles":null,"display":{"title":{"path":[],"schema":{"type":"string","format":null},"fallback":"Diploma"},"subtitle":{"path":[],"schema":{"type":"string","format":null},"fallback":"EBSI Verifiable diploma"},"description":{"path":[],"schema":{"type":"string","format":null},"fallback":"This card is a proof that you passed this diploma successfully. You can use this card when you need to prove this information to services that have adopted EU EBSI framework."},"properties":[{"label":"First name","path":["$.credentialSubject.firstName"],"schema":{"type":"string","format":null},"fallback":"Unknown"},{"label":"Last name","path":["$.credentialSubject.familyName"],"schema":{"type":"string","format":null},"fallback":"Unknown"},{"label":"Birth date","path":["$.credentialSubject.dateOfBirth"],"schema":{"type":"string","format":"date"},"fallback":"Unknown"},{"label":"Grading scheme","path":["$.credentialSubject.gradingScheme.title"],"schema":{"type":"string","format":null},"fallback":"Unknown"},{"label":"Title","path":["$.credentialSubject.learningAchievement.title"],"schema":{"type":"string","format":null},"fallback":"Unknown"},{"label":"Description","path":["$.credentialSubject.learningAchievement.description"],"schema":{"type":"string","format":null},"fallback":"Unknown"},{"label":"ECTS Points","path":["$.credentialSubject.learningSpecification.ectsCreditPoints"],"schema":{"type":"number","format":null},"fallback":"Unknown"},{"label":"Issue date","path":["$.issuanceDate"],"schema":{"type":"string","format":"date"},"fallback":"Unknown"},{"label":"Issued by","path":["$.credentialSubject.awardingOpportunity.awardingBody.preferredName"],"schema":{"type":"string","format":null},"fallback":"Unknown"},{"label":"Registration","path":["$.credentialSubject.awardingOpportunity.awardingBody.registration"],"schema":{"type":"string","format":null},"fallback":"Unknown"},{"label":"Website","path":["$.credentialSubject.awardingOpportunity.awardingBody.homepage"],"schema":{"type":"string","format":"uri"},"fallback":"Unknown"}]}}],"presentation_definition":null},"challenge":null,"domain":null,"activities":[{"acquisitionAt":"2023-02-16T17:34:24.679713","presentation":null},{"acquisitionAt":null,"presentation":{"issuer":{"preferredName":"","did":[],"organizationInfo":{"id":"","legalName":"","currentAddress":"","website":"domain","issuerDomain":[]}},"presentedAt":"2023-02-16T17:34:47.206600"}}],"jwt":"eyJhbGciOiJFUzI1NksiLCJraWQiOiJkaWQ6ZWJzaTp6aFN3NXJQWGtjSGp2cXV3blZjVHp6QiM1MTVhOWM0MzZjMGYyYWQzYWI2NWQ2Y2VmYzVjMWYwNmMwNWI4YWRmY2Y1NGVlMDZkYzgwNTQzMjA0NzBmZmFmIiwidHlwIjoiSldUIn0.eyJleHAiOjE2NzY1NTAwNTkuMjMzMzY3LCJpYXQiOjE2NzY1NDkwNTkuMjMzMzYsImlzcyI6ImRpZDplYnNpOnpoU3c1clBYa2NIanZxdXduVmNUenpCIiwianRpIjoidXJuOnV1aWQ6NmIxZDg0MTEtOWVkNS00NTY2LTljN2YtNGMyNDE2NWZmMjM2IiwibmJmIjoxNjc2NTQ5MDU5LjIzMzM2NCwibm9uY2UiOiIwYTc4MDg2MC1hZGYyLTExZWQtYmIzZS0wYTE2Mjg5NTg1NjAiLCJzdWIiOiJkaWQ6ZWJzaTp6aEZXY3ZyOERGTDNjQVZkaGVDcGpIZzNzUG4xV1VoOUd5bm02aGV2UEZ6cHciLCJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSJdLCJjcmVkZW50aWFsU2NoZW1hIjp7ImlkIjoiaHR0cHM6Ly9hcGkucHJlcHJvZC5lYnNpLmV1L3RydXN0ZWQtc2NoZW1hcy1yZWdpc3RyeS92MS9zY2hlbWFzLzB4YmY3OGZjMDhhN2E5ZjI4ZjU0NzlmNThkZWEyNjlkMzY1N2Y1NGYxM2NhMzdkMzgwY2Q0ZTkyMjM3ZmI2OTFkZCIsInR5cGUiOiJKc29uU2NoZW1hVmFsaWRhdG9yMjAxOCJ9LCJjcmVkZW50aWFsU3RhdHVzIjp7ImlkIjoiaHR0cHM6Ly9lc3NpZi5ldXJvcGEuZXUvc3RhdHVzL2VkdWNhdGlvbiNoaWdoZXJFZHVjYXRpb24jMzkyYWM3ZjYtMzk5YS00MzdiLWEyNjgtNDY5MWVhZDhmMTc2IiwidHlwZSI6IkNyZWRlbnRpYWxTdGF0dXNMaXN0MjAyMCJ9LCJjcmVkZW50aWFsU3ViamVjdCI6eyJhd2FyZGluZ09wcG9ydHVuaXR5Ijp7ImF3YXJkaW5nQm9keSI6eyJlaWRhc0xlZ2FsSWRlbnRpZmllciI6IlVua25vd24iLCJob21lcGFnZSI6Imh0dHBzOi8vbGVhc3Rvbi5iY2RpcGxvbWEuY29tLyIsImlkIjoiZGlkOmVic2k6emRSdnZLYlhoVlZCc1hoYXRqdWlCaHMiLCJwcmVmZXJyZWROYW1lIjoiTGVhc3RvbiBVbml2ZXJzaXR5IiwicmVnaXN0cmF0aW9uIjoiMDU5NzA2NUoifSwiZW5kZWRBdFRpbWUiOiIyMDIwLTA2LTI2VDAwOjAwOjAwWiIsImlkIjoiaHR0cHM6Ly9sZWFzdG9uLmJjZGlwbG9tYS5jb20vbGF3LWVjb25vbWljcy1tYW5hZ2VtZW50I0F3YXJkaW5nT3Bwb3J0dW5pdHkiLCJpZGVudGlmaWVyIjoiaHR0cHM6Ly9jZXJ0aWZpY2F0ZS1kZW1vLmJjZGlwbG9tYS5jb20vY2hlY2svODdFRDJGMjI3MEU2QzQxNDU2RTk0Qjg2QjlEOTExNUI0RTM1QkNDQUQyMDBBNDlCODQ2NTkyQzE0Rjc5Qzg2QlYxRm5ibGx0YTBOWlRuSmtSM2xEV2xSbVREbFNSVUpFVkZaSVNtTm1ZekpoVVU1c1pVSjVaMkZKU0hwV2JtWloiLCJsb2NhdGlvbiI6IkZSQU5DRSIsInN0YXJ0ZWRBdFRpbWUiOiIyMDE5LTA5LTAyVDAwOjAwOjAwWiJ9LCJkYXRlT2ZCaXJ0aCI6IjE5OTMtMDQtMDgiLCJmYW1pbHlOYW1lIjoiRE9FIiwiZ2l2ZW5OYW1lcyI6IkphbmUiLCJncmFkaW5nU2NoZW1lIjp7ImlkIjoiaHR0cHM6Ly9sZWFzdG9uLmJjZGlwbG9tYS5jb20vbGF3LWVjb25vbWljcy1tYW5hZ2VtZW50I0dyYWRpbmdTY2hlbWUiLCJ0aXRsZSI6IjIgeWVhciBmdWxsLXRpbWUgcHJvZ3JhbW1lIC8gNCBzZW1lc3RlcnMifSwiaWQiOiJkaWQ6ZWJzaTp6aEZXY3ZyOERGTDNjQVZkaGVDcGpIZzNzUG4xV1VoOUd5bm02aGV2UEZ6cHciLCJpZGVudGlmaWVyIjoiMDkwNDAwODA4NEgiLCJsZWFybmluZ0FjaGlldmVtZW50Ijp7ImFkZGl0aW9uYWxOb3RlIjpbIkRJU1RSSUJVVElPTiBNQU5BR0VNRU5UIl0sImRlc2NyaXB0aW9uIjoiVGhlIE1hc3RlciBpbiBJbmZvcm1hdGlvbiBhbmQgQ29tcHV0ZXIgU2NpZW5jZXMgKE1JQ1MpIGF0IHRoZSBVbml2ZXJzaXR5IG9mIEx1eGVtYm91cmcgZW5hYmxlcyBzdHVkZW50cyB0byBhY3F1aXJlIGRlZXBlciBrbm93bGVkZ2UgaW4gY29tcHV0ZXIgc2NpZW5jZSBieSB1bmRlcnN0YW5kaW5nIGl0cyBhYnN0cmFjdCBhbmQgaW50ZXJkaXNjaXBsaW5hcnkgZm91bmRhdGlvbnMsIGZvY3VzaW5nIG9uIHByb2JsZW0gc29sdmluZyBhbmQgZGV2ZWxvcGluZyBsaWZlbG9uZyBsZWFybmluZyBza2lsbHMuIiwiaWQiOiJodHRwczovL2xlYXN0b24uYmNkaXBsb21hLmNvbS9sYXctZWNvbm9taWNzLW1hbmFnZW1lbnQjTGVhcm5pbmdBY2hpZXZtZW50IiwidGl0bGUiOiJNYXN0ZXIgaW4gSW5mb3JtYXRpb24gYW5kIENvbXB1dGVyIFNjaWVuY2VzIn0sImxlYXJuaW5nU3BlY2lmaWNhdGlvbiI6eyJlY3RzQ3JlZGl0UG9pbnRzIjoxMjAsImVxZkxldmVsIjo3LCJpZCI6Imh0dHBzOi8vbGVhc3Rvbi5iY2RpcGxvbWEuY29tL2xhdy1lY29ub21pY3MtbWFuYWdlbWVudCNMZWFybmluZ1NwZWNpZmljYXRpb24iLCJpc2NlZGZDb2RlIjpbIjciXSwibnFmTGV2ZWwiOlsiNyJdfX0sImV2aWRlbmNlIjp7ImRvY3VtZW50UHJlc2VuY2UiOlsiUGh5c2ljYWwiXSwiZXZpZGVuY2VEb2N1bWVudCI6WyJQYXNzcG9ydCJdLCJpZCI6Imh0dHBzOi8vZXNzaWYuZXVyb3BhLmV1L3Rzci12YS9ldmlkZW5jZS9mMmFlZWM5Ny1mYzBkLTQyYmYtOGNhNy0wNTQ4MTkyZDU2NzgiLCJzdWJqZWN0UHJlc2VuY2UiOiJQaHlzaWNhbCIsInR5cGUiOlsiRG9jdW1lbnRWZXJpZmljYXRpb24iXSwidmVyaWZpZXIiOiJkaWQ6ZWJzaToyOTYyZmI3ODRkZjYxYmFhMjY3YzgxMzI0OTc1MzlmOGM2NzRiMzdjMTI0NGE3YSJ9LCJpZCI6InVybjp1dWlkOjZiMWQ4NDExLTllZDUtNDU2Ni05YzdmLTRjMjQxNjVmZjIzNiIsImlzc3VhbmNlRGF0ZSI6IjIwMjMtMDItMTZUMTI6MDQ6MTlaIiwiaXNzdWVkIjoiMjAyMy0wMi0xNlQxMjowNDoxOVoiLCJpc3N1ZXIiOiJkaWQ6ZWJzaTp6aFN3NXJQWGtjSGp2cXV3blZjVHp6QiIsInByb29mIjp7ImNyZWF0ZWQiOiIyMDIyLTA0LTI3VDEyOjI1OjA3WiIsImNyZWF0b3IiOiJkaWQ6ZWJzaTp6ZFJ2dktiWGhWVkJzWGhhdGp1aUJocyIsImRvbWFpbiI6Imh0dHBzOi8vYXBpLnByZXByb2QuZWJzaS5ldSIsImp3cyI6ImV5SmlOalFpT21aaGJITmxMQ0pqY21sMElqcGJJbUkyTkNKZExDSmhiR2NpT2lKRlV6STFOa3NpZlEuLm1JQm5NOFhEUXFTWUtRTlhfTHZhSmhtc2J5Q3I1T1o1Y1UyWmstUmVxTHByNGRvRnNnbW9vYmtPNTEyOHRaeS04S2ltVmpKa0d3MHdMMXVCV25NTFdRIiwibm9uY2UiOiIzZWE2OGRhZS1kMDdhLTRkYWEtOTMyYi1mYmI1OGY1YzIwYzQiLCJ0eXBlIjoiRWNkc2FTZWNwMjU2azFTaWduYXR1cmUyMDE5In0sInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiLCJWZXJpZmlhYmxlQXR0ZXN0YXRpb24iLCJWZXJpZmlhYmxlRGlwbG9tYSJdLCJ2YWxpZEZyb20iOiIyMDIzLTAyLTE2VDEyOjA0OjE5WiJ9fQ.7Bmdrkp-hpM7BcNASc7ngjia3cjrZr-nqmP0Plsfnefn26G_yR5fBJV8BT_U2zsMlcqWsxuJ9pS2Vyiq_SrooQ"}' - // ]; - - // test('successfully send presentation', () async { - // const response = - // '{"access":"ok","created":1676566727.680238,"credential_status":"signature check bypassed","holder_did_status":"ok","id_token_status":"ok","qrcode_status":"ok","response_format":"ok","status_code":200,"vp_token_status":"ok"}'; // ignore: lines_longer_than_80_chars - // dioAdapter.onGet( - // url, - // (request) => request.reply(200, jsonDecode(response)), - // ); - // final oidc4vc = OIDC4VC(client); - - // expect( - // () async { - // await oidc4vc.sendPresentation( - // uri, - // credentialToBePresented, - // mnemonic, - // null, - // ); - // }, - // returnsNormally, - // ); - // }); - - // test('throw exception', () async { - // expect( - // () async { - // dioAdapter.onPost( - // url, - // (request) => request.throws( - // 401, - // DioException(requestOptions: RequestOptions(path: tokenUrl)), - // ), - // ); - // final oidc4vc = OIDC4VC(client); - - // await oidc4vc.sendPresentation( - // uri, - // credentialToBePresented, - // mnemonic, - // null, - // ); - // }, - // throwsA(isA()), - // ); - // }); - // }); - // }); - - // test('get corresponding did document', () async { - // const url = - // 'https://api-pilot.oidc4vc.eu/did-registry/v3/identifiers/$didKey'; - // const response = { - // '@context': 'https://w3id.org/did/v1', - // 'id': 'did:ebsi:z24q8qN8UE1j4XAFiFKtvJbH', - // 'verificationMethod': [ - // { - // 'id': 'did:ebsi:z24q8qN8UE1j4XAFiFKtvJbH#keys-1', - // 'type': 'Secp256k1VerificationKey2018', - // 'controller': 'did:ebsi:z24q8qN8UE1j4XAFiFKtvJbH', - // 'publicKeyHex': - // '04f9139fbd3b9780863b17e8390934a1017fc8d0f18b84f02acb04f24c9cae261e45745c6507881509b9e6d837329153ea75fafb2c1a5d504702d04f1e22a80ecc' // ignore: lines_longer_than_80_chars - // } - // ], - // 'authentication': ['did:ebsi:z24q8qN8UE1j4XAFiFKtvJbH'], - // 'assertionMethod': ['did:ebsi:z24q8qN8UE1j4XAFiFKtvJbH#keys-1'] - // }; - - // dioAdapter.onGet( - // url, - // (request) => request.reply(200, response), - // ); - // final oidc4vc = OIDC4VC(client); - - // final value = await oidc4vc.getDidDocument(didKey); - - // expect(value.data, response); - // }); - - // test('sandbox', () { - // const cNonce = 'cNonce'; - // const did = 'did'; - // const issuer = 'issuer'; - // final payload = { - // 'iss': did, - // 'nonce': cNonce, - // 'iat': DateTime.now().microsecondsSinceEpoch, - // 'aud': issuer - // }; - - // final jwt = JWT( - // // Payload - // payload, - - // issuer: issuer, - // ); - - // // Sign it (default with HS256 algorithm) - // // ignore: unused_local_variable - // final token = jwt.sign(SecretKey('secret passphrase')); - // }); - - // // test('generateToken', () { - // // final jwk = { - // // 'kty': 'OKP', - // // 'crv': 'Ed25519', - // // 'd': '-_9OD-PMHKE2mFKEVH5R1vDhEMimtXPUX-2w1Xa0hQ0=', - // // 'x': '1tknP1Fx-YIQBzw3xXtCwLGgYoTb-nSsNn-k9uzWpuw=', - // // }; - - // // final vpTokenPayload = { - // // 'iss': 'did:ebsi:zrDzYQPDztwjQ8HdXto1B4FVB14fxoiNawZd8eyEuhR7K', - // // 'nonce': '8dd8d00a-ad17-11ed-9fe9-0a1628958560', - // // 'iat': '1676455223753366', - // // 'aud': 'https://talao.co/sandbox/ebsi/issuer/vgvghylozl', - // // }; - - // // final oidc4vc = OIDC4VC(client); - - // // final tokenParameters = TokenParameters(jwk); - // // final token = oidc4vc.generateToken(vpTokenPayload, tokenParameters); - //}); - //} } diff --git a/packages/oidc4vc/test/src/oidc4vci_draft_type_test.dart b/packages/oidc4vc/test/src/oidc4vci_draft_type_test.dart new file mode 100644 index 000000000..1e5771ecf --- /dev/null +++ b/packages/oidc4vc/test/src/oidc4vci_draft_type_test.dart @@ -0,0 +1,16 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:oidc4vc/oidc4vc.dart'; + +void main() { + group('OIDC4VCIDraftTypeX', () { + test('formattedString', () { + expect(OIDC4VCIDraftType.draft11.formattedString, 'Draft 11'); + expect(OIDC4VCIDraftType.draft13.formattedString, 'Draft 13'); + }); + + test('numbering', () { + expect(OIDC4VCIDraftType.draft11.numbering, '11'); + expect(OIDC4VCIDraftType.draft13.numbering, '13'); + }); + }); +} diff --git a/packages/oidc4vc/test/src/oidc4vp_draft_type_test.dart b/packages/oidc4vc/test/src/oidc4vp_draft_type_test.dart new file mode 100644 index 000000000..7b088cb12 --- /dev/null +++ b/packages/oidc4vc/test/src/oidc4vp_draft_type_test.dart @@ -0,0 +1,13 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:oidc4vc/oidc4vc.dart'; + +void main() { + group('OIDC4VPDraftTypeX', () { + test('formattedString', () { + expect(OIDC4VPDraftType.draft10.formattedString, 'Draft 10'); + expect(OIDC4VPDraftType.draft13.formattedString, 'Draft 13'); + expect(OIDC4VPDraftType.draft18.formattedString, 'Draft 18'); + expect(OIDC4VPDraftType.draft20.formattedString, 'Draft 20'); + }); + }); +} diff --git a/packages/oidc4vc/test/src/pkce_dart_test.dart b/packages/oidc4vc/test/src/pkce_dart_test.dart new file mode 100644 index 000000000..02574173a --- /dev/null +++ b/packages/oidc4vc/test/src/pkce_dart_test.dart @@ -0,0 +1,32 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:oidc4vc/oidc4vc.dart'; + +void main() { + group('PkcePair', () { + test('generate', () { + final pkcePair = PkcePair.generate(); + expect(pkcePair.codeVerifier.length, 43); + expect(pkcePair.codeChallenge.length, 43); + }); + + test('generate with invalid length', () { + expect( + () => PkcePair.generate(length: 31), // Less than 32 + throwsA(isA()), + ); + + expect( + () => PkcePair.generate(length: 97), // Greater than 96 + throwsA(isA()), + ); + }); + + test('generate produces unique values', () { + final pkcePair1 = PkcePair.generate(); + final pkcePair2 = PkcePair.generate(); + + expect(pkcePair1.codeVerifier, isNot(pkcePair2.codeVerifier)); + expect(pkcePair1.codeChallenge, isNot(pkcePair2.codeChallenge)); + }); + }); +} diff --git a/packages/oidc4vc/test/src/proof_header_type_test.dart b/packages/oidc4vc/test/src/proof_header_type_test.dart new file mode 100644 index 000000000..d9a73f101 --- /dev/null +++ b/packages/oidc4vc/test/src/proof_header_type_test.dart @@ -0,0 +1,11 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:oidc4vc/oidc4vc.dart'; + +void main() { + group('ProofHeaderTypeX', () { + test('formattedString', () { + expect(ProofHeaderType.kid.formattedString, 'kid'); + expect(ProofHeaderType.jwk.formattedString, 'jwk'); + }); + }); +} diff --git a/packages/oidc4vc/test/src/proof_type_test.dart b/packages/oidc4vc/test/src/proof_type_test.dart new file mode 100644 index 000000000..e9056bfac --- /dev/null +++ b/packages/oidc4vc/test/src/proof_type_test.dart @@ -0,0 +1,16 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:oidc4vc/oidc4vc.dart'; + +void main() { + group('ProofTypeeX', () { + test('formattedString', () { + expect(ProofType.ldpVp.formattedString, 'ldp_vp'); + expect(ProofType.jwt.formattedString, 'jwt'); + }); + + test('value', () { + expect(ProofType.ldpVp.value, 'ldp_vp'); + expect(ProofType.jwt.value, 'jwt'); + }); + }); +} diff --git a/packages/oidc4vc/test/src/soipv2_draft_type_test.dart b/packages/oidc4vc/test/src/soipv2_draft_type_test.dart new file mode 100644 index 000000000..97b46019b --- /dev/null +++ b/packages/oidc4vc/test/src/soipv2_draft_type_test.dart @@ -0,0 +1,10 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:oidc4vc/oidc4vc.dart'; + +void main() { + group('SIOPV2DraftTypeX', () { + test('formattedString', () { + expect(SIOPV2DraftType.draft12.formattedString, 'Draft 12'); + }); + }); +} diff --git a/packages/oidc4vc/test/src/token_parameters/token_parameters_class.dart b/packages/oidc4vc/test/src/token_parameters/token_parameters_class.dart index 53a0ea956..701363be4 100644 --- a/packages/oidc4vc/test/src/token_parameters/token_parameters_class.dart +++ b/packages/oidc4vc/test/src/token_parameters/token_parameters_class.dart @@ -1,43 +1,72 @@ -// import 'package:flutter_test/flutter_test.dart'; -// import 'package:oidc4vc/src/token_parameters.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:oidc4vc/oidc4vc.dart'; -// import '../const_values.dart'; +import '../const_values.dart'; -// class TokenParameterTest { -// final tokenParameters = TokenParameters(privateKey, '', ''); +class TokenParameterTest { + final tokenParameters = TokenParameters( + privateKey: privateKey, + clientId: clientId, + did: clientId, + clientType: ClientType.did, + mediaType: MediaType.proofOfOwnership, + proofHeaderType: ProofHeaderType.kid, + kid: '', + ); -// void publicKeyTest() { -// expect(tokenParameters.publicJWK, publicJWK); -// } + void publicKeyTest() { + expect(tokenParameters.publicJWK, publicJWK); + } -// void didTest() { -// expect(tokenParameters.did, didKey); -// } + void didTest() { + expect(tokenParameters.did, clientId); + } -// void keyIdTest() { -// expect(tokenParameters.kid, kid); -// } + void keyIdTest() { + expect(tokenParameters.kid, kid); + } -// void algorithmIsES256KTest() { -// expect(tokenParameters.alg, ES256KAlg); -// } + void algorithmIsES256KTest() { + expect(tokenParameters.alg, ES256KAlg); + } -// void algorithmIsES256Test() { -// final tokenParameters = TokenParameters(privateKey2, '', ''); -// expect(tokenParameters.alg, ES256Alg); -// } + void algorithmIsES256Test() { + final tokenParameters = TokenParameters( + privateKey: privateKey2, + clientId: clientId, + did: clientId, + clientType: ClientType.did, + mediaType: MediaType.proofOfOwnership, + proofHeaderType: ProofHeaderType.kid, + ); + expect(tokenParameters.alg, ES256Alg); + } -// void algorithmIsNotNullTest() { -// final tokenParameters = TokenParameters(keyWithAlg, '', ''); -// expect(tokenParameters.alg, HS256Alg); -// } + void algorithmIsNotNullTest() { + final tokenParameters = TokenParameters( + privateKey: keyWithAlg, + clientId: clientId, + did: clientId, + clientType: ClientType.did, + mediaType: MediaType.proofOfOwnership, + proofHeaderType: ProofHeaderType.kid, + ); + expect(tokenParameters.alg, HS256Alg); + } -// // void thumprintOfKey() { -// // expect(tokenParameters.thumbprint, thumbprint); -// // } + void thumprintOfKey() { + expect(tokenParameters.thumbprint, thumbprint); + } -// // void thumprintOfKeyForrfc7638() { -// // final tokenParameters = TokenParameters(rfc7638Jwk, '', '', ''); -// // expect(tokenParameters.thumbprint, expectedThumbprintForrfc7638Jwk); -// // } -// } + void thumprintOfKeyForrfc7638() { + final tokenParameters = TokenParameters( + privateKey: rfc7638Jwk, + clientId: clientId, + did: clientId, + clientType: ClientType.did, + mediaType: MediaType.proofOfOwnership, + proofHeaderType: ProofHeaderType.kid, + ); + expect(tokenParameters.thumbprint, expectedThumbprintForrfc7638Jwk); + } +} diff --git a/packages/oidc4vc/test/src/token_parameters/token_parameters_test.dart b/packages/oidc4vc/test/src/token_parameters/token_parameters_test.dart index b32e6bb3d..ebb4006fc 100644 --- a/packages/oidc4vc/test/src/token_parameters/token_parameters_test.dart +++ b/packages/oidc4vc/test/src/token_parameters/token_parameters_test.dart @@ -1,130 +1,77 @@ -// // ignore: unnecessary_lambdas - -// // Copyright (c) 2022, Very Good Ventures -// // https://verygood.ventures -// // -// // Use of this source code is governed by an MIT-style -// // license that can be found in the LICENSE file or at -// // https://opensource.org/licenses/MIT. - -// // ignore_for_file: lines_longer_than_80_chars - -// import 'package:dio/dio.dart'; -// import 'package:flutter_test/flutter_test.dart'; -// import 'package:mocktail/mocktail.dart'; - -// import 'token_parameters_class.dart'; - -// class MockDio extends Mock implements Dio {} - -// void main() { -// group('TokenParameters', () { -// final tokenParametersTest = TokenParameterTest(); - -// // test( -// // 'public key is P-256K private key without d parameter', -// // tokenParametersTest.publicKeyTest, -// // ); - -// // test('did EBSI', tokenParametersTest.didTest); - -// // test('kID EBSI', tokenParametersTest.keyIdTest); - -// group('algorithm test', () { -// test( -// "algorithm is ES256K when key's curve is not P-256", -// tokenParametersTest.algorithmIsES256KTest, -// ); - -// test( -// "algorithm is ES256 when key's curve is P-256", -// tokenParametersTest.algorithmIsES256Test, -// ); - -// test( -// 'if alg is not null then return as it is', -// tokenParametersTest.algorithmIsNotNullTest, -// ); -// }); - -// // group('thumbprint test', () { -// // test('thumbprint of the public Key', tokenParametersTest.thumprintOfKey); - -// // test( -// // 'thumbrprint of the Key from exemple in rfc 7638', -// // tokenParametersTest.thumprintOfKeyForrfc7638, -// // ); -// // }); - -// // group('more didKey test', () { -// // test('did EBSI from Thierry s key', () { -// // final thierryPrivate1 = { -// // 'crv': 'secp256k1', -// // 'kty': 'EC', -// // 'x': 'XMO-urq7MgxkcpQDAJQY84lIO7sTo1Ab-_cqvUyreno', -// // 'y': 'sRnukSTqSCXUShUyDitq6MvTLr5F1ETs6xnR455WW_g' -// // }; - -// // const expectedDid = -// // 'did:ebsi:zkEcb5YVNX5ZRq3nZVH3FVWePLE6vxbqDattYsGan6iLi'; -// // final tokenParameters = TokenParameters(thierryPrivate1, '', '', ''); -// // expect(tokenParameters.didKey, expectedDid); -// // }); - -// // test('did EBSI 1 from Thierry vectors', () { -// // final thierryPrivate = { -// // 'crv': 'secp256k1', -// // 'd': '5DCgRx7Snk-ltE3exxHy94L6LPf8gBSb5_-U8NgRH10', -// // 'kty': 'EC', -// // 'x': 'XMO-urq7MgxkcpQDAJQY84lIO7sTo1Ab-_cqvUyreno', -// // 'y': 'sRnukSTqSCXUShUyDitq6MvTLr5F1ETs6xnR455WW_g' -// // }; -// // const expectedDid = -// // 'did:ebsi:zkEcb5YVNX5ZRq3nZVH3FVWePLE6vxbqDattYsGan6iLi'; -// // final tokenParameters = TokenParameters(thierryPrivate, '', '', ''); -// // expect(tokenParameters.didKey, expectedDid); -// // }); -// // test('did EBSI 2 from Thierry vectors', () { -// // final thierryPrivate = { -// // 'crv': 'secp256k1', -// // 'd': '2CYcyeeIGExqssjp0W3jzHdoEzSWHGBr0ukO66r0h2g', -// // 'kty': 'EC', -// // 'x': 'sg-ra2GWe8qoBIsBL2ZF7HjV71PP02nWuJLnTL2bn7E', -// // 'y': 'h8F81NtkFSHgyY_KCEjXhDfQEU_Jv0AEHMvR9EW65xs' -// // }; -// // const expectedDid = -// // 'did:ebsi:zcpweJw1cLnMM4yaWHRGjSKpuyGNq7SSTuQrBLp5tCpEz'; -// // final tokenParameters = TokenParameters(thierryPrivate, '', '', ''); -// // expect(tokenParameters.didKey, expectedDid); -// // }); -// // test('did EBSI 3 from Thierry vectors', () { -// // final thierryPrivate = { -// // 'crv': 'secp256k1', -// // 'd': 'zlZrsHYH8aeaJWu4ptTjNnDhgBGyFc0UguJ8N4zbsdA', -// // 'kty': 'EC', -// // 'x': 'hdEzHBKnhigtNoU7nIaxQJWIVTqVcuEZdpOKUzkAfkA', -// // 'y': 'Gq1zCf9H0_Wyo5nNyjR8IA-XgTkX1PYaBYb2WKYF3PQ' -// // }; -// // const expectedDid = -// // 'did:ebsi:zgfaTZiwnaK7k4Zf9ce9ydcYQKh76QJcpf2MFDeKYSj1c'; -// // final tokenParameters = TokenParameters(thierryPrivate, '', '', ''); -// // expect(tokenParameters.didKey, expectedDid); -// // }); - -// // test('did EBSI from Alice s key', () { -// // final aliceKey = { -// // 'crv': 'P-256', -// // 'd': 'd_PpSCGQWWgUc1t4iLLH8bKYlYfc9Zy_M7TsfOAcbg8', -// // 'kty': 'EC', -// // 'x': 'ngy44T1vxAT6Di4nr-UaM9K3Tlnz9pkoksDokKFkmNc', -// // 'y': 'QCRfOKlSM31GTkb4JHx3nXB4G_jSPMsbdjzlkT_UpPc', -// // }; - -// // const expectedDid = -// // 'did:ebsi:znxntxQrN369GsNyjFjYb8fuvU7g3sJGyYGwMTcUGdzuy'; -// // final tokenParameters = TokenParameters(aliceKey, '', '', ''); -// // expect(tokenParameters.didKey, expectedDid); -// // }); -// // }); -// }); -// } +// ignore: unnecessary_lambdas + +// Copyright (c) 2022, Very Good Ventures +// https://verygood.ventures +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +// ignore_for_file: lines_longer_than_80_chars + +import 'package:flutter_test/flutter_test.dart'; +import 'package:oidc4vc/oidc4vc.dart'; + +import 'token_parameters_class.dart'; + +void main() { + group('TokenParameters', () { + final tokenParametersTest = TokenParameterTest(); + + test( + 'public key is P-256K private key without d parameter', + tokenParametersTest.publicKeyTest, + ); + + test('did EBSI', tokenParametersTest.didTest); + + test('kID EBSI', tokenParametersTest.keyIdTest); + + group('algorithm test', () { + test( + "algorithm is ES256K when key's curve is not P-256", + tokenParametersTest.algorithmIsES256KTest, + ); + + test( + "algorithm is ES256 when key's curve is P-256", + tokenParametersTest.algorithmIsES256Test, + ); + + test( + 'if alg is not null then return as it is', + tokenParametersTest.algorithmIsNotNullTest, + ); + }); + + group('thumbprint test', () { + test('thumbprint of the public Key', tokenParametersTest.thumprintOfKey); + + test( + 'thumbrprint of the Key from exemple in rfc 7638', + tokenParametersTest.thumprintOfKeyForrfc7638, + ); + }); + + test('get alg', () { + const keyWithAlg = { + 'crv': 'P-256K', + 'd': 'ccWWNSjGiv1iWlNh4kfhWvwG3yyQMe8o31Du0uKRzrs', + 'kty': 'EC', + 'x': 'J4vQtLUyrVUiFIXRrtEq4xurmBZp2eq9wJmXkIA_stI', + 'y': 'EUU6vXoG3BGX2zzwjXrGDcr4EyDD0Vfk3_5fg5kSgKE', + 'alg': 'HS256', + }; + + final tokenParameters = TokenParameters( + privateKey: keyWithAlg, + clientId: '', + did: '', + clientType: ClientType.did, + mediaType: MediaType.proofOfOwnership, + proofHeaderType: ProofHeaderType.kid, + ); + expect(tokenParameters.alg, 'HS256'); + }); + }); +} diff --git a/packages/oidc4vc/test/src/vc_format_type_test.dart b/packages/oidc4vc/test/src/vc_format_type_test.dart new file mode 100644 index 000000000..deb3714d2 --- /dev/null +++ b/packages/oidc4vc/test/src/vc_format_type_test.dart @@ -0,0 +1,38 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:oidc4vc/oidc4vc.dart'; + +void main() { + group('VCFormatTypeX', () { + test('formattedString', () { + expect(VCFormatType.ldpVc.vcValue, 'ldp_vc'); + expect(VCFormatType.jwtVc.vcValue, 'jwt_vc'); + expect(VCFormatType.jwtVcJson.vcValue, 'jwt_vc_json'); + expect(VCFormatType.jwtVcJsonLd.vcValue, 'jwt_vc_json-ld'); + expect(VCFormatType.vcSdJWT.vcValue, 'vc+sd-jwt'); + }); + + test('value', () { + expect(VCFormatType.ldpVc.vpValue, 'ldp_vp'); + expect(VCFormatType.jwtVc.vpValue, 'jwt_vp'); + expect(VCFormatType.jwtVcJson.vpValue, 'jwt_vp_json'); + expect(VCFormatType.jwtVcJsonLd.vpValue, 'jwt_vp_json-ld'); + expect(VCFormatType.vcSdJWT.vpValue, 'vc+sd-jwt'); + }); + + test('urlValue', () { + expect(VCFormatType.ldpVc.urlValue, 'ldp_vc'); + expect(VCFormatType.jwtVc.urlValue, 'jwt_vc'); + expect(VCFormatType.jwtVcJson.urlValue, 'jwt_vc_json'); + expect(VCFormatType.jwtVcJsonLd.urlValue, 'jwt_vc_json-ld'); + expect(VCFormatType.vcSdJWT.urlValue, 'vcsd-jwt'); + }); + + test('supportCryptoCredential', () { + expect(VCFormatType.ldpVc.supportCryptoCredential, true); + expect(VCFormatType.jwtVc.supportCryptoCredential, false); + expect(VCFormatType.jwtVcJson.supportCryptoCredential, true); + expect(VCFormatType.jwtVcJsonLd.supportCryptoCredential, false); + expect(VCFormatType.vcSdJWT.supportCryptoCredential, false); + }); + }); +} diff --git a/packages/oidc4vc/test/src/verifier_token_parameters/verifier_token_parameters_class.dart b/packages/oidc4vc/test/src/verifier_token_parameters/verifier_token_parameters_class.dart index a681b97f1..44acfca68 100644 --- a/packages/oidc4vc/test/src/verifier_token_parameters/verifier_token_parameters_class.dart +++ b/packages/oidc4vc/test/src/verifier_token_parameters/verifier_token_parameters_class.dart @@ -1,56 +1,96 @@ -// import 'package:flutter_test/flutter_test.dart'; -// import 'package:oidc4vc/oidc4vc.dart'; - -// import '../const_values.dart'; -// import '../token_parameters/token_parameters_class.dart'; - -// class VerifierTokenParametersTest extends TokenParameterTest { -// final verifierTokenParameters = -// VerifierTokenParameters(privateKey, '', '', '', [], ''); - -// @override -// void publicKeyTest() { -// expect(verifierTokenParameters.publicJWK, publicJWK); -// } - -// @override -// void didTest() { -// expect(tokenParameters.did, didKey); -// } - -// @override -// void keyIdTest() { -// expect(tokenParameters.kid, kid); -// } - -// @override -// void algorithmIsES256KTest() { -// expect(tokenParameters.alg, ES256KAlg); -// } - -// @override -// void algorithmIsES256Test() { -// final tokenParameters = -// VerifierTokenParameters(privateKey2, '', '', '', [], ''); -// expect(tokenParameters.alg, ES256Alg); -// } - -// @override -// void algorithmIsNotNullTest() { -// final tokenParameters = -// VerifierTokenParameters(keyWithAlg, '', '', '', [], ''); -// expect(tokenParameters.alg, HS256Alg); -// } - -// // @override -// // void thumprintOfKey() { -// // expect(tokenParameters.thumbprint, thumbprint); -// // } - -// // @override -// // void thumprintOfKeyForrfc7638() { -// // final tokenParameters2 = -// // VerifierTokenParameters(rfc7638Jwk, '', '', '', Uri.parse(''), []); -// // expect(tokenParameters2.thumbprint, expectedThumbprintForrfc7638Jwk); -// // } -// } +import 'package:flutter_test/flutter_test.dart'; +import 'package:oidc4vc/oidc4vc.dart'; + +import '../const_values.dart'; +import '../token_parameters/token_parameters_class.dart'; + +class VerifierTokenParametersTest extends TokenParameterTest { + final verifierTokenParameters = VerifierTokenParameters( + privateKey: privateKey, + clientId: clientId, + did: clientId, + clientType: ClientType.did, + mediaType: MediaType.proofOfOwnership, + proofHeaderType: ProofHeaderType.kid, + audience: '', + credentials: [], + kid: '', + nonce: '', + ); + + @override + void publicKeyTest() { + expect(verifierTokenParameters.publicJWK, publicJWK); + } + + @override + void didTest() { + expect(tokenParameters.did, clientId); + } + + @override + void keyIdTest() { + expect(tokenParameters.kid, kid); + } + + @override + void algorithmIsES256KTest() { + expect(tokenParameters.alg, ES256KAlg); + } + + @override + void algorithmIsES256Test() { + final tokenParameters = VerifierTokenParameters( + privateKey: privateKey2, + clientId: clientId, + did: clientId, + clientType: ClientType.did, + mediaType: MediaType.proofOfOwnership, + proofHeaderType: ProofHeaderType.kid, + audience: '', + credentials: [], + kid: '', + nonce: '', + ); + expect(tokenParameters.alg, ES256Alg); + } + + @override + void algorithmIsNotNullTest() { + final tokenParameters = VerifierTokenParameters( + privateKey: keyWithAlg, + clientId: clientId, + did: clientId, + clientType: ClientType.did, + mediaType: MediaType.proofOfOwnership, + proofHeaderType: ProofHeaderType.kid, + audience: '', + credentials: [], + kid: '', + nonce: '', + ); + expect(tokenParameters.alg, HS256Alg); + } + + @override + void thumprintOfKey() { + expect(tokenParameters.thumbprint, thumbprint); + } + + @override + void thumprintOfKeyForrfc7638() { + final tokenParameters2 = VerifierTokenParameters( + privateKey: rfc7638Jwk, + clientId: clientId, + did: clientId, + clientType: ClientType.did, + mediaType: MediaType.proofOfOwnership, + proofHeaderType: ProofHeaderType.kid, + audience: '', + credentials: [], + kid: '', + nonce: '', + ); + expect(tokenParameters2.thumbprint, expectedThumbprintForrfc7638Jwk); + } +} diff --git a/packages/oidc4vc/test/src/verifier_token_parameters/verifier_token_parameters_test.dart b/packages/oidc4vc/test/src/verifier_token_parameters/verifier_token_parameters_test.dart index f22b2edb4..89743d4f2 100644 --- a/packages/oidc4vc/test/src/verifier_token_parameters/verifier_token_parameters_test.dart +++ b/packages/oidc4vc/test/src/verifier_token_parameters/verifier_token_parameters_test.dart @@ -1,187 +1,194 @@ -// // Copyright (c) 2022, Very Good Ventures -// // https://verygood.ventures -// // -// // Use of this source code is governed by an MIT-style -// // license that can be found in the LICENSE file or at -// // https://opensource.org/licenses/MIT. - -// import 'package:flutter_test/flutter_test.dart'; -// import 'package:oidc4vc/oidc4vc.dart'; - -// import 'verifier_token_parameters_class.dart'; - -// void main() { -// group('Verifier TokenParameters', () { -// const privateKey = { -// 'crv': 'P-256K', -// 'd': 'ccWWNSjGiv1iWlNh4kfhWvwG3yyQMe8o31Du0uKRzrs', -// 'kty': 'EC', -// 'x': 'J4vQtLUyrVUiFIXRrtEq4xurmBZp2eq9wJmXkIA_stI', -// 'y': 'EUU6vXoG3BGX2zzwjXrGDcr4EyDD0Vfk3_5fg5kSgKE', -// }; - -// const credentialsToBePresented = [ -// r'{"id":"urn:uuid:6b1d8411-9ed5-4566-9c7f-4c24165ff236","receivedId":"urn:uuid:6b1d8411-9ed5-4566-9c7f-4c24165ff236","image":null,"data":{"@context":["https://www.w3.org/2018/credentials/v1"],"credentialSchema":{"id":"https://api.preprod.ebsi.eu/trusted-schemas-registry/v1/schemas/0xbf78fc08a7a9f28f5479f58dea269d3657f54f13ca37d380cd4e92237fb691dd","type":"JsonSchemaValidator2018"},"credentialStatus":{"id":"https://essif.europa.eu/status/education#higherEducation#392ac7f6-399a-437b-a268-4691ead8f176","type":"CredentialStatusList2020"},"credentialSubject":{"awardingOpportunity":{"awardingBody":{"eidasLegalIdentifier":"Unknown","homepage":"https://leaston.bcdiploma.com/","id":"did:ebsi:zdRvvKbXhVVBsXhatjuiBhs","preferredName":"Leaston University","registration":"0597065J"},"endedAtTime":"2020-06-26T00:00:00Z","id":"https://leaston.bcdiploma.com/law-economics-management#AwardingOpportunity","identifier":"https://certificate-demo.bcdiploma.com/check/87ED2F2270E6C41456E94B86B9D9115B4E35BCCAD200A49B846592C14F79C86BV1Fnbllta0NZTnJkR3lDWlRmTDlSRUJEVFZISmNmYzJhUU5sZUJ5Z2FJSHpWbmZZ","location":"FRANCE","startedAtTime":"2019-09-02T00:00:00Z"},"dateOfBirth":"1993-04-08","familyName":"DOE","givenNames":"Jane","gradingScheme":{"id":"https://leaston.bcdiploma.com/law-economics-management#GradingScheme","title":"2 year full-time programme / 4 semesters"},"id":"did:ebsi:zoxRGVZQndTfQk54B7tKdwwNdhai5gm9F8Nav8eceNABa","identifier":"0904008084H","learningAchievement":{"additionalNote":["DISTRIBUTION MANAGEMENT"],"description":"The Master in Information and Computer Sciences (MICS) at the University of Luxembourg enables students to acquire deeper knowledge in computer science by understanding its abstract and interdisciplinary foundations, focusing on problem solving and developing lifelong learning skills.","id":"https://leaston.bcdiploma.com/law-economics-management#LearningAchievment","title":"Master in Information and Computer Sciences"},"learningSpecification":{"ectsCreditPoints":120,"eqfLevel":7,"id":"https://leaston.bcdiploma.com/law-economics-management#LearningSpecification","iscedfCode":["7"],"nqfLevel":["7"]}},"evidence":{"documentPresence":["Physical"],"evidenceDocument":["Passport"],"id":"https://essif.europa.eu/tsr-va/evidence/f2aeec97-fc0d-42bf-8ca7-0548192d5678","subjectPresence":"Physical","type":["DocumentVerification"],"verifier":"did:ebsi:2962fb784df61baa267c8132497539f8c674b37c1244a7a"},"id":"urn:uuid:6b1d8411-9ed5-4566-9c7f-4c24165ff236","issuanceDate":"2023-02-08T15:10:56Z","issued":"2023-02-08T15:10:56Z","issuer":"did:ebsi:zhSw5rPXkcHjvquwnVcTzzB","proof":{"created":"2022-04-27T12:25:07Z","creator":"did:ebsi:zdRvvKbXhVVBsXhatjuiBhs","domain":"https://api.preprod.ebsi.eu","jws":"eyJiNjQiOmZhbHNlLCJjcml0IjpbImI2NCJdLCJhbGciOiJFUzI1NksifQ..mIBnM8XDQqSYKQNX_LvaJhmsbyCr5OZ5cU2Zk-ReqLpr4doFsgmoobkO5128tZy-8KimVjJkGw0wL1uBWnMLWQ","nonce":"3ea68dae-d07a-4daa-932b-fbb58f5c20c4","type":"EcdsaSecp256k1Signature2019"},"type":["VerifiableCredential","VerifiableAttestation","VerifiableDiploma"],"validFrom":"2023-02-08T15:10:56Z"},"shareLink":"","credentialPreview":{"id":"urn:uuid:6b1d8411-9ed5-4566-9c7f-4c24165ff236","type":["VerifiableCredential","VerifiableAttestation","VerifiableDiploma"],"issuer":"did:ebsi:zhSw5rPXkcHjvquwnVcTzzB","description":[],"name":[],"issuanceDate":"2023-02-08T15:10:56Z","proof":[{"type":"EcdsaSecp256k1Signature2019","proofPurpose":null,"verificationMethod":null,"created":"2022-04-27T12:25:07Z","jws":"eyJiNjQiOmZhbHNlLCJjcml0IjpbImI2NCJdLCJhbGciOiJFUzI1NksifQ..mIBnM8XDQqSYKQNX_LvaJhmsbyCr5OZ5cU2Zk-ReqLpr4doFsgmoobkO5128tZy-8KimVjJkGw0wL1uBWnMLWQ"}],"credentialSubject":{"id":"did:ebsi:zoxRGVZQndTfQk54B7tKdwwNdhai5gm9F8Nav8eceNABa","type":null,"issuedBy":{"name":""}},"evidence":[{"id":"https://essif.europa.eu/tsr-va/evidence/f2aeec97-fc0d-42bf-8ca7-0548192d5678","type":["DocumentVerification"]}],"credentialStatus":{"id":"https://essif.europa.eu/status/education#higherEducation#392ac7f6-399a-437b-a268-4691ead8f176","type":"CredentialStatusList2020","revocationListIndex":"","revocationListCredential":""}},"display":{"backgroundColor":"","icon":"","nameFallback":"","descriptionFallback":""},"expirationDate":null,"credential_manifest":{"id":"VerifiableDiploma_1","issuer":{"id":"did:ebsi:zhSw5rPXkcHjvquwnVcTzzB","name":"Test EBSILUX"},"output_descriptors":[{"id":"diploma_01","schema":"https://api.preprod.ebsi.eu/trusted-schemas-registry/v1/schemas/0xbf78fc08a7a9f28f5479f58dea269d3657f54f13ca37d380cd4e92237fb691dd","name":null,"description":null,"styles":null,"display":{"title":{"path":[],"schema":{"type":"string","format":null},"fallback":"Diploma"},"subtitle":{"path":[],"schema":{"type":"string","format":null},"fallback":"EBSI Verifiable diploma"},"description":{"path":[],"schema":{"type":"string","format":null},"fallback":"This card is a proof that you passed this diploma successfully. You can use this card when you need to prove this information to services that have adopted EU EBSI framework."},"properties":[{"label":"First name","path":["$.credentialSubject.firstName"],"schema":{"type":"string","format":null},"fallback":"Unknown"},{"label":"Last name","path":["$.credentialSubject.familyName"],"schema":{"type":"string","format":null},"fallback":"Unknown"},{"label":"Birth date","path":["$.credentialSubject.dateOfBirth"],"schema":{"type":"string","format":"date"},"fallback":"Unknown"},{"label":"Grading scheme","path":["$.credentialSubject.gradingScheme.title"],"schema":{"type":"string","format":null},"fallback":"Unknown"},{"label":"Title","path":["$.credentialSubject.learningAchievement.title"],"schema":{"type":"string","format":null},"fallback":"Unknown"},{"label":"Description","path":["$.credentialSubject.learningAchievement.description"],"schema":{"type":"string","format":null},"fallback":"Unknown"},{"label":"ECTS Points","path":["$.credentialSubject.learningSpecification.ectsCreditPoints"],"schema":{"type":"number","format":null},"fallback":"Unknown"},{"label":"Issue date","path":["$.issuanceDate"],"schema":{"type":"string","format":"date"},"fallback":"Unknown"},{"label":"Issued by","path":["$.credentialSubject.awardingOpportunity.awardingBody.preferredName"],"schema":{"type":"string","format":null},"fallback":"Unknown"},{"label":"Registration","path":["$.credentialSubject.awardingOpportunity.awardingBody.registration"],"schema":{"type":"string","format":null},"fallback":"Unknown"},{"label":"Website","path":["$.credentialSubject.awardingOpportunity.awardingBody.homepage"],"schema":{"type":"string","format":"uri"},"fallback":"Unknown"}]}}],"presentation_definition":null},"challenge":null,"domain":null,"activities":[{"acquisitionAt":"2023-02-08T20:41:02.024360","presentation":null}],"jwt":"eyJhbGciOiJFUzI1NksiLCJraWQiOiJkaWQ6ZWJzaTp6aFN3NXJQWGtjSGp2cXV3blZjVHp6QiM1MTVhOWM0MzZjMGYyYWQzYWI2NWQ2Y2VmYzVjMWYwNmMwNWI4YWRmY2Y1NGVlMDZkYzgwNTQzMjA0NzBmZmFmIiwidHlwIjoiSldUIn0.eyJleHAiOjE2NzU4NzAwNTYuMTcxMDgyLCJpYXQiOjE2NzU4NjkwNTYuMTcxMDc1LCJpc3MiOiJkaWQ6ZWJzaTp6aFN3NXJQWGtjSGp2cXV3blZjVHp6QiIsImp0aSI6InVybjp1dWlkOjZiMWQ4NDExLTllZDUtNDU2Ni05YzdmLTRjMjQxNjVmZjIzNiIsIm5iZiI6MTY3NTg2OTA1Ni4xNzEwOCwibm9uY2UiOiJjOGM3Nzg0Yi1hN2MyLTExZWQtOGUwMS0wYTE2Mjg5NTg1NjAiLCJzdWIiOiJkaWQ6ZWJzaTp6b3hSR1ZaUW5kVGZRazU0Qjd0S2R3d05kaGFpNWdtOUY4TmF2OGVjZU5BQmEiLCJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSJdLCJjcmVkZW50aWFsU2NoZW1hIjp7ImlkIjoiaHR0cHM6Ly9hcGkucHJlcHJvZC5lYnNpLmV1L3RydXN0ZWQtc2NoZW1hcy1yZWdpc3RyeS92MS9zY2hlbWFzLzB4YmY3OGZjMDhhN2E5ZjI4ZjU0NzlmNThkZWEyNjlkMzY1N2Y1NGYxM2NhMzdkMzgwY2Q0ZTkyMjM3ZmI2OTFkZCIsInR5cGUiOiJKc29uU2NoZW1hVmFsaWRhdG9yMjAxOCJ9LCJjcmVkZW50aWFsU3RhdHVzIjp7ImlkIjoiaHR0cHM6Ly9lc3NpZi5ldXJvcGEuZXUvc3RhdHVzL2VkdWNhdGlvbiNoaWdoZXJFZHVjYXRpb24jMzkyYWM3ZjYtMzk5YS00MzdiLWEyNjgtNDY5MWVhZDhmMTc2IiwidHlwZSI6IkNyZWRlbnRpYWxTdGF0dXNMaXN0MjAyMCJ9LCJjcmVkZW50aWFsU3ViamVjdCI6eyJhd2FyZGluZ09wcG9ydHVuaXR5Ijp7ImF3YXJkaW5nQm9keSI6eyJlaWRhc0xlZ2FsSWRlbnRpZmllciI6IlVua25vd24iLCJob21lcGFnZSI6Imh0dHBzOi8vbGVhc3Rvbi5iY2RpcGxvbWEuY29tLyIsImlkIjoiZGlkOmVic2k6emRSdnZLYlhoVlZCc1hoYXRqdWlCaHMiLCJwcmVmZXJyZWROYW1lIjoiTGVhc3RvbiBVbml2ZXJzaXR5IiwicmVnaXN0cmF0aW9uIjoiMDU5NzA2NUoifSwiZW5kZWRBdFRpbWUiOiIyMDIwLTA2LTI2VDAwOjAwOjAwWiIsImlkIjoiaHR0cHM6Ly9sZWFzdG9uLmJjZGlwbG9tYS5jb20vbGF3LWVjb25vbWljcy1tYW5hZ2VtZW50I0F3YXJkaW5nT3Bwb3J0dW5pdHkiLCJpZGVudGlmaWVyIjoiaHR0cHM6Ly9jZXJ0aWZpY2F0ZS1kZW1vLmJjZGlwbG9tYS5jb20vY2hlY2svODdFRDJGMjI3MEU2QzQxNDU2RTk0Qjg2QjlEOTExNUI0RTM1QkNDQUQyMDBBNDlCODQ2NTkyQzE0Rjc5Qzg2QlYxRm5ibGx0YTBOWlRuSmtSM2xEV2xSbVREbFNSVUpFVkZaSVNtTm1ZekpoVVU1c1pVSjVaMkZKU0hwV2JtWloiLCJsb2NhdGlvbiI6IkZSQU5DRSIsInN0YXJ0ZWRBdFRpbWUiOiIyMDE5LTA5LTAyVDAwOjAwOjAwWiJ9LCJkYXRlT2ZCaXJ0aCI6IjE5OTMtMDQtMDgiLCJmYW1pbHlOYW1lIjoiRE9FIiwiZ2l2ZW5OYW1lcyI6IkphbmUiLCJncmFkaW5nU2NoZW1lIjp7ImlkIjoiaHR0cHM6Ly9sZWFzdG9uLmJjZGlwbG9tYS5jb20vbGF3LWVjb25vbWljcy1tYW5hZ2VtZW50I0dyYWRpbmdTY2hlbWUiLCJ0aXRsZSI6IjIgeWVhciBmdWxsLXRpbWUgcHJvZ3JhbW1lIC8gNCBzZW1lc3RlcnMifSwiaWQiOiJkaWQ6ZWJzaTp6b3hSR1ZaUW5kVGZRazU0Qjd0S2R3d05kaGFpNWdtOUY4TmF2OGVjZU5BQmEiLCJpZGVudGlmaWVyIjoiMDkwNDAwODA4NEgiLCJsZWFybmluZ0FjaGlldmVtZW50Ijp7ImFkZGl0aW9uYWxOb3RlIjpbIkRJU1RSSUJVVElPTiBNQU5BR0VNRU5UIl0sImRlc2NyaXB0aW9uIjoiVGhlIE1hc3RlciBpbiBJbmZvcm1hdGlvbiBhbmQgQ29tcHV0ZXIgU2NpZW5jZXMgKE1JQ1MpIGF0IHRoZSBVbml2ZXJzaXR5IG9mIEx1eGVtYm91cmcgZW5hYmxlcyBzdHVkZW50cyB0byBhY3F1aXJlIGRlZXBlciBrbm93bGVkZ2UgaW4gY29tcHV0ZXIgc2NpZW5jZSBieSB1bmRlcnN0YW5kaW5nIGl0cyBhYnN0cmFjdCBhbmQgaW50ZXJkaXNjaXBsaW5hcnkgZm91bmRhdGlvbnMsIGZvY3VzaW5nIG9uIHByb2JsZW0gc29sdmluZyBhbmQgZGV2ZWxvcGluZyBsaWZlbG9uZyBsZWFybmluZyBza2lsbHMuIiwiaWQiOiJodHRwczovL2xlYXN0b24uYmNkaXBsb21hLmNvbS9sYXctZWNvbm9taWNzLW1hbmFnZW1lbnQjTGVhcm5pbmdBY2hpZXZtZW50IiwidGl0bGUiOiJNYXN0ZXIgaW4gSW5mb3JtYXRpb24gYW5kIENvbXB1dGVyIFNjaWVuY2VzIn0sImxlYXJuaW5nU3BlY2lmaWNhdGlvbiI6eyJlY3RzQ3JlZGl0UG9pbnRzIjoxMjAsImVxZkxldmVsIjo3LCJpZCI6Imh0dHBzOi8vbGVhc3Rvbi5iY2RpcGxvbWEuY29tL2xhdy1lY29ub21pY3MtbWFuYWdlbWVudCNMZWFybmluZ1NwZWNpZmljYXRpb24iLCJpc2NlZGZDb2RlIjpbIjciXSwibnFmTGV2ZWwiOlsiNyJdfX0sImV2aWRlbmNlIjp7ImRvY3VtZW50UHJlc2VuY2UiOlsiUGh5c2ljYWwiXSwiZXZpZGVuY2VEb2N1bWVudCI6WyJQYXNzcG9ydCJdLCJpZCI6Imh0dHBzOi8vZXNzaWYuZXVyb3BhLmV1L3Rzci12YS9ldmlkZW5jZS9mMmFlZWM5Ny1mYzBkLTQyYmYtOGNhNy0wNTQ4MTkyZDU2NzgiLCJzdWJqZWN0UHJlc2VuY2UiOiJQaHlzaWNhbCIsInR5cGUiOlsiRG9jdW1lbnRWZXJpZmljYXRpb24iXSwidmVyaWZpZXIiOiJkaWQ6ZWJzaToyOTYyZmI3ODRkZjYxYmFhMjY3YzgxMzI0OTc1MzlmOGM2NzRiMzdjMTI0NGE3YSJ9LCJpZCI6InVybjp1dWlkOjZiMWQ4NDExLTllZDUtNDU2Ni05YzdmLTRjMjQxNjVmZjIzNiIsImlzc3VhbmNlRGF0ZSI6IjIwMjMtMDItMDhUMTU6MTA6NTZaIiwiaXNzdWVkIjoiMjAyMy0wMi0wOFQxNToxMDo1NloiLCJpc3N1ZXIiOiJkaWQ6ZWJzaTp6aFN3NXJQWGtjSGp2cXV3blZjVHp6QiIsInByb29mIjp7ImNyZWF0ZWQiOiIyMDIyLTA0LTI3VDEyOjI1OjA3WiIsImNyZWF0b3IiOiJkaWQ6ZWJzaTp6ZFJ2dktiWGhWVkJzWGhhdGp1aUJocyIsImRvbWFpbiI6Imh0dHBzOi8vYXBpLnByZXByb2QuZWJzaS5ldSIsImp3cyI6ImV5SmlOalFpT21aaGJITmxMQ0pqY21sMElqcGJJbUkyTkNKZExDSmhiR2NpT2lKRlV6STFOa3NpZlEuLm1JQm5NOFhEUXFTWUtRTlhfTHZhSmhtc2J5Q3I1T1o1Y1UyWmstUmVxTHByNGRvRnNnbW9vYmtPNTEyOHRaeS04S2ltVmpKa0d3MHdMMXVCV25NTFdRIiwibm9uY2UiOiIzZWE2OGRhZS1kMDdhLTRkYWEtOTMyYi1mYmI1OGY1YzIwYzQiLCJ0eXBlIjoiRWNkc2FTZWNwMjU2azFTaWduYXR1cmUyMDE5In0sInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiLCJWZXJpZmlhYmxlQXR0ZXN0YXRpb24iLCJWZXJpZmlhYmxlRGlwbG9tYSJdLCJ2YWxpZEZyb20iOiIyMDIzLTAyLTA4VDE1OjEwOjU2WiJ9fQ.avhpj0UC7SJiOo8PWJV3zhJoxngdmrBnyTkQsnqe76JlOmatLkgAY5Mp_sUJQnN1uENiT-_KBf0KY3izx4X_SA"}', -// r'{"id":"urn:uuid:6b1d8411-9ed5-4566-9c7f-4c24165ff236","receivedId":"urn:uuid:6b1d8411-9ed5-4566-9c7f-4c24165ff236","image":null,"data":{"@context":["https://www.w3.org/2018/credentials/v1"],"credentialSchema":{"id":"https://api.preprod.ebsi.eu/trusted-schemas-registry/v1/schemas/0xbf78fc08a7a9f28f5479f58dea269d3657f54f13ca37d380cd4e92237fb691dd","type":"JsonSchemaValidator2018"},"credentialStatus":{"id":"https://essif.europa.eu/status/education#higherEducation#392ac7f6-399a-437b-a268-4691ead8f176","type":"CredentialStatusList2020"},"credentialSubject":{"awardingOpportunity":{"awardingBody":{"eidasLegalIdentifier":"Unknown","homepage":"https://leaston.bcdiploma.com/","id":"did:ebsi:zdRvvKbXhVVBsXhatjuiBhs","preferredName":"Leaston University","registration":"0597065J"},"endedAtTime":"2020-06-26T00:00:00Z","id":"https://leaston.bcdiploma.com/law-economics-management#AwardingOpportunity","identifier":"https://certificate-demo.bcdiploma.com/check/87ED2F2270E6C41456E94B86B9D9115B4E35BCCAD200A49B846592C14F79C86BV1Fnbllta0NZTnJkR3lDWlRmTDlSRUJEVFZISmNmYzJhUU5sZUJ5Z2FJSHpWbmZZ","location":"FRANCE","startedAtTime":"2019-09-02T00:00:00Z"},"dateOfBirth":"1993-04-08","familyName":"DOE","givenNames":"Jane","gradingScheme":{"id":"https://leaston.bcdiploma.com/law-economics-management#GradingScheme","title":"2 year full-time programme / 4 semesters"},"id":"did:ebsi:zoxRGVZQndTfQk54B7tKdwwNdhai5gm9F8Nav8eceNABa","identifier":"0904008084H","learningAchievement":{"additionalNote":["DISTRIBUTION MANAGEMENT"],"description":"The Master in Information and Computer Sciences (MICS) at the University of Luxembourg enables students to acquire deeper knowledge in computer science by understanding its abstract and interdisciplinary foundations, focusing on problem solving and developing lifelong learning skills.","id":"https://leaston.bcdiploma.com/law-economics-management#LearningAchievment","title":"Master in Information and Computer Sciences"},"learningSpecification":{"ectsCreditPoints":120,"eqfLevel":7,"id":"https://leaston.bcdiploma.com/law-economics-management#LearningSpecification","iscedfCode":["7"],"nqfLevel":["7"]}},"evidence":{"documentPresence":["Physical"],"evidenceDocument":["Passport"],"id":"https://essif.europa.eu/tsr-va/evidence/f2aeec97-fc0d-42bf-8ca7-0548192d5678","subjectPresence":"Physical","type":["DocumentVerification"],"verifier":"did:ebsi:2962fb784df61baa267c8132497539f8c674b37c1244a7a"},"id":"urn:uuid:6b1d8411-9ed5-4566-9c7f-4c24165ff236","issuanceDate":"2023-02-08T15:10:56Z","issued":"2023-02-08T15:10:56Z","issuer":"did:ebsi:zhSw5rPXkcHjvquwnVcTzzB","proof":{"created":"2022-04-27T12:25:07Z","creator":"did:ebsi:zdRvvKbXhVVBsXhatjuiBhs","domain":"https://api.preprod.ebsi.eu","jws":"eyJiNjQiOmZhbHNlLCJjcml0IjpbImI2NCJdLCJhbGciOiJFUzI1NksifQ..mIBnM8XDQqSYKQNX_LvaJhmsbyCr5OZ5cU2Zk-ReqLpr4doFsgmoobkO5128tZy-8KimVjJkGw0wL1uBWnMLWQ","nonce":"3ea68dae-d07a-4daa-932b-fbb58f5c20c4","type":"EcdsaSecp256k1Signature2019"},"type":["VerifiableCredential","VerifiableAttestation","VerifiableDiploma"],"validFrom":"2023-02-08T15:10:56Z"},"shareLink":"","credentialPreview":{"id":"urn:uuid:6b1d8411-9ed5-4566-9c7f-4c24165ff236","type":["VerifiableCredential","VerifiableAttestation","VerifiableDiploma"],"issuer":"did:ebsi:zhSw5rPXkcHjvquwnVcTzzB","description":[],"name":[],"issuanceDate":"2023-02-08T15:10:56Z","proof":[{"type":"EcdsaSecp256k1Signature2019","proofPurpose":null,"verificationMethod":null,"created":"2022-04-27T12:25:07Z","jws":"eyJiNjQiOmZhbHNlLCJjcml0IjpbImI2NCJdLCJhbGciOiJFUzI1NksifQ..mIBnM8XDQqSYKQNX_LvaJhmsbyCr5OZ5cU2Zk-ReqLpr4doFsgmoobkO5128tZy-8KimVjJkGw0wL1uBWnMLWQ"}],"credentialSubject":{"id":"did:ebsi:zoxRGVZQndTfQk54B7tKdwwNdhai5gm9F8Nav8eceNABa","type":null,"issuedBy":{"name":""}},"evidence":[{"id":"https://essif.europa.eu/tsr-va/evidence/f2aeec97-fc0d-42bf-8ca7-0548192d5678","type":["DocumentVerification"]}],"credentialStatus":{"id":"https://essif.europa.eu/status/education#higherEducation#392ac7f6-399a-437b-a268-4691ead8f176","type":"CredentialStatusList2020","revocationListIndex":"","revocationListCredential":""}},"display":{"backgroundColor":"","icon":"","nameFallback":"","descriptionFallback":""},"expirationDate":null,"credential_manifest":{"id":"VerifiableDiploma_1","issuer":{"id":"did:ebsi:zhSw5rPXkcHjvquwnVcTzzB","name":"Test EBSILUX"},"output_descriptors":[{"id":"diploma_01","schema":"https://api.preprod.ebsi.eu/trusted-schemas-registry/v1/schemas/0xbf78fc08a7a9f28f5479f58dea269d3657f54f13ca37d380cd4e92237fb691dd","name":null,"description":null,"styles":null,"display":{"title":{"path":[],"schema":{"type":"string","format":null},"fallback":"Diploma"},"subtitle":{"path":[],"schema":{"type":"string","format":null},"fallback":"EBSI Verifiable diploma"},"description":{"path":[],"schema":{"type":"string","format":null},"fallback":"This card is a proof that you passed this diploma successfully. You can use this card when you need to prove this information to services that have adopted EU EBSI framework."},"properties":[{"label":"First name","path":["$.credentialSubject.firstName"],"schema":{"type":"string","format":null},"fallback":"Unknown"},{"label":"Last name","path":["$.credentialSubject.familyName"],"schema":{"type":"string","format":null},"fallback":"Unknown"},{"label":"Birth date","path":["$.credentialSubject.dateOfBirth"],"schema":{"type":"string","format":"date"},"fallback":"Unknown"},{"label":"Grading scheme","path":["$.credentialSubject.gradingScheme.title"],"schema":{"type":"string","format":null},"fallback":"Unknown"},{"label":"Title","path":["$.credentialSubject.learningAchievement.title"],"schema":{"type":"string","format":null},"fallback":"Unknown"},{"label":"Description","path":["$.credentialSubject.learningAchievement.description"],"schema":{"type":"string","format":null},"fallback":"Unknown"},{"label":"ECTS Points","path":["$.credentialSubject.learningSpecification.ectsCreditPoints"],"schema":{"type":"number","format":null},"fallback":"Unknown"},{"label":"Issue date","path":["$.issuanceDate"],"schema":{"type":"string","format":"date"},"fallback":"Unknown"},{"label":"Issued by","path":["$.credentialSubject.awardingOpportunity.awardingBody.preferredName"],"schema":{"type":"string","format":null},"fallback":"Unknown"},{"label":"Registration","path":["$.credentialSubject.awardingOpportunity.awardingBody.registration"],"schema":{"type":"string","format":null},"fallback":"Unknown"},{"label":"Website","path":["$.credentialSubject.awardingOpportunity.awardingBody.homepage"],"schema":{"type":"string","format":"uri"},"fallback":"Unknown"}]}}],"presentation_definition":null},"challenge":null,"domain":null,"activities":[{"acquisitionAt":"2023-02-08T20:41:02.024360","presentation":null}]}', -// ]; - -// final verifierTokenParameters = VerifierTokenParameters( -// privateKey, -// '', -// '', -// '', -// credentialsToBePresented, -// '', -// ); - -// const nonce = '69165b47-a851-11ed-bd52-0a1628958560'; - -// const audience = 'xjcqarovuv'; - -// const jwtsOfCredentials = [ -// // ignore: lines_longer_than_80_chars -// 'eyJhbGciOiJFUzI1NksiLCJraWQiOiJkaWQ6ZWJzaTp6aFN3NXJQWGtjSGp2cXV3blZjVHp6QiM1MTVhOWM0MzZjMGYyYWQzYWI2NWQ2Y2VmYzVjMWYwNmMwNWI4YWRmY2Y1NGVlMDZkYzgwNTQzMjA0NzBmZmFmIiwidHlwIjoiSldUIn0.eyJleHAiOjE2NzU4NzAwNTYuMTcxMDgyLCJpYXQiOjE2NzU4NjkwNTYuMTcxMDc1LCJpc3MiOiJkaWQ6ZWJzaTp6aFN3NXJQWGtjSGp2cXV3blZjVHp6QiIsImp0aSI6InVybjp1dWlkOjZiMWQ4NDExLTllZDUtNDU2Ni05YzdmLTRjMjQxNjVmZjIzNiIsIm5iZiI6MTY3NTg2OTA1Ni4xNzEwOCwibm9uY2UiOiJjOGM3Nzg0Yi1hN2MyLTExZWQtOGUwMS0wYTE2Mjg5NTg1NjAiLCJzdWIiOiJkaWQ6ZWJzaTp6b3hSR1ZaUW5kVGZRazU0Qjd0S2R3d05kaGFpNWdtOUY4TmF2OGVjZU5BQmEiLCJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSJdLCJjcmVkZW50aWFsU2NoZW1hIjp7ImlkIjoiaHR0cHM6Ly9hcGkucHJlcHJvZC5lYnNpLmV1L3RydXN0ZWQtc2NoZW1hcy1yZWdpc3RyeS92MS9zY2hlbWFzLzB4YmY3OGZjMDhhN2E5ZjI4ZjU0NzlmNThkZWEyNjlkMzY1N2Y1NGYxM2NhMzdkMzgwY2Q0ZTkyMjM3ZmI2OTFkZCIsInR5cGUiOiJKc29uU2NoZW1hVmFsaWRhdG9yMjAxOCJ9LCJjcmVkZW50aWFsU3RhdHVzIjp7ImlkIjoiaHR0cHM6Ly9lc3NpZi5ldXJvcGEuZXUvc3RhdHVzL2VkdWNhdGlvbiNoaWdoZXJFZHVjYXRpb24jMzkyYWM3ZjYtMzk5YS00MzdiLWEyNjgtNDY5MWVhZDhmMTc2IiwidHlwZSI6IkNyZWRlbnRpYWxTdGF0dXNMaXN0MjAyMCJ9LCJjcmVkZW50aWFsU3ViamVjdCI6eyJhd2FyZGluZ09wcG9ydHVuaXR5Ijp7ImF3YXJkaW5nQm9keSI6eyJlaWRhc0xlZ2FsSWRlbnRpZmllciI6IlVua25vd24iLCJob21lcGFnZSI6Imh0dHBzOi8vbGVhc3Rvbi5iY2RpcGxvbWEuY29tLyIsImlkIjoiZGlkOmVic2k6emRSdnZLYlhoVlZCc1hoYXRqdWlCaHMiLCJwcmVmZXJyZWROYW1lIjoiTGVhc3RvbiBVbml2ZXJzaXR5IiwicmVnaXN0cmF0aW9uIjoiMDU5NzA2NUoifSwiZW5kZWRBdFRpbWUiOiIyMDIwLTA2LTI2VDAwOjAwOjAwWiIsImlkIjoiaHR0cHM6Ly9sZWFzdG9uLmJjZGlwbG9tYS5jb20vbGF3LWVjb25vbWljcy1tYW5hZ2VtZW50I0F3YXJkaW5nT3Bwb3J0dW5pdHkiLCJpZGVudGlmaWVyIjoiaHR0cHM6Ly9jZXJ0aWZpY2F0ZS1kZW1vLmJjZGlwbG9tYS5jb20vY2hlY2svODdFRDJGMjI3MEU2QzQxNDU2RTk0Qjg2QjlEOTExNUI0RTM1QkNDQUQyMDBBNDlCODQ2NTkyQzE0Rjc5Qzg2QlYxRm5ibGx0YTBOWlRuSmtSM2xEV2xSbVREbFNSVUpFVkZaSVNtTm1ZekpoVVU1c1pVSjVaMkZKU0hwV2JtWloiLCJsb2NhdGlvbiI6IkZSQU5DRSIsInN0YXJ0ZWRBdFRpbWUiOiIyMDE5LTA5LTAyVDAwOjAwOjAwWiJ9LCJkYXRlT2ZCaXJ0aCI6IjE5OTMtMDQtMDgiLCJmYW1pbHlOYW1lIjoiRE9FIiwiZ2l2ZW5OYW1lcyI6IkphbmUiLCJncmFkaW5nU2NoZW1lIjp7ImlkIjoiaHR0cHM6Ly9sZWFzdG9uLmJjZGlwbG9tYS5jb20vbGF3LWVjb25vbWljcy1tYW5hZ2VtZW50I0dyYWRpbmdTY2hlbWUiLCJ0aXRsZSI6IjIgeWVhciBmdWxsLXRpbWUgcHJvZ3JhbW1lIC8gNCBzZW1lc3RlcnMifSwiaWQiOiJkaWQ6ZWJzaTp6b3hSR1ZaUW5kVGZRazU0Qjd0S2R3d05kaGFpNWdtOUY4TmF2OGVjZU5BQmEiLCJpZGVudGlmaWVyIjoiMDkwNDAwODA4NEgiLCJsZWFybmluZ0FjaGlldmVtZW50Ijp7ImFkZGl0aW9uYWxOb3RlIjpbIkRJU1RSSUJVVElPTiBNQU5BR0VNRU5UIl0sImRlc2NyaXB0aW9uIjoiVGhlIE1hc3RlciBpbiBJbmZvcm1hdGlvbiBhbmQgQ29tcHV0ZXIgU2NpZW5jZXMgKE1JQ1MpIGF0IHRoZSBVbml2ZXJzaXR5IG9mIEx1eGVtYm91cmcgZW5hYmxlcyBzdHVkZW50cyB0byBhY3F1aXJlIGRlZXBlciBrbm93bGVkZ2UgaW4gY29tcHV0ZXIgc2NpZW5jZSBieSB1bmRlcnN0YW5kaW5nIGl0cyBhYnN0cmFjdCBhbmQgaW50ZXJkaXNjaXBsaW5hcnkgZm91bmRhdGlvbnMsIGZvY3VzaW5nIG9uIHByb2JsZW0gc29sdmluZyBhbmQgZGV2ZWxvcGluZyBsaWZlbG9uZyBsZWFybmluZyBza2lsbHMuIiwiaWQiOiJodHRwczovL2xlYXN0b24uYmNkaXBsb21hLmNvbS9sYXctZWNvbm9taWNzLW1hbmFnZW1lbnQjTGVhcm5pbmdBY2hpZXZtZW50IiwidGl0bGUiOiJNYXN0ZXIgaW4gSW5mb3JtYXRpb24gYW5kIENvbXB1dGVyIFNjaWVuY2VzIn0sImxlYXJuaW5nU3BlY2lmaWNhdGlvbiI6eyJlY3RzQ3JlZGl0UG9pbnRzIjoxMjAsImVxZkxldmVsIjo3LCJpZCI6Imh0dHBzOi8vbGVhc3Rvbi5iY2RpcGxvbWEuY29tL2xhdy1lY29ub21pY3MtbWFuYWdlbWVudCNMZWFybmluZ1NwZWNpZmljYXRpb24iLCJpc2NlZGZDb2RlIjpbIjciXSwibnFmTGV2ZWwiOlsiNyJdfX0sImV2aWRlbmNlIjp7ImRvY3VtZW50UHJlc2VuY2UiOlsiUGh5c2ljYWwiXSwiZXZpZGVuY2VEb2N1bWVudCI6WyJQYXNzcG9ydCJdLCJpZCI6Imh0dHBzOi8vZXNzaWYuZXVyb3BhLmV1L3Rzci12YS9ldmlkZW5jZS9mMmFlZWM5Ny1mYzBkLTQyYmYtOGNhNy0wNTQ4MTkyZDU2NzgiLCJzdWJqZWN0UHJlc2VuY2UiOiJQaHlzaWNhbCIsInR5cGUiOlsiRG9jdW1lbnRWZXJpZmljYXRpb24iXSwidmVyaWZpZXIiOiJkaWQ6ZWJzaToyOTYyZmI3ODRkZjYxYmFhMjY3YzgxMzI0OTc1MzlmOGM2NzRiMzdjMTI0NGE3YSJ9LCJpZCI6InVybjp1dWlkOjZiMWQ4NDExLTllZDUtNDU2Ni05YzdmLTRjMjQxNjVmZjIzNiIsImlzc3VhbmNlRGF0ZSI6IjIwMjMtMDItMDhUMTU6MTA6NTZaIiwiaXNzdWVkIjoiMjAyMy0wMi0wOFQxNToxMDo1NloiLCJpc3N1ZXIiOiJkaWQ6ZWJzaTp6aFN3NXJQWGtjSGp2cXV3blZjVHp6QiIsInByb29mIjp7ImNyZWF0ZWQiOiIyMDIyLTA0LTI3VDEyOjI1OjA3WiIsImNyZWF0b3IiOiJkaWQ6ZWJzaTp6ZFJ2dktiWGhWVkJzWGhhdGp1aUJocyIsImRvbWFpbiI6Imh0dHBzOi8vYXBpLnByZXByb2QuZWJzaS5ldSIsImp3cyI6ImV5SmlOalFpT21aaGJITmxMQ0pqY21sMElqcGJJbUkyTkNKZExDSmhiR2NpT2lKRlV6STFOa3NpZlEuLm1JQm5NOFhEUXFTWUtRTlhfTHZhSmhtc2J5Q3I1T1o1Y1UyWmstUmVxTHByNGRvRnNnbW9vYmtPNTEyOHRaeS04S2ltVmpKa0d3MHdMMXVCV25NTFdRIiwibm9uY2UiOiIzZWE2OGRhZS1kMDdhLTRkYWEtOTMyYi1mYmI1OGY1YzIwYzQiLCJ0eXBlIjoiRWNkc2FTZWNwMjU2azFTaWduYXR1cmUyMDE5In0sInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiLCJWZXJpZmlhYmxlQXR0ZXN0YXRpb24iLCJWZXJpZmlhYmxlRGlwbG9tYSJdLCJ2YWxpZEZyb20iOiIyMDIzLTAyLTA4VDE1OjEwOjU2WiJ9fQ.avhpj0UC7SJiOo8PWJV3zhJoxngdmrBnyTkQsnqe76JlOmatLkgAY5Mp_sUJQnN1uENiT-_KBf0KY3izx4X_SA', - -// { -// '@context': ['https://www.w3.org/2018/credentials/v1'], -// 'credentialSchema': { -// 'id': -// 'https://api.preprod.ebsi.eu/trusted-schemas-registry/v1/schemas/0xbf78fc08a7a9f28f5479f58dea269d3657f54f13ca37d380cd4e92237fb691dd', -// 'type': 'JsonSchemaValidator2018', -// }, -// 'credentialStatus': { -// 'id': -// 'https://essif.europa.eu/status/education#higherEducation#392ac7f6-399a-437b-a268-4691ead8f176', -// 'type': 'CredentialStatusList2020', -// }, -// 'credentialSubject': { -// 'awardingOpportunity': { -// 'awardingBody': { -// 'eidasLegalIdentifier': 'Unknown', -// 'homepage': 'https://leaston.bcdiploma.com/', -// 'id': 'did:ebsi:zdRvvKbXhVVBsXhatjuiBhs', -// 'preferredName': 'Leaston University', -// 'registration': '0597065J', -// }, -// 'endedAtTime': '2020-06-26T00:00:00Z', -// 'id': -// 'https://leaston.bcdiploma.com/law-economics-management#AwardingOpportunity', -// 'identifier': -// 'https://certificate-demo.bcdiploma.com/check/87ED2F2270E6C41456E94B86B9D9115B4E35BCCAD200A49B846592C14F79C86BV1Fnbllta0NZTnJkR3lDWlRmTDlSRUJEVFZISmNmYzJhUU5sZUJ5Z2FJSHpWbmZZ', -// 'location': 'FRANCE', -// 'startedAtTime': '2019-09-02T00:00:00Z', -// }, -// 'dateOfBirth': '1993-04-08', -// 'familyName': 'DOE', -// 'givenNames': 'Jane', -// 'gradingScheme': { -// 'id': -// 'https://leaston.bcdiploma.com/law-economics-management#GradingScheme', -// 'title': '2 year full-time programme / 4 semesters', -// }, -// 'id': 'did:ebsi:zoxRGVZQndTfQk54B7tKdwwNdhai5gm9F8Nav8eceNABa', -// 'identifier': '0904008084H', -// 'learningAchievement': { -// 'additionalNote': ['DISTRIBUTION MANAGEMENT'], -// 'description': -// 'The Master in Information and Computer Sciences (MICS) at the University of Luxembourg enables students to acquire deeper knowledge in computer science by understanding its abstract and interdisciplinary foundations, focusing on problem solving and developing lifelong learning skills.', // ignore: lines_longer_than_80_chars -// 'id': -// 'https://leaston.bcdiploma.com/law-economics-management#LearningAchievment', -// 'title': 'Master in Information and Computer Sciences', -// }, -// 'learningSpecification': { -// 'ectsCreditPoints': 120, -// 'eqfLevel': 7, -// 'id': -// 'https://leaston.bcdiploma.com/law-economics-management#LearningSpecification', -// 'iscedfCode': ['7'], -// 'nqfLevel': ['7'], -// }, -// }, -// 'evidence': { -// 'documentPresence': ['Physical'], -// 'evidenceDocument': ['Passport'], -// 'id': -// 'https://essif.europa.eu/tsr-va/evidence/f2aeec97-fc0d-42bf-8ca7-0548192d5678', -// 'subjectPresence': 'Physical', -// 'type': ['DocumentVerification'], -// 'verifier': -// 'did:ebsi:2962fb784df61baa267c8132497539f8c674b37c1244a7a', -// }, -// 'id': 'urn:uuid:6b1d8411-9ed5-4566-9c7f-4c24165ff236', -// 'issuanceDate': '2023-02-08T15:10:56Z', -// 'issued': '2023-02-08T15:10:56Z', -// 'issuer': 'did:ebsi:zhSw5rPXkcHjvquwnVcTzzB', -// 'proof': { -// 'created': '2022-04-27T12:25:07Z', -// 'creator': 'did:ebsi:zdRvvKbXhVVBsXhatjuiBhs', -// 'domain': 'https://api.preprod.ebsi.eu', -// 'jws': -// 'eyJiNjQiOmZhbHNlLCJjcml0IjpbImI2NCJdLCJhbGciOiJFUzI1NksifQ..mIBnM8XDQqSYKQNX_LvaJhmsbyCr5OZ5cU2Zk-ReqLpr4doFsgmoobkO5128tZy-8KimVjJkGw0wL1uBWnMLWQ', // ignore: lines_longer_than_80_chars -// 'nonce': '3ea68dae-d07a-4daa-932b-fbb58f5c20c4', -// 'type': 'EcdsaSecp256k1Signature2019', -// }, -// 'type': [ -// 'VerifiableCredential', -// 'VerifiableAttestation', -// 'VerifiableDiploma', -// ], -// 'validFrom': '2023-02-08T15:10:56Z', -// } -// ]; - -// test('nonce', () { -// expect(verifierTokenParameters.nonce, nonce); -// }); - -// test('jwtsOfCredentials', () { -// expect(verifierTokenParameters.jsonIdOrJwtList, jwtsOfCredentials); -// }); - -// test('audience', () { -// expect(verifierTokenParameters.audience, audience); -// }); - -// group('override test', () { -// final verifierTokenParametersTest = VerifierTokenParametersTest(); - -// // test( -// // 'public key is P-256K private key without d parameter', -// // verifierTokenParametersTest.publicKeyTest, -// // ); - -// // test('did EBSI', verifierTokenParametersTest.didTest); - -// // test('kID EBSI', verifierTokenParametersTest.keyIdTest); - -// group('algorithm test', () { -// test( -// "algorithm is ES256K when key's curve is not P-256", -// verifierTokenParametersTest.algorithmIsES256KTest, -// ); - -// test( -// "algorithm is ES256 when key's curve is P-256", -// verifierTokenParametersTest.algorithmIsES256Test, -// ); - -// test( -// 'if alg is not null then return as it is', -// verifierTokenParametersTest.algorithmIsNotNullTest, -// ); -// }); - -// group('thumbprint test', () { -// // test( -// // 'thumbprint of the public Key', -// // verifierTokenParametersTest.thumprintOfKey, -// // ); - -// // test( -// // 'thumbrprint of the Key from exemple in rfc 7638', -// // verifierTokenParametersTest.thumprintOfKeyForrfc7638, -// // ); -// }); -// }); -// }); -// } +// Copyright (c) 2022, Very Good Ventures +// https://verygood.ventures +// +// Use of this source code is governed by an MIT-style +// license that can be found in the LICENSE file or at +// https://opensource.org/licenses/MIT. + +import 'package:flutter_test/flutter_test.dart'; +import 'package:oidc4vc/oidc4vc.dart'; + +import 'verifier_token_parameters_class.dart'; + +void main() { + group('Verifier TokenParameters', () { + const privateKey = { + 'crv': 'P-256K', + 'd': 'ccWWNSjGiv1iWlNh4kfhWvwG3yyQMe8o31Du0uKRzrs', + 'kty': 'EC', + 'x': 'J4vQtLUyrVUiFIXRrtEq4xurmBZp2eq9wJmXkIA_stI', + 'y': 'EUU6vXoG3BGX2zzwjXrGDcr4EyDD0Vfk3_5fg5kSgKE', + }; + + const clientId = + '''did:key:z2dmzD81cgPx8Vki7JbuuMmFYrWPgYoytykUZ3eyqht1j9Kbrbpg5is8LfTLuQ1RsW5r7s7ZjbDDFbDgy1tLrdc7Bj3itBGQkuGUQyfzKhFqbUNW2PqJPMSSzWoF2DGSvDSijCtJtYCSRsjSVLrwu5oHNbnPFvSEC4iRZPpU6B6nExRBTa'''; + + const credentialsToBePresented = [ + r'{"id":"urn:uuid:6b1d8411-9ed5-4566-9c7f-4c24165ff236","receivedId":"urn:uuid:6b1d8411-9ed5-4566-9c7f-4c24165ff236","image":null,"data":{"@context":["https://www.w3.org/2018/credentials/v1"],"credentialSchema":{"id":"https://api.preprod.ebsi.eu/trusted-schemas-registry/v1/schemas/0xbf78fc08a7a9f28f5479f58dea269d3657f54f13ca37d380cd4e92237fb691dd","type":"JsonSchemaValidator2018"},"credentialStatus":{"id":"https://essif.europa.eu/status/education#higherEducation#392ac7f6-399a-437b-a268-4691ead8f176","type":"CredentialStatusList2020"},"credentialSubject":{"awardingOpportunity":{"awardingBody":{"eidasLegalIdentifier":"Unknown","homepage":"https://leaston.bcdiploma.com/","id":"did:ebsi:zdRvvKbXhVVBsXhatjuiBhs","preferredName":"Leaston University","registration":"0597065J"},"endedAtTime":"2020-06-26T00:00:00Z","id":"https://leaston.bcdiploma.com/law-economics-management#AwardingOpportunity","identifier":"https://certificate-demo.bcdiploma.com/check/87ED2F2270E6C41456E94B86B9D9115B4E35BCCAD200A49B846592C14F79C86BV1Fnbllta0NZTnJkR3lDWlRmTDlSRUJEVFZISmNmYzJhUU5sZUJ5Z2FJSHpWbmZZ","location":"FRANCE","startedAtTime":"2019-09-02T00:00:00Z"},"dateOfBirth":"1993-04-08","familyName":"DOE","givenNames":"Jane","gradingScheme":{"id":"https://leaston.bcdiploma.com/law-economics-management#GradingScheme","title":"2 year full-time programme / 4 semesters"},"id":"did:ebsi:zoxRGVZQndTfQk54B7tKdwwNdhai5gm9F8Nav8eceNABa","identifier":"0904008084H","learningAchievement":{"additionalNote":["DISTRIBUTION MANAGEMENT"],"description":"The Master in Information and Computer Sciences (MICS) at the University of Luxembourg enables students to acquire deeper knowledge in computer science by understanding its abstract and interdisciplinary foundations, focusing on problem solving and developing lifelong learning skills.","id":"https://leaston.bcdiploma.com/law-economics-management#LearningAchievment","title":"Master in Information and Computer Sciences"},"learningSpecification":{"ectsCreditPoints":120,"eqfLevel":7,"id":"https://leaston.bcdiploma.com/law-economics-management#LearningSpecification","iscedfCode":["7"],"nqfLevel":["7"]}},"evidence":{"documentPresence":["Physical"],"evidenceDocument":["Passport"],"id":"https://essif.europa.eu/tsr-va/evidence/f2aeec97-fc0d-42bf-8ca7-0548192d5678","subjectPresence":"Physical","type":["DocumentVerification"],"verifier":"did:ebsi:2962fb784df61baa267c8132497539f8c674b37c1244a7a"},"id":"urn:uuid:6b1d8411-9ed5-4566-9c7f-4c24165ff236","issuanceDate":"2023-02-08T15:10:56Z","issued":"2023-02-08T15:10:56Z","issuer":"did:ebsi:zhSw5rPXkcHjvquwnVcTzzB","proof":{"created":"2022-04-27T12:25:07Z","creator":"did:ebsi:zdRvvKbXhVVBsXhatjuiBhs","domain":"https://api.preprod.ebsi.eu","jws":"eyJiNjQiOmZhbHNlLCJjcml0IjpbImI2NCJdLCJhbGciOiJFUzI1NksifQ..mIBnM8XDQqSYKQNX_LvaJhmsbyCr5OZ5cU2Zk-ReqLpr4doFsgmoobkO5128tZy-8KimVjJkGw0wL1uBWnMLWQ","nonce":"3ea68dae-d07a-4daa-932b-fbb58f5c20c4","type":"EcdsaSecp256k1Signature2019"},"type":["VerifiableCredential","VerifiableAttestation","VerifiableDiploma"],"validFrom":"2023-02-08T15:10:56Z"},"shareLink":"","credentialPreview":{"id":"urn:uuid:6b1d8411-9ed5-4566-9c7f-4c24165ff236","type":["VerifiableCredential","VerifiableAttestation","VerifiableDiploma"],"issuer":"did:ebsi:zhSw5rPXkcHjvquwnVcTzzB","description":[],"name":[],"issuanceDate":"2023-02-08T15:10:56Z","proof":[{"type":"EcdsaSecp256k1Signature2019","proofPurpose":null,"verificationMethod":null,"created":"2022-04-27T12:25:07Z","jws":"eyJiNjQiOmZhbHNlLCJjcml0IjpbImI2NCJdLCJhbGciOiJFUzI1NksifQ..mIBnM8XDQqSYKQNX_LvaJhmsbyCr5OZ5cU2Zk-ReqLpr4doFsgmoobkO5128tZy-8KimVjJkGw0wL1uBWnMLWQ"}],"credentialSubject":{"id":"did:ebsi:zoxRGVZQndTfQk54B7tKdwwNdhai5gm9F8Nav8eceNABa","type":null,"issuedBy":{"name":""}},"evidence":[{"id":"https://essif.europa.eu/tsr-va/evidence/f2aeec97-fc0d-42bf-8ca7-0548192d5678","type":["DocumentVerification"]}],"credentialStatus":{"id":"https://essif.europa.eu/status/education#higherEducation#392ac7f6-399a-437b-a268-4691ead8f176","type":"CredentialStatusList2020","revocationListIndex":"","revocationListCredential":""}},"display":{"backgroundColor":"","icon":"","nameFallback":"","descriptionFallback":""},"expirationDate":null,"credential_manifest":{"id":"VerifiableDiploma_1","issuer":{"id":"did:ebsi:zhSw5rPXkcHjvquwnVcTzzB","name":"Test EBSILUX"},"output_descriptors":[{"id":"diploma_01","schema":"https://api.preprod.ebsi.eu/trusted-schemas-registry/v1/schemas/0xbf78fc08a7a9f28f5479f58dea269d3657f54f13ca37d380cd4e92237fb691dd","name":null,"description":null,"styles":null,"display":{"title":{"path":[],"schema":{"type":"string","format":null},"fallback":"Diploma"},"subtitle":{"path":[],"schema":{"type":"string","format":null},"fallback":"EBSI Verifiable diploma"},"description":{"path":[],"schema":{"type":"string","format":null},"fallback":"This card is a proof that you passed this diploma successfully. You can use this card when you need to prove this information to services that have adopted EU EBSI framework."},"properties":[{"label":"First name","path":["$.credentialSubject.firstName"],"schema":{"type":"string","format":null},"fallback":"Unknown"},{"label":"Last name","path":["$.credentialSubject.familyName"],"schema":{"type":"string","format":null},"fallback":"Unknown"},{"label":"Birth date","path":["$.credentialSubject.dateOfBirth"],"schema":{"type":"string","format":"date"},"fallback":"Unknown"},{"label":"Grading scheme","path":["$.credentialSubject.gradingScheme.title"],"schema":{"type":"string","format":null},"fallback":"Unknown"},{"label":"Title","path":["$.credentialSubject.learningAchievement.title"],"schema":{"type":"string","format":null},"fallback":"Unknown"},{"label":"Description","path":["$.credentialSubject.learningAchievement.description"],"schema":{"type":"string","format":null},"fallback":"Unknown"},{"label":"ECTS Points","path":["$.credentialSubject.learningSpecification.ectsCreditPoints"],"schema":{"type":"number","format":null},"fallback":"Unknown"},{"label":"Issue date","path":["$.issuanceDate"],"schema":{"type":"string","format":"date"},"fallback":"Unknown"},{"label":"Issued by","path":["$.credentialSubject.awardingOpportunity.awardingBody.preferredName"],"schema":{"type":"string","format":null},"fallback":"Unknown"},{"label":"Registration","path":["$.credentialSubject.awardingOpportunity.awardingBody.registration"],"schema":{"type":"string","format":null},"fallback":"Unknown"},{"label":"Website","path":["$.credentialSubject.awardingOpportunity.awardingBody.homepage"],"schema":{"type":"string","format":"uri"},"fallback":"Unknown"}]}}],"presentation_definition":null},"challenge":null,"domain":null,"activities":[{"acquisitionAt":"2023-02-08T20:41:02.024360","presentation":null}],"jwt":"eyJhbGciOiJFUzI1NksiLCJraWQiOiJkaWQ6ZWJzaTp6aFN3NXJQWGtjSGp2cXV3blZjVHp6QiM1MTVhOWM0MzZjMGYyYWQzYWI2NWQ2Y2VmYzVjMWYwNmMwNWI4YWRmY2Y1NGVlMDZkYzgwNTQzMjA0NzBmZmFmIiwidHlwIjoiSldUIn0.eyJleHAiOjE2NzU4NzAwNTYuMTcxMDgyLCJpYXQiOjE2NzU4NjkwNTYuMTcxMDc1LCJpc3MiOiJkaWQ6ZWJzaTp6aFN3NXJQWGtjSGp2cXV3blZjVHp6QiIsImp0aSI6InVybjp1dWlkOjZiMWQ4NDExLTllZDUtNDU2Ni05YzdmLTRjMjQxNjVmZjIzNiIsIm5iZiI6MTY3NTg2OTA1Ni4xNzEwOCwibm9uY2UiOiJjOGM3Nzg0Yi1hN2MyLTExZWQtOGUwMS0wYTE2Mjg5NTg1NjAiLCJzdWIiOiJkaWQ6ZWJzaTp6b3hSR1ZaUW5kVGZRazU0Qjd0S2R3d05kaGFpNWdtOUY4TmF2OGVjZU5BQmEiLCJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSJdLCJjcmVkZW50aWFsU2NoZW1hIjp7ImlkIjoiaHR0cHM6Ly9hcGkucHJlcHJvZC5lYnNpLmV1L3RydXN0ZWQtc2NoZW1hcy1yZWdpc3RyeS92MS9zY2hlbWFzLzB4YmY3OGZjMDhhN2E5ZjI4ZjU0NzlmNThkZWEyNjlkMzY1N2Y1NGYxM2NhMzdkMzgwY2Q0ZTkyMjM3ZmI2OTFkZCIsInR5cGUiOiJKc29uU2NoZW1hVmFsaWRhdG9yMjAxOCJ9LCJjcmVkZW50aWFsU3RhdHVzIjp7ImlkIjoiaHR0cHM6Ly9lc3NpZi5ldXJvcGEuZXUvc3RhdHVzL2VkdWNhdGlvbiNoaWdoZXJFZHVjYXRpb24jMzkyYWM3ZjYtMzk5YS00MzdiLWEyNjgtNDY5MWVhZDhmMTc2IiwidHlwZSI6IkNyZWRlbnRpYWxTdGF0dXNMaXN0MjAyMCJ9LCJjcmVkZW50aWFsU3ViamVjdCI6eyJhd2FyZGluZ09wcG9ydHVuaXR5Ijp7ImF3YXJkaW5nQm9keSI6eyJlaWRhc0xlZ2FsSWRlbnRpZmllciI6IlVua25vd24iLCJob21lcGFnZSI6Imh0dHBzOi8vbGVhc3Rvbi5iY2RpcGxvbWEuY29tLyIsImlkIjoiZGlkOmVic2k6emRSdnZLYlhoVlZCc1hoYXRqdWlCaHMiLCJwcmVmZXJyZWROYW1lIjoiTGVhc3RvbiBVbml2ZXJzaXR5IiwicmVnaXN0cmF0aW9uIjoiMDU5NzA2NUoifSwiZW5kZWRBdFRpbWUiOiIyMDIwLTA2LTI2VDAwOjAwOjAwWiIsImlkIjoiaHR0cHM6Ly9sZWFzdG9uLmJjZGlwbG9tYS5jb20vbGF3LWVjb25vbWljcy1tYW5hZ2VtZW50I0F3YXJkaW5nT3Bwb3J0dW5pdHkiLCJpZGVudGlmaWVyIjoiaHR0cHM6Ly9jZXJ0aWZpY2F0ZS1kZW1vLmJjZGlwbG9tYS5jb20vY2hlY2svODdFRDJGMjI3MEU2QzQxNDU2RTk0Qjg2QjlEOTExNUI0RTM1QkNDQUQyMDBBNDlCODQ2NTkyQzE0Rjc5Qzg2QlYxRm5ibGx0YTBOWlRuSmtSM2xEV2xSbVREbFNSVUpFVkZaSVNtTm1ZekpoVVU1c1pVSjVaMkZKU0hwV2JtWloiLCJsb2NhdGlvbiI6IkZSQU5DRSIsInN0YXJ0ZWRBdFRpbWUiOiIyMDE5LTA5LTAyVDAwOjAwOjAwWiJ9LCJkYXRlT2ZCaXJ0aCI6IjE5OTMtMDQtMDgiLCJmYW1pbHlOYW1lIjoiRE9FIiwiZ2l2ZW5OYW1lcyI6IkphbmUiLCJncmFkaW5nU2NoZW1lIjp7ImlkIjoiaHR0cHM6Ly9sZWFzdG9uLmJjZGlwbG9tYS5jb20vbGF3LWVjb25vbWljcy1tYW5hZ2VtZW50I0dyYWRpbmdTY2hlbWUiLCJ0aXRsZSI6IjIgeWVhciBmdWxsLXRpbWUgcHJvZ3JhbW1lIC8gNCBzZW1lc3RlcnMifSwiaWQiOiJkaWQ6ZWJzaTp6b3hSR1ZaUW5kVGZRazU0Qjd0S2R3d05kaGFpNWdtOUY4TmF2OGVjZU5BQmEiLCJpZGVudGlmaWVyIjoiMDkwNDAwODA4NEgiLCJsZWFybmluZ0FjaGlldmVtZW50Ijp7ImFkZGl0aW9uYWxOb3RlIjpbIkRJU1RSSUJVVElPTiBNQU5BR0VNRU5UIl0sImRlc2NyaXB0aW9uIjoiVGhlIE1hc3RlciBpbiBJbmZvcm1hdGlvbiBhbmQgQ29tcHV0ZXIgU2NpZW5jZXMgKE1JQ1MpIGF0IHRoZSBVbml2ZXJzaXR5IG9mIEx1eGVtYm91cmcgZW5hYmxlcyBzdHVkZW50cyB0byBhY3F1aXJlIGRlZXBlciBrbm93bGVkZ2UgaW4gY29tcHV0ZXIgc2NpZW5jZSBieSB1bmRlcnN0YW5kaW5nIGl0cyBhYnN0cmFjdCBhbmQgaW50ZXJkaXNjaXBsaW5hcnkgZm91bmRhdGlvbnMsIGZvY3VzaW5nIG9uIHByb2JsZW0gc29sdmluZyBhbmQgZGV2ZWxvcGluZyBsaWZlbG9uZyBsZWFybmluZyBza2lsbHMuIiwiaWQiOiJodHRwczovL2xlYXN0b24uYmNkaXBsb21hLmNvbS9sYXctZWNvbm9taWNzLW1hbmFnZW1lbnQjTGVhcm5pbmdBY2hpZXZtZW50IiwidGl0bGUiOiJNYXN0ZXIgaW4gSW5mb3JtYXRpb24gYW5kIENvbXB1dGVyIFNjaWVuY2VzIn0sImxlYXJuaW5nU3BlY2lmaWNhdGlvbiI6eyJlY3RzQ3JlZGl0UG9pbnRzIjoxMjAsImVxZkxldmVsIjo3LCJpZCI6Imh0dHBzOi8vbGVhc3Rvbi5iY2RpcGxvbWEuY29tL2xhdy1lY29ub21pY3MtbWFuYWdlbWVudCNMZWFybmluZ1NwZWNpZmljYXRpb24iLCJpc2NlZGZDb2RlIjpbIjciXSwibnFmTGV2ZWwiOlsiNyJdfX0sImV2aWRlbmNlIjp7ImRvY3VtZW50UHJlc2VuY2UiOlsiUGh5c2ljYWwiXSwiZXZpZGVuY2VEb2N1bWVudCI6WyJQYXNzcG9ydCJdLCJpZCI6Imh0dHBzOi8vZXNzaWYuZXVyb3BhLmV1L3Rzci12YS9ldmlkZW5jZS9mMmFlZWM5Ny1mYzBkLTQyYmYtOGNhNy0wNTQ4MTkyZDU2NzgiLCJzdWJqZWN0UHJlc2VuY2UiOiJQaHlzaWNhbCIsInR5cGUiOlsiRG9jdW1lbnRWZXJpZmljYXRpb24iXSwidmVyaWZpZXIiOiJkaWQ6ZWJzaToyOTYyZmI3ODRkZjYxYmFhMjY3YzgxMzI0OTc1MzlmOGM2NzRiMzdjMTI0NGE3YSJ9LCJpZCI6InVybjp1dWlkOjZiMWQ4NDExLTllZDUtNDU2Ni05YzdmLTRjMjQxNjVmZjIzNiIsImlzc3VhbmNlRGF0ZSI6IjIwMjMtMDItMDhUMTU6MTA6NTZaIiwiaXNzdWVkIjoiMjAyMy0wMi0wOFQxNToxMDo1NloiLCJpc3N1ZXIiOiJkaWQ6ZWJzaTp6aFN3NXJQWGtjSGp2cXV3blZjVHp6QiIsInByb29mIjp7ImNyZWF0ZWQiOiIyMDIyLTA0LTI3VDEyOjI1OjA3WiIsImNyZWF0b3IiOiJkaWQ6ZWJzaTp6ZFJ2dktiWGhWVkJzWGhhdGp1aUJocyIsImRvbWFpbiI6Imh0dHBzOi8vYXBpLnByZXByb2QuZWJzaS5ldSIsImp3cyI6ImV5SmlOalFpT21aaGJITmxMQ0pqY21sMElqcGJJbUkyTkNKZExDSmhiR2NpT2lKRlV6STFOa3NpZlEuLm1JQm5NOFhEUXFTWUtRTlhfTHZhSmhtc2J5Q3I1T1o1Y1UyWmstUmVxTHByNGRvRnNnbW9vYmtPNTEyOHRaeS04S2ltVmpKa0d3MHdMMXVCV25NTFdRIiwibm9uY2UiOiIzZWE2OGRhZS1kMDdhLTRkYWEtOTMyYi1mYmI1OGY1YzIwYzQiLCJ0eXBlIjoiRWNkc2FTZWNwMjU2azFTaWduYXR1cmUyMDE5In0sInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiLCJWZXJpZmlhYmxlQXR0ZXN0YXRpb24iLCJWZXJpZmlhYmxlRGlwbG9tYSJdLCJ2YWxpZEZyb20iOiIyMDIzLTAyLTA4VDE1OjEwOjU2WiJ9fQ.avhpj0UC7SJiOo8PWJV3zhJoxngdmrBnyTkQsnqe76JlOmatLkgAY5Mp_sUJQnN1uENiT-_KBf0KY3izx4X_SA"}', + r'{"id":"urn:uuid:6b1d8411-9ed5-4566-9c7f-4c24165ff236","receivedId":"urn:uuid:6b1d8411-9ed5-4566-9c7f-4c24165ff236","image":null,"data":{"@context":["https://www.w3.org/2018/credentials/v1"],"credentialSchema":{"id":"https://api.preprod.ebsi.eu/trusted-schemas-registry/v1/schemas/0xbf78fc08a7a9f28f5479f58dea269d3657f54f13ca37d380cd4e92237fb691dd","type":"JsonSchemaValidator2018"},"credentialStatus":{"id":"https://essif.europa.eu/status/education#higherEducation#392ac7f6-399a-437b-a268-4691ead8f176","type":"CredentialStatusList2020"},"credentialSubject":{"awardingOpportunity":{"awardingBody":{"eidasLegalIdentifier":"Unknown","homepage":"https://leaston.bcdiploma.com/","id":"did:ebsi:zdRvvKbXhVVBsXhatjuiBhs","preferredName":"Leaston University","registration":"0597065J"},"endedAtTime":"2020-06-26T00:00:00Z","id":"https://leaston.bcdiploma.com/law-economics-management#AwardingOpportunity","identifier":"https://certificate-demo.bcdiploma.com/check/87ED2F2270E6C41456E94B86B9D9115B4E35BCCAD200A49B846592C14F79C86BV1Fnbllta0NZTnJkR3lDWlRmTDlSRUJEVFZISmNmYzJhUU5sZUJ5Z2FJSHpWbmZZ","location":"FRANCE","startedAtTime":"2019-09-02T00:00:00Z"},"dateOfBirth":"1993-04-08","familyName":"DOE","givenNames":"Jane","gradingScheme":{"id":"https://leaston.bcdiploma.com/law-economics-management#GradingScheme","title":"2 year full-time programme / 4 semesters"},"id":"did:ebsi:zoxRGVZQndTfQk54B7tKdwwNdhai5gm9F8Nav8eceNABa","identifier":"0904008084H","learningAchievement":{"additionalNote":["DISTRIBUTION MANAGEMENT"],"description":"The Master in Information and Computer Sciences (MICS) at the University of Luxembourg enables students to acquire deeper knowledge in computer science by understanding its abstract and interdisciplinary foundations, focusing on problem solving and developing lifelong learning skills.","id":"https://leaston.bcdiploma.com/law-economics-management#LearningAchievment","title":"Master in Information and Computer Sciences"},"learningSpecification":{"ectsCreditPoints":120,"eqfLevel":7,"id":"https://leaston.bcdiploma.com/law-economics-management#LearningSpecification","iscedfCode":["7"],"nqfLevel":["7"]}},"evidence":{"documentPresence":["Physical"],"evidenceDocument":["Passport"],"id":"https://essif.europa.eu/tsr-va/evidence/f2aeec97-fc0d-42bf-8ca7-0548192d5678","subjectPresence":"Physical","type":["DocumentVerification"],"verifier":"did:ebsi:2962fb784df61baa267c8132497539f8c674b37c1244a7a"},"id":"urn:uuid:6b1d8411-9ed5-4566-9c7f-4c24165ff236","issuanceDate":"2023-02-08T15:10:56Z","issued":"2023-02-08T15:10:56Z","issuer":"did:ebsi:zhSw5rPXkcHjvquwnVcTzzB","proof":{"created":"2022-04-27T12:25:07Z","creator":"did:ebsi:zdRvvKbXhVVBsXhatjuiBhs","domain":"https://api.preprod.ebsi.eu","jws":"eyJiNjQiOmZhbHNlLCJjcml0IjpbImI2NCJdLCJhbGciOiJFUzI1NksifQ..mIBnM8XDQqSYKQNX_LvaJhmsbyCr5OZ5cU2Zk-ReqLpr4doFsgmoobkO5128tZy-8KimVjJkGw0wL1uBWnMLWQ","nonce":"3ea68dae-d07a-4daa-932b-fbb58f5c20c4","type":"EcdsaSecp256k1Signature2019"},"type":["VerifiableCredential","VerifiableAttestation","VerifiableDiploma"],"validFrom":"2023-02-08T15:10:56Z"},"shareLink":"","credentialPreview":{"id":"urn:uuid:6b1d8411-9ed5-4566-9c7f-4c24165ff236","type":["VerifiableCredential","VerifiableAttestation","VerifiableDiploma"],"issuer":"did:ebsi:zhSw5rPXkcHjvquwnVcTzzB","description":[],"name":[],"issuanceDate":"2023-02-08T15:10:56Z","proof":[{"type":"EcdsaSecp256k1Signature2019","proofPurpose":null,"verificationMethod":null,"created":"2022-04-27T12:25:07Z","jws":"eyJiNjQiOmZhbHNlLCJjcml0IjpbImI2NCJdLCJhbGciOiJFUzI1NksifQ..mIBnM8XDQqSYKQNX_LvaJhmsbyCr5OZ5cU2Zk-ReqLpr4doFsgmoobkO5128tZy-8KimVjJkGw0wL1uBWnMLWQ"}],"credentialSubject":{"id":"did:ebsi:zoxRGVZQndTfQk54B7tKdwwNdhai5gm9F8Nav8eceNABa","type":null,"issuedBy":{"name":""}},"evidence":[{"id":"https://essif.europa.eu/tsr-va/evidence/f2aeec97-fc0d-42bf-8ca7-0548192d5678","type":["DocumentVerification"]}],"credentialStatus":{"id":"https://essif.europa.eu/status/education#higherEducation#392ac7f6-399a-437b-a268-4691ead8f176","type":"CredentialStatusList2020","revocationListIndex":"","revocationListCredential":""}},"display":{"backgroundColor":"","icon":"","nameFallback":"","descriptionFallback":""},"expirationDate":null,"credential_manifest":{"id":"VerifiableDiploma_1","issuer":{"id":"did:ebsi:zhSw5rPXkcHjvquwnVcTzzB","name":"Test EBSILUX"},"output_descriptors":[{"id":"diploma_01","schema":"https://api.preprod.ebsi.eu/trusted-schemas-registry/v1/schemas/0xbf78fc08a7a9f28f5479f58dea269d3657f54f13ca37d380cd4e92237fb691dd","name":null,"description":null,"styles":null,"display":{"title":{"path":[],"schema":{"type":"string","format":null},"fallback":"Diploma"},"subtitle":{"path":[],"schema":{"type":"string","format":null},"fallback":"EBSI Verifiable diploma"},"description":{"path":[],"schema":{"type":"string","format":null},"fallback":"This card is a proof that you passed this diploma successfully. You can use this card when you need to prove this information to services that have adopted EU EBSI framework."},"properties":[{"label":"First name","path":["$.credentialSubject.firstName"],"schema":{"type":"string","format":null},"fallback":"Unknown"},{"label":"Last name","path":["$.credentialSubject.familyName"],"schema":{"type":"string","format":null},"fallback":"Unknown"},{"label":"Birth date","path":["$.credentialSubject.dateOfBirth"],"schema":{"type":"string","format":"date"},"fallback":"Unknown"},{"label":"Grading scheme","path":["$.credentialSubject.gradingScheme.title"],"schema":{"type":"string","format":null},"fallback":"Unknown"},{"label":"Title","path":["$.credentialSubject.learningAchievement.title"],"schema":{"type":"string","format":null},"fallback":"Unknown"},{"label":"Description","path":["$.credentialSubject.learningAchievement.description"],"schema":{"type":"string","format":null},"fallback":"Unknown"},{"label":"ECTS Points","path":["$.credentialSubject.learningSpecification.ectsCreditPoints"],"schema":{"type":"number","format":null},"fallback":"Unknown"},{"label":"Issue date","path":["$.issuanceDate"],"schema":{"type":"string","format":"date"},"fallback":"Unknown"},{"label":"Issued by","path":["$.credentialSubject.awardingOpportunity.awardingBody.preferredName"],"schema":{"type":"string","format":null},"fallback":"Unknown"},{"label":"Registration","path":["$.credentialSubject.awardingOpportunity.awardingBody.registration"],"schema":{"type":"string","format":null},"fallback":"Unknown"},{"label":"Website","path":["$.credentialSubject.awardingOpportunity.awardingBody.homepage"],"schema":{"type":"string","format":"uri"},"fallback":"Unknown"}]}}],"presentation_definition":null},"challenge":null,"domain":null,"activities":[{"acquisitionAt":"2023-02-08T20:41:02.024360","presentation":null}]}', + ]; + + const nonce = '69165b47-a851-11ed-bd52-0a1628958560'; + + const audience = 'xjcqarovuv'; + + final verifierTokenParameters = VerifierTokenParameters( + privateKey: privateKey, + clientId: clientId, + did: clientId, + clientType: ClientType.did, + mediaType: MediaType.proofOfOwnership, + proofHeaderType: ProofHeaderType.kid, + audience: audience, + credentials: credentialsToBePresented, + kid: '', + nonce: nonce, + ); + + const jwtsOfCredentials = [ + // ignore: lines_longer_than_80_chars + 'eyJhbGciOiJFUzI1NksiLCJraWQiOiJkaWQ6ZWJzaTp6aFN3NXJQWGtjSGp2cXV3blZjVHp6QiM1MTVhOWM0MzZjMGYyYWQzYWI2NWQ2Y2VmYzVjMWYwNmMwNWI4YWRmY2Y1NGVlMDZkYzgwNTQzMjA0NzBmZmFmIiwidHlwIjoiSldUIn0.eyJleHAiOjE2NzU4NzAwNTYuMTcxMDgyLCJpYXQiOjE2NzU4NjkwNTYuMTcxMDc1LCJpc3MiOiJkaWQ6ZWJzaTp6aFN3NXJQWGtjSGp2cXV3blZjVHp6QiIsImp0aSI6InVybjp1dWlkOjZiMWQ4NDExLTllZDUtNDU2Ni05YzdmLTRjMjQxNjVmZjIzNiIsIm5iZiI6MTY3NTg2OTA1Ni4xNzEwOCwibm9uY2UiOiJjOGM3Nzg0Yi1hN2MyLTExZWQtOGUwMS0wYTE2Mjg5NTg1NjAiLCJzdWIiOiJkaWQ6ZWJzaTp6b3hSR1ZaUW5kVGZRazU0Qjd0S2R3d05kaGFpNWdtOUY4TmF2OGVjZU5BQmEiLCJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSJdLCJjcmVkZW50aWFsU2NoZW1hIjp7ImlkIjoiaHR0cHM6Ly9hcGkucHJlcHJvZC5lYnNpLmV1L3RydXN0ZWQtc2NoZW1hcy1yZWdpc3RyeS92MS9zY2hlbWFzLzB4YmY3OGZjMDhhN2E5ZjI4ZjU0NzlmNThkZWEyNjlkMzY1N2Y1NGYxM2NhMzdkMzgwY2Q0ZTkyMjM3ZmI2OTFkZCIsInR5cGUiOiJKc29uU2NoZW1hVmFsaWRhdG9yMjAxOCJ9LCJjcmVkZW50aWFsU3RhdHVzIjp7ImlkIjoiaHR0cHM6Ly9lc3NpZi5ldXJvcGEuZXUvc3RhdHVzL2VkdWNhdGlvbiNoaWdoZXJFZHVjYXRpb24jMzkyYWM3ZjYtMzk5YS00MzdiLWEyNjgtNDY5MWVhZDhmMTc2IiwidHlwZSI6IkNyZWRlbnRpYWxTdGF0dXNMaXN0MjAyMCJ9LCJjcmVkZW50aWFsU3ViamVjdCI6eyJhd2FyZGluZ09wcG9ydHVuaXR5Ijp7ImF3YXJkaW5nQm9keSI6eyJlaWRhc0xlZ2FsSWRlbnRpZmllciI6IlVua25vd24iLCJob21lcGFnZSI6Imh0dHBzOi8vbGVhc3Rvbi5iY2RpcGxvbWEuY29tLyIsImlkIjoiZGlkOmVic2k6emRSdnZLYlhoVlZCc1hoYXRqdWlCaHMiLCJwcmVmZXJyZWROYW1lIjoiTGVhc3RvbiBVbml2ZXJzaXR5IiwicmVnaXN0cmF0aW9uIjoiMDU5NzA2NUoifSwiZW5kZWRBdFRpbWUiOiIyMDIwLTA2LTI2VDAwOjAwOjAwWiIsImlkIjoiaHR0cHM6Ly9sZWFzdG9uLmJjZGlwbG9tYS5jb20vbGF3LWVjb25vbWljcy1tYW5hZ2VtZW50I0F3YXJkaW5nT3Bwb3J0dW5pdHkiLCJpZGVudGlmaWVyIjoiaHR0cHM6Ly9jZXJ0aWZpY2F0ZS1kZW1vLmJjZGlwbG9tYS5jb20vY2hlY2svODdFRDJGMjI3MEU2QzQxNDU2RTk0Qjg2QjlEOTExNUI0RTM1QkNDQUQyMDBBNDlCODQ2NTkyQzE0Rjc5Qzg2QlYxRm5ibGx0YTBOWlRuSmtSM2xEV2xSbVREbFNSVUpFVkZaSVNtTm1ZekpoVVU1c1pVSjVaMkZKU0hwV2JtWloiLCJsb2NhdGlvbiI6IkZSQU5DRSIsInN0YXJ0ZWRBdFRpbWUiOiIyMDE5LTA5LTAyVDAwOjAwOjAwWiJ9LCJkYXRlT2ZCaXJ0aCI6IjE5OTMtMDQtMDgiLCJmYW1pbHlOYW1lIjoiRE9FIiwiZ2l2ZW5OYW1lcyI6IkphbmUiLCJncmFkaW5nU2NoZW1lIjp7ImlkIjoiaHR0cHM6Ly9sZWFzdG9uLmJjZGlwbG9tYS5jb20vbGF3LWVjb25vbWljcy1tYW5hZ2VtZW50I0dyYWRpbmdTY2hlbWUiLCJ0aXRsZSI6IjIgeWVhciBmdWxsLXRpbWUgcHJvZ3JhbW1lIC8gNCBzZW1lc3RlcnMifSwiaWQiOiJkaWQ6ZWJzaTp6b3hSR1ZaUW5kVGZRazU0Qjd0S2R3d05kaGFpNWdtOUY4TmF2OGVjZU5BQmEiLCJpZGVudGlmaWVyIjoiMDkwNDAwODA4NEgiLCJsZWFybmluZ0FjaGlldmVtZW50Ijp7ImFkZGl0aW9uYWxOb3RlIjpbIkRJU1RSSUJVVElPTiBNQU5BR0VNRU5UIl0sImRlc2NyaXB0aW9uIjoiVGhlIE1hc3RlciBpbiBJbmZvcm1hdGlvbiBhbmQgQ29tcHV0ZXIgU2NpZW5jZXMgKE1JQ1MpIGF0IHRoZSBVbml2ZXJzaXR5IG9mIEx1eGVtYm91cmcgZW5hYmxlcyBzdHVkZW50cyB0byBhY3F1aXJlIGRlZXBlciBrbm93bGVkZ2UgaW4gY29tcHV0ZXIgc2NpZW5jZSBieSB1bmRlcnN0YW5kaW5nIGl0cyBhYnN0cmFjdCBhbmQgaW50ZXJkaXNjaXBsaW5hcnkgZm91bmRhdGlvbnMsIGZvY3VzaW5nIG9uIHByb2JsZW0gc29sdmluZyBhbmQgZGV2ZWxvcGluZyBsaWZlbG9uZyBsZWFybmluZyBza2lsbHMuIiwiaWQiOiJodHRwczovL2xlYXN0b24uYmNkaXBsb21hLmNvbS9sYXctZWNvbm9taWNzLW1hbmFnZW1lbnQjTGVhcm5pbmdBY2hpZXZtZW50IiwidGl0bGUiOiJNYXN0ZXIgaW4gSW5mb3JtYXRpb24gYW5kIENvbXB1dGVyIFNjaWVuY2VzIn0sImxlYXJuaW5nU3BlY2lmaWNhdGlvbiI6eyJlY3RzQ3JlZGl0UG9pbnRzIjoxMjAsImVxZkxldmVsIjo3LCJpZCI6Imh0dHBzOi8vbGVhc3Rvbi5iY2RpcGxvbWEuY29tL2xhdy1lY29ub21pY3MtbWFuYWdlbWVudCNMZWFybmluZ1NwZWNpZmljYXRpb24iLCJpc2NlZGZDb2RlIjpbIjciXSwibnFmTGV2ZWwiOlsiNyJdfX0sImV2aWRlbmNlIjp7ImRvY3VtZW50UHJlc2VuY2UiOlsiUGh5c2ljYWwiXSwiZXZpZGVuY2VEb2N1bWVudCI6WyJQYXNzcG9ydCJdLCJpZCI6Imh0dHBzOi8vZXNzaWYuZXVyb3BhLmV1L3Rzci12YS9ldmlkZW5jZS9mMmFlZWM5Ny1mYzBkLTQyYmYtOGNhNy0wNTQ4MTkyZDU2NzgiLCJzdWJqZWN0UHJlc2VuY2UiOiJQaHlzaWNhbCIsInR5cGUiOlsiRG9jdW1lbnRWZXJpZmljYXRpb24iXSwidmVyaWZpZXIiOiJkaWQ6ZWJzaToyOTYyZmI3ODRkZjYxYmFhMjY3YzgxMzI0OTc1MzlmOGM2NzRiMzdjMTI0NGE3YSJ9LCJpZCI6InVybjp1dWlkOjZiMWQ4NDExLTllZDUtNDU2Ni05YzdmLTRjMjQxNjVmZjIzNiIsImlzc3VhbmNlRGF0ZSI6IjIwMjMtMDItMDhUMTU6MTA6NTZaIiwiaXNzdWVkIjoiMjAyMy0wMi0wOFQxNToxMDo1NloiLCJpc3N1ZXIiOiJkaWQ6ZWJzaTp6aFN3NXJQWGtjSGp2cXV3blZjVHp6QiIsInByb29mIjp7ImNyZWF0ZWQiOiIyMDIyLTA0LTI3VDEyOjI1OjA3WiIsImNyZWF0b3IiOiJkaWQ6ZWJzaTp6ZFJ2dktiWGhWVkJzWGhhdGp1aUJocyIsImRvbWFpbiI6Imh0dHBzOi8vYXBpLnByZXByb2QuZWJzaS5ldSIsImp3cyI6ImV5SmlOalFpT21aaGJITmxMQ0pqY21sMElqcGJJbUkyTkNKZExDSmhiR2NpT2lKRlV6STFOa3NpZlEuLm1JQm5NOFhEUXFTWUtRTlhfTHZhSmhtc2J5Q3I1T1o1Y1UyWmstUmVxTHByNGRvRnNnbW9vYmtPNTEyOHRaeS04S2ltVmpKa0d3MHdMMXVCV25NTFdRIiwibm9uY2UiOiIzZWE2OGRhZS1kMDdhLTRkYWEtOTMyYi1mYmI1OGY1YzIwYzQiLCJ0eXBlIjoiRWNkc2FTZWNwMjU2azFTaWduYXR1cmUyMDE5In0sInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiLCJWZXJpZmlhYmxlQXR0ZXN0YXRpb24iLCJWZXJpZmlhYmxlRGlwbG9tYSJdLCJ2YWxpZEZyb20iOiIyMDIzLTAyLTA4VDE1OjEwOjU2WiJ9fQ.avhpj0UC7SJiOo8PWJV3zhJoxngdmrBnyTkQsnqe76JlOmatLkgAY5Mp_sUJQnN1uENiT-_KBf0KY3izx4X_SA', + + { + '@context': ['https://www.w3.org/2018/credentials/v1'], + 'credentialSchema': { + 'id': + 'https://api.preprod.ebsi.eu/trusted-schemas-registry/v1/schemas/0xbf78fc08a7a9f28f5479f58dea269d3657f54f13ca37d380cd4e92237fb691dd', + 'type': 'JsonSchemaValidator2018', + }, + 'credentialStatus': { + 'id': + 'https://essif.europa.eu/status/education#higherEducation#392ac7f6-399a-437b-a268-4691ead8f176', + 'type': 'CredentialStatusList2020', + }, + 'credentialSubject': { + 'awardingOpportunity': { + 'awardingBody': { + 'eidasLegalIdentifier': 'Unknown', + 'homepage': 'https://leaston.bcdiploma.com/', + 'id': 'did:ebsi:zdRvvKbXhVVBsXhatjuiBhs', + 'preferredName': 'Leaston University', + 'registration': '0597065J', + }, + 'endedAtTime': '2020-06-26T00:00:00Z', + 'id': + 'https://leaston.bcdiploma.com/law-economics-management#AwardingOpportunity', + 'identifier': + 'https://certificate-demo.bcdiploma.com/check/87ED2F2270E6C41456E94B86B9D9115B4E35BCCAD200A49B846592C14F79C86BV1Fnbllta0NZTnJkR3lDWlRmTDlSRUJEVFZISmNmYzJhUU5sZUJ5Z2FJSHpWbmZZ', + 'location': 'FRANCE', + 'startedAtTime': '2019-09-02T00:00:00Z', + }, + 'dateOfBirth': '1993-04-08', + 'familyName': 'DOE', + 'givenNames': 'Jane', + 'gradingScheme': { + 'id': + 'https://leaston.bcdiploma.com/law-economics-management#GradingScheme', + 'title': '2 year full-time programme / 4 semesters', + }, + 'id': 'did:ebsi:zoxRGVZQndTfQk54B7tKdwwNdhai5gm9F8Nav8eceNABa', + 'identifier': '0904008084H', + 'learningAchievement': { + 'additionalNote': ['DISTRIBUTION MANAGEMENT'], + 'description': + 'The Master in Information and Computer Sciences (MICS) at the University of Luxembourg enables students to acquire deeper knowledge in computer science by understanding its abstract and interdisciplinary foundations, focusing on problem solving and developing lifelong learning skills.', // ignore: lines_longer_than_80_chars + 'id': + 'https://leaston.bcdiploma.com/law-economics-management#LearningAchievment', + 'title': 'Master in Information and Computer Sciences', + }, + 'learningSpecification': { + 'ectsCreditPoints': 120, + 'eqfLevel': 7, + 'id': + 'https://leaston.bcdiploma.com/law-economics-management#LearningSpecification', + 'iscedfCode': ['7'], + 'nqfLevel': ['7'], + }, + }, + 'evidence': { + 'documentPresence': ['Physical'], + 'evidenceDocument': ['Passport'], + 'id': + 'https://essif.europa.eu/tsr-va/evidence/f2aeec97-fc0d-42bf-8ca7-0548192d5678', + 'subjectPresence': 'Physical', + 'type': ['DocumentVerification'], + 'verifier': + 'did:ebsi:2962fb784df61baa267c8132497539f8c674b37c1244a7a', + }, + 'id': 'urn:uuid:6b1d8411-9ed5-4566-9c7f-4c24165ff236', + 'issuanceDate': '2023-02-08T15:10:56Z', + 'issued': '2023-02-08T15:10:56Z', + 'issuer': 'did:ebsi:zhSw5rPXkcHjvquwnVcTzzB', + 'proof': { + 'created': '2022-04-27T12:25:07Z', + 'creator': 'did:ebsi:zdRvvKbXhVVBsXhatjuiBhs', + 'domain': 'https://api.preprod.ebsi.eu', + 'jws': + 'eyJiNjQiOmZhbHNlLCJjcml0IjpbImI2NCJdLCJhbGciOiJFUzI1NksifQ..mIBnM8XDQqSYKQNX_LvaJhmsbyCr5OZ5cU2Zk-ReqLpr4doFsgmoobkO5128tZy-8KimVjJkGw0wL1uBWnMLWQ', // ignore: lines_longer_than_80_chars + 'nonce': '3ea68dae-d07a-4daa-932b-fbb58f5c20c4', + 'type': 'EcdsaSecp256k1Signature2019', + }, + 'type': [ + 'VerifiableCredential', + 'VerifiableAttestation', + 'VerifiableDiploma', + ], + 'validFrom': '2023-02-08T15:10:56Z', + } + ]; + + test('nonce', () { + expect(verifierTokenParameters.nonce, nonce); + }); + + test('jwtsOfCredentials', () { + expect(verifierTokenParameters.jsonIdOrJwtList, jwtsOfCredentials); + }); + + test('audience', () { + expect(verifierTokenParameters.audience, audience); + }); + + group('override test', () { + final verifierTokenParametersTest = VerifierTokenParametersTest(); + + test( + 'public key is P-256K private key without d parameter', + verifierTokenParametersTest.publicKeyTest, + ); + + test('did EBSI', verifierTokenParametersTest.didTest); + + test('kID EBSI', verifierTokenParametersTest.keyIdTest); + + group('algorithm test', () { + test( + "algorithm is ES256K when key's curve is not P-256", + verifierTokenParametersTest.algorithmIsES256KTest, + ); + + test( + "algorithm is ES256 when key's curve is P-256", + verifierTokenParametersTest.algorithmIsES256Test, + ); + + test( + 'if alg is not null then return as it is', + verifierTokenParametersTest.algorithmIsNotNullTest, + ); + }); + + group('thumbprint test', () { + test( + 'thumbprint of the public Key', + verifierTokenParametersTest.thumprintOfKey, + ); + + test( + 'thumbrprint of the Key from exemple in rfc 7638', + verifierTokenParametersTest.thumprintOfKeyForrfc7638, + ); + }); + }); + }); +} diff --git a/packages/polygonid/analysis_options.yaml b/packages/polygonid/analysis_options.yaml index 55ba4e475..700f43002 100644 --- a/packages/polygonid/analysis_options.yaml +++ b/packages/polygonid/analysis_options.yaml @@ -2,6 +2,8 @@ include: package:very_good_analysis/analysis_options.yaml analyzer: exclude: - "build/" + - "**/*.g.dart" + linter: rules: public_member_api_docs: false diff --git a/packages/polygonid/lib/src/polygonid.dart b/packages/polygonid/lib/src/polygonid.dart index b13f2fba4..b3557a778 100644 --- a/packages/polygonid/lib/src/polygonid.dart +++ b/packages/polygonid/lib/src/polygonid.dart @@ -20,16 +20,18 @@ import 'package:web3dart/web3dart.dart'; /// A Very Good Project created by Very Good CLI. /// {@endtemplate} class PolygonId { - /// {@macro polygonid} - factory PolygonId() { - return _instance ??= PolygonId._(); + factory PolygonId({PolygonIdSdk? sdk}) { + if (sdk != null) { + _instance.polygonIdSdk = sdk; + } + return _instance; } - /// private contructor - PolygonId._(); + PolygonId._internal(); + + late PolygonIdSdk polygonIdSdk; - /// _instance - static PolygonId? _instance; + static final PolygonId _instance = PolygonId._internal(); /// blockchain static const blockchain = 'polygon'; @@ -62,6 +64,8 @@ class PolygonId { ipfsUrl: ipfsUrl, ), ); + + polygonIdSdk = PolygonIdSdk.I; isInitialized = true; } @@ -75,7 +79,7 @@ class PolygonId { required String network, required String ipfsUrl, }) { - return PolygonIdSdk.I.setEnv( + return polygonIdSdk.setEnv( env: EnvEntity( blockchain: blockchain, network: network, @@ -91,64 +95,38 @@ class PolygonId { /// PolygonId SDK getEnv Future getEnv() async { - return PolygonIdSdk.I.getEnv(); + return polygonIdSdk.getEnv(); } /// check if curcuit is already downloaded Future isCircuitsDownloaded() async { final isDownloaded = - await PolygonIdSdk.I.proof.isAlreadyDownloadedCircuitsFromServer(); + await polygonIdSdk.proof.isAlreadyDownloadedCircuitsFromServer(); return isDownloaded; } /// init Circuits Download And Get Info Stream Stream get initCircuitsDownloadAndGetInfoStream { - return PolygonIdSdk.I.proof.initCircuitsDownloadAndGetInfoStream; - } - - /// Create Identity - /// - /// blockchain and network are not optional, they are used to associate the - /// identity to a specific blockchain network. - /// - /// it is recommended to securely save the privateKey generated with - /// createIdentity(), this will often be used within the sdk methods as a - /// security system, you can find the privateKey in the PrivateIdentityEntity - /// object. - /// - Future addIdentity({ - required String mnemonic, - required String network, - }) async { - try { - final sdk = PolygonIdSdk.I; - final secret = bip393.mnemonicToEntropy(mnemonic); - final identity = await sdk.identity.addIdentity(secret: secret); - return identity; - } catch (e) { - if (e is IdentityAlreadyExistsException) { - final identity = await getIdentity( - mnemonic: mnemonic, - network: network, - ); - return identity; - } else { - throw Exception('ISSUE_WHILE_ADDING_IDENTITY'); - } - } + return polygonIdSdk.proof.initCircuitsDownloadAndGetInfoStream; } /// get private key from mnemonics Future getPrivateKey({ required String mnemonic, + Uint8List? private, }) async { - final secret = bip393.mnemonicToEntropy(mnemonic); + late Uint8List privateKey; - final privadoIdWallet = await BjjWallet.createBjjWallet( - secret: Uint8List.fromList(secret.codeUnits), - ); + if (private == null) { + final secret = bip393.mnemonicToEntropy(mnemonic); + final privadoIdWallet = await BjjWallet.createBjjWallet( + secret: Uint8List.fromList(secret.codeUnits), + ); + privateKey = privadoIdWallet.privateKey; + } else { + privateKey = private; + } - final privateKey = privadoIdWallet.privateKey; final epk = HEX.encode(privateKey); return epk; } @@ -157,10 +135,13 @@ class PolygonId { Future getUserIdentity({ required String mnemonic, required String network, + Uint8List? private, }) async { - final sdk = PolygonIdSdk.I; - final privateKey = await getPrivateKey(mnemonic: mnemonic); - final did = await sdk.identity.getDidIdentifier( + final privateKey = await getPrivateKey( + mnemonic: mnemonic, + private: private, + ); + final did = await polygonIdSdk.identity.getDidIdentifier( blockchain: blockchain, network: network, privateKey: privateKey, @@ -173,32 +154,63 @@ class PolygonId { Future getIdentity({ required String mnemonic, required String network, + Uint8List? private, }) async { - final sdk = PolygonIdSdk.I; final userIdentity = await getUserIdentity( mnemonic: mnemonic, network: network, + private: private, ); - final identity = await sdk.identity.restoreIdentity( + final identity = await polygonIdSdk.identity.restoreIdentity( privateKey: userIdentity.privateKey, genesisDid: userIdentity.did, ); return identity; } + /// Create Identity + /// + /// blockchain and network are not optional, they are used to associate the + /// identity to a specific blockchain network. + /// + /// it is recommended to securely save the privateKey generated with + /// createIdentity(), this will often be used within the sdk methods as a + /// security system, you can find the privateKey in the PrivateIdentityEntity + /// object. + /// + Future addIdentity({ + required String mnemonic, + required String network, + }) async { + try { + final secret = bip393.mnemonicToEntropy(mnemonic); + final identity = await polygonIdSdk.identity.addIdentity(secret: secret); + return identity; + } catch (e) { + if (e is IdentityAlreadyExistsException) { + final identity = await getIdentity( + mnemonic: mnemonic, + network: network, + ); + return identity; + } else { + throw Exception('ISSUE_WHILE_ADDING_IDENTITY'); + } + } + } + /// Remove the previously stored identity associated with the identifier Future removeIdentity({ required String genesisDid, required String privateKey, required String network, }) async { - final sdk = PolygonIdSdk.I; - final genesisDid = await sdk.identity.getDidIdentifier( + final genesisDid = await polygonIdSdk.identity.getDidIdentifier( blockchain: blockchain, network: network, privateKey: privateKey, ); - return sdk.identity.removeIdentity( + return polygonIdSdk.identity.removeIdentity( privateKey: privateKey, genesisDid: genesisDid, ); @@ -207,8 +219,7 @@ class PolygonId { /// Get a list of public info of [IdentityEntity] associated /// to the identities stored in the Polygon ID Sdk. Future> getIdentities() { - final sdk = PolygonIdSdk.I; - return sdk.identity.getIdentities(); + return polygonIdSdk.identity.getIdentities(); } /// Returns a [Iden3MessageEntity] from an iden3comm message string. @@ -219,8 +230,7 @@ class PolygonId { /// iden3comm message string needs to be parsed to a supported /// [Iden3MessageEntity] by the Polygon Id Sdk using this method. Future getIden3Message({required String message}) { - final sdk = PolygonIdSdk.I; - return sdk.iden3comm.getIden3Message(message: message); + return polygonIdSdk.iden3comm.getIden3Message(message: message); } /// Authenticate response from iden3Message sharing the needed @@ -239,12 +249,15 @@ class PolygonId { required Iden3MessageEntity iden3MessageEntity, required String network, required String mnemonic, + Uint8List? private, }) async { try { - final sdk = PolygonIdSdk.I; + final privateKey = await getPrivateKey( + mnemonic: mnemonic, + private: private, + ); - final privateKey = await getPrivateKey(mnemonic: mnemonic); - final did = await sdk.identity.getDidIdentifier( + final did = await polygonIdSdk.identity.getDidIdentifier( blockchain: blockchain, network: network, privateKey: privateKey, @@ -252,7 +265,7 @@ class PolygonId { /// Authenticate response from iden3Message sharing the needed /// (if any) proofs requested by it - await sdk.iden3comm.authenticate( + await polygonIdSdk.iden3comm.authenticate( message: iden3MessageEntity, genesisDid: did, privateKey: privateKey, @@ -279,18 +292,20 @@ class PolygonId { required Iden3MessageEntity iden3MessageEntity, required String mnemonic, required String network, + Uint8List? private, }) async { try { - final sdk = PolygonIdSdk.I; - - final privateKey = await getPrivateKey(mnemonic: mnemonic); - final did = await sdk.identity.getDidIdentifier( + final privateKey = await getPrivateKey( + mnemonic: mnemonic, + private: private, + ); + final did = await polygonIdSdk.identity.getDidIdentifier( blockchain: blockchain, network: network, privateKey: privateKey, ); - final claimEntities = await sdk.iden3comm.fetchAndSaveClaims( + final claimEntities = await polygonIdSdk.iden3comm.fetchAndSaveClaims( message: iden3MessageEntity, genesisDid: did, privateKey: privateKey, @@ -310,17 +325,19 @@ class PolygonId { required String claimId, required String mnemonic, required String network, + Uint8List? private, }) async { - final sdk = PolygonIdSdk.I; - - final privateKey = await getPrivateKey(mnemonic: mnemonic); - final did = await sdk.identity.getDidIdentifier( + final privateKey = await getPrivateKey( + mnemonic: mnemonic, + private: private, + ); + final did = await polygonIdSdk.identity.getDidIdentifier( blockchain: blockchain, network: network, privateKey: privateKey, ); - return sdk.credential.getClaimsByIds( + return polygonIdSdk.credential.getClaimsByIds( claimIds: [claimId], genesisDid: did, privateKey: privateKey, @@ -344,13 +361,14 @@ class PolygonId { Future backupIdentity({ required String mnemonic, required String network, + Uint8List? private, }) async { - final sdk = PolygonIdSdk.I; final userIdentity = await getUserIdentity( mnemonic: mnemonic, network: network, + private: private, ); - return sdk.identity.backupIdentity( + return polygonIdSdk.identity.backupIdentity( privateKey: userIdentity.privateKey, genesisDid: userIdentity.did, ); @@ -374,13 +392,14 @@ class PolygonId { required String mnemonic, required String encryptedDb, required String network, + Uint8List? private, }) async { - final sdk = PolygonIdSdk.I; final userIdentity = await getUserIdentity( mnemonic: mnemonic, network: network, + private: private, ); - final identity = await sdk.identity.restoreIdentity( + final identity = await polygonIdSdk.identity.restoreIdentity( privateKey: userIdentity.privateKey, genesisDid: userIdentity.did, encryptedDb: encryptedDb, @@ -401,9 +420,7 @@ class PolygonId { required PrivateIdentityEntity privateIdentityEntity, }) async { try { - final sdk = PolygonIdSdk.I; - - final claimEntities = await sdk.credential.getClaims( + final claimEntities = await polygonIdSdk.credential.getClaims( genesisDid: privateIdentityEntity.did, privateKey: privateIdentityEntity.privateKey, ); @@ -418,8 +435,7 @@ class PolygonId { Future>> getSchemas({ required Iden3MessageEntity message, }) async { - final sdk = PolygonIdSdk.I; - return sdk.iden3comm.getSchemas(message: message); + return polygonIdSdk.iden3comm.getSchemas(message: message); } /// Get a list of [ClaimEntity] from iden3comm message @@ -438,19 +454,22 @@ class PolygonId { required Iden3MessageEntity iden3MessageEntity, required String mnemonic, required String network, + Uint8List? private, }) async { try { - final sdk = PolygonIdSdk.I; - - final privateKey = await getPrivateKey(mnemonic: mnemonic); + final privateKey = await getPrivateKey( + mnemonic: mnemonic, + private: private, + ); - final did = await sdk.identity.getDidIdentifier( + final did = await polygonIdSdk.identity.getDidIdentifier( blockchain: blockchain, network: network, privateKey: privateKey, ); - final claimEntities = await sdk.iden3comm.getClaimsFromIden3Message( + final claimEntities = + await polygonIdSdk.iden3comm.getClaimsFromIden3Message( message: iden3MessageEntity, genesisDid: did, privateKey: privateKey, @@ -468,8 +487,8 @@ class PolygonId { required Iden3MessageEntity contractIden3messageEntity, required String mnemonic, required String network, + Uint8List? private, }) async { - final sdk = PolygonIdSdk.I; var challenge = walletAddress; if (challenge.toLowerCase().startsWith('0x')) { @@ -483,9 +502,11 @@ class PolygonId { final userIdentity = await getUserIdentity( mnemonic: mnemonic, network: network, + private: private, ); - final List response = await sdk.iden3comm.getProofs( + final List response = + await polygonIdSdk.iden3comm.getProofs( message: contractIden3messageEntity, genesisDid: userIdentity.did, privateKey: userIdentity.privateKey, diff --git a/packages/polygonid/pubspec.yaml b/packages/polygonid/pubspec.yaml index 590605453..c680a37ce 100644 --- a/packages/polygonid/pubspec.yaml +++ b/packages/polygonid/pubspec.yaml @@ -20,4 +20,5 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter + mocktail: ^1.0.2 very_good_analysis: ^5.0.0+1 diff --git a/packages/polygonid/test/src/polygonid_test.dart b/packages/polygonid/test/src/polygonid_test.dart index 8978d590f..652cbf0b3 100644 --- a/packages/polygonid/test/src/polygonid_test.dart +++ b/packages/polygonid/test/src/polygonid_test.dart @@ -1,11 +1,472 @@ // ignore_for_file: prefer_const_constructors +import 'dart:typed_data'; + import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:polygonid/polygonid.dart'; +import 'package:polygonid_flutter_sdk/common/domain/entities/env_entity.dart'; +import 'package:polygonid_flutter_sdk/identity/domain/entities/identity_entity.dart'; +import 'package:polygonid_flutter_sdk/sdk/credential.dart'; +import 'package:polygonid_flutter_sdk/sdk/iden3comm.dart'; +import 'package:polygonid_flutter_sdk/sdk/identity.dart'; +import 'package:polygonid_flutter_sdk/sdk/polygon_id_sdk.dart'; +import 'package:polygonid_flutter_sdk/sdk/proof.dart'; + +class ConcreteIden3MessageEntity extends Iden3MessageEntity { + const ConcreteIden3MessageEntity({ + required super.id, + required super.typ, + required super.type, + super.messageType, + required super.thid, + required super.from, + super.to, + required this.body, + }); + + @override + final dynamic body; +} + +final iden3MessageEntity = ConcreteIden3MessageEntity( + id: '123', + typ: 'type', + type: 'type', + messageType: Iden3MessageType.authRequest, + thid: 'thid', + from: 'from', + to: 'to', + body: {'key': 'value'}, +); + +final downloadInfo = DownloadInfo.onDone(contentLength: 1, downloaded: 1); +const mnemonics = + '''notice photo opera keen climb agent soft parrot best joke field devote'''; + +const privateIdentityEntity = PrivateIdentityEntity( + did: '', + privateKey: '', + profiles: {}, + publicKey: [], +); + +final privateKey = Uint8List.fromList([ + 0x00, + 0x01, + 0x02, + 0x03, + 0x04, + 0x05, + 0x06, + 0x07, + 0x08, + 0x09, + 0x0a, + 0x0b, + 0x0c, + 0x0d, + 0x0e, + 0x0f, + 0x10, + 0x11, + 0x12, + 0x13, + 0x14, + 0x15, + 0x16, + 0x17, + 0x18, + 0x19, + 0x1a, + 0x1b, + 0x1c, + 0x1d, + 0x1e, + 0x1f, +]); + +const stringPrivateKey = + '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f'; + +const did = 'I am did'; + +class MockProof extends Mock implements Proof { + @override + Future isAlreadyDownloadedCircuitsFromServer() async { + return true; + } + + @override + Stream get initCircuitsDownloadAndGetInfoStream { + return Stream.value(downloadInfo); + } +} + +class MockIdentity extends Mock implements Identity { + @override + Future getDidIdentifier({ + required String privateKey, + required String blockchain, + required String network, + BigInt? profileNonce, + }) { + return Future.value(did); + } + + @override + Future addIdentity({String? secret}) async { + return Future.value(privateIdentityEntity); + } + + @override + Future removeIdentity({ + required String genesisDid, + required String privateKey, + }) { + return Future.value(); + } + + @override + Future> getIdentities() { + return Future.value([]); + } + + @override + Future restoreIdentity({ + required String genesisDid, + required String privateKey, + String? encryptedDb, + }) { + return Future.value(privateIdentityEntity); + } + + @override + Future backupIdentity({ + required String genesisDid, + required String privateKey, + }) { + return Future.value('ok'); + } +} + +class MockCredential extends Mock implements Credential { + @override + Future> getClaims({ + List? filters, + required String genesisDid, + required String privateKey, + }) { + return Future.value([]); + } + + @override + Future> getClaimsByIds({ + required List claimIds, + required String genesisDid, + required String privateKey, + }) { + return Future.value([]); + } +} + +class MockIden3comm extends Mock implements Iden3comm { + @override + Future getIden3Message({required String message}) { + return Future.value(iden3MessageEntity); + } + + @override + Future>> getSchemas({ + required Iden3MessageEntity message, + }) { + return Future.value([]); + } + + @override + Future authenticate({ + required Iden3MessageEntity message, + required String genesisDid, + BigInt? profileNonce, + required String privateKey, + String? pushToken, + Map>? nonRevocationProofs, + String? challenge, + }) { + return Future.value(); + } + + @override + Future> fetchAndSaveClaims({ + required Iden3MessageEntity message, + required String genesisDid, + BigInt? profileNonce, + required String privateKey, + }) { + return Future.value([]); + } + + @override + Future> getClaimsFromIden3Message({ + required Iden3MessageEntity message, + required String genesisDid, + BigInt? profileNonce, + required String privateKey, + Map>? nonRevocationProofs, + }) { + return Future.value([]); + } +} + +class MockPolygonIdSdk extends Mock implements PolygonIdSdk {} void main() { + final MockPolygonIdSdk polygonIdSdk = MockPolygonIdSdk(); + final PolygonId polygonId = PolygonId(sdk: polygonIdSdk); + + final proof = MockProof(); + final identity = MockIdentity(); + final credential = MockCredential(); + final iden3comm = MockIden3comm(); + + final env = EnvEntity( + blockchain: 'polygon', + network: 'network', + web3Url: 'web3Url', + web3RdpUrl: 'web3RdpUrl', + web3ApiKey: 'web3ApiKey', + idStateContract: 'idStateContract', + pushUrl: 'pushUrl', + ipfsUrl: 'ipfsUrl', + ); + group('PolygonId', () { test('can be instantiated', () { - expect(PolygonId(), isNotNull); + expect(polygonId, isNotNull); + }); + + test('initialised default value is correct', () { + expect(PolygonId.blockchain, 'polygon'); + expect(polygonId.isInitialized, false); + }); + + test('setEnv works correctly', () async { + when(() => polygonIdSdk.setEnv(env: env)).thenAnswer((_) async => {}); + await polygonId.setEnv( + network: 'network', + web3Url: 'web3Url', + web3RdpUrl: 'web3RdpUrl', + web3ApiKey: 'web3ApiKey', + idStateContract: 'idStateContract', + pushUrl: 'pushUrl', + ipfsUrl: 'ipfsUrl', + ); + verify(() => polygonIdSdk.setEnv(env: env)).called(1); + }); + + test('getEnv works correctly', () async { + when(polygonIdSdk.getEnv).thenAnswer((_) async => env); + final data = await polygonId.getEnv(); + expect(data, env); + verify(polygonIdSdk.getEnv).called(1); + }); + + test('isCircuitsDownloaded works correctly', () async { + when(() => polygonIdSdk.proof).thenReturn(proof); + final data = await polygonId.isCircuitsDownloaded(); + expect(data, true); + }); + + test('initCircuitsDownloadAndGetInfoStream works correctly', () async { + when(() => polygonIdSdk.proof).thenReturn(proof); + final stream = polygonId.initCircuitsDownloadAndGetInfoStream; + final emittedValues = await stream.toList(); + expect(emittedValues, [downloadInfo]); + }); + + test('getPrivateKey returns correct value', () async { + final data = await polygonId.getPrivateKey( + mnemonic: mnemonics, + private: privateKey, + ); + expect(data, stringPrivateKey); + }); + + test('getUserIdentity returns correct value', () async { + when(() => polygonIdSdk.identity).thenReturn(identity); + final data = await polygonId.getUserIdentity( + mnemonic: mnemonics, + private: privateKey, + network: 'polygon', + ); + expect(data.did, did); + expect(data.privateKey, stringPrivateKey); + }); + + test('getIdentity returns correct value', () async { + when(() => polygonIdSdk.identity).thenReturn(identity); + final data = await polygonId.getIdentity( + mnemonic: mnemonics, + private: privateKey, + network: 'polygon', + ); + + expect(data, privateIdentityEntity); + }); + + test('addIdentity works correctly', () async { + when(() => polygonIdSdk.identity).thenReturn(identity); + final data = + await polygonId.addIdentity(mnemonic: mnemonics, network: 'polygon'); + expect(data, privateIdentityEntity); + }); + + test('getIden3Message works correctly', () async { + when(() => polygonIdSdk.iden3comm).thenReturn(iden3comm); + + final data = await polygonId.getIden3Message(message: ''); + expect(data, iden3MessageEntity); + }); + + test('removeIdentity works correctly', () async { + when(() => polygonIdSdk.identity).thenReturn(identity); + + await polygonId.removeIdentity( + genesisDid: '', + network: '', + privateKey: '', + ); + verify(() => polygonIdSdk.identity.removeIdentity); + }); + + test('authenticate works correctly', () async { + when(() => polygonIdSdk.iden3comm).thenReturn(iden3comm); + when(() => polygonIdSdk.identity).thenReturn(identity); + + final data = await polygonId.authenticate( + iden3MessageEntity: iden3MessageEntity, + network: 'polygon', + mnemonic: mnemonics, + private: privateKey, + ); + + expect(data, true); + }); + + test('authenticate throws Exception', () async { + when(() => polygonIdSdk.iden3comm).thenReturn(iden3comm); + when(() => polygonIdSdk.identity).thenReturn(identity); + + final data = await polygonId.authenticate( + iden3MessageEntity: iden3MessageEntity, + network: 'polygon', + mnemonic: mnemonics, + ); + + expect(data, false); + }); + + test('getClaims works correctly', () async { + when(() => polygonIdSdk.iden3comm).thenReturn(iden3comm); + when(() => polygonIdSdk.identity).thenReturn(identity); + final data = await polygonId.getClaims( + iden3MessageEntity: iden3MessageEntity, + network: 'polygon', + mnemonic: mnemonics, + private: privateKey, + ); + expect(data, []); + }); + + test('getClaims throws Exception', () async { + when(() => polygonIdSdk.iden3comm).thenReturn(iden3comm); + when(() => polygonIdSdk.identity).thenReturn(identity); + expect( + () => polygonId.getClaims( + iden3MessageEntity: iden3MessageEntity, + network: 'polygon', + mnemonic: mnemonics, + ), + throwsA(isA()), + ); + }); + + test('getClaimById works correctly', () async { + when(() => polygonIdSdk.credential).thenReturn(credential); + when(() => polygonIdSdk.identity).thenReturn(identity); + final data = await polygonId.getClaimById( + network: 'polygon', + mnemonic: mnemonics, + private: privateKey, + claimId: '', + ); + expect(data, []); + }); + + test('backupIdentity works correctly', () async { + when(() => polygonIdSdk.identity).thenReturn(identity); + final data = await polygonId.backupIdentity( + network: 'polygon', + mnemonic: mnemonics, + private: privateKey, + ); + expect(data, 'ok'); + }); + + test('backupIdrestoreIdentityentity works correctly', () async { + when(() => polygonIdSdk.identity).thenReturn(identity); + final data = await polygonId.restoreIdentity( + network: 'polygon', + mnemonic: mnemonics, + encryptedDb: '', + private: privateKey, + ); + expect(data, privateIdentityEntity); + }); + + test('restoreClaims works correctly', () async { + when(() => polygonIdSdk.credential).thenReturn(credential); + + final data = await polygonId.restoreClaims( + privateIdentityEntity: PrivateIdentityEntity( + did: '', + privateKey: '', + profiles: {}, + publicKey: [], + ), + ); + expect(data, []); + }); + + test('getSchemas works correctly', () async { + when(() => polygonIdSdk.iden3comm).thenReturn(iden3comm); + + final data = await polygonId.getSchemas(message: iden3MessageEntity); + expect(data, >[]); + }); + + test('getClaimsFromIden3Message throws Exception', () async { + when(() => polygonIdSdk.iden3comm).thenReturn(iden3comm); + when(() => polygonIdSdk.identity).thenReturn(identity); + expect( + () => polygonId.getClaimsFromIden3Message( + iden3MessageEntity: iden3MessageEntity, + network: 'polygon', + mnemonic: mnemonics, + ), + throwsA(isA()), + ); + }); + + test('getClaimsFromIden3Message works correctly', () async { + when(() => polygonIdSdk.iden3comm).thenReturn(iden3comm); + when(() => polygonIdSdk.identity).thenReturn(identity); + final data = await polygonId.getClaimsFromIden3Message( + iden3MessageEntity: iden3MessageEntity, + network: 'polygon', + mnemonic: mnemonics, + private: privateKey, + ); + expect(data, []); }); }); } diff --git a/packages/secure_jwt_signer/.github/ISSUE_TEMPLATE/bug_report.md b/packages/secure_jwt_signer/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..50a4c7b8b --- /dev/null +++ b/packages/secure_jwt_signer/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,29 @@ +--- +name: Bug Report +about: Create a report to help us improve +title: "fix: " +labels: bug +--- + +**Description** + +A clear and concise description of what the bug is. + +**Steps To Reproduce** + +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected Behavior** + +A clear and concise description of what you expected to happen. + +**Screenshots** + +If applicable, add screenshots to help explain your problem. + +**Additional Context** + +Add any other context about the problem here. diff --git a/packages/secure_jwt_signer/.github/ISSUE_TEMPLATE/build.md b/packages/secure_jwt_signer/.github/ISSUE_TEMPLATE/build.md new file mode 100644 index 000000000..0cf8e62cd --- /dev/null +++ b/packages/secure_jwt_signer/.github/ISSUE_TEMPLATE/build.md @@ -0,0 +1,14 @@ +--- +name: Build System +about: Changes that affect the build system or external dependencies +title: "build: " +labels: build +--- + +**Description** + +Describe what changes need to be done to the build system and why. + +**Requirements** + +- [ ] The build system is passing diff --git a/packages/secure_jwt_signer/.github/ISSUE_TEMPLATE/chore.md b/packages/secure_jwt_signer/.github/ISSUE_TEMPLATE/chore.md new file mode 100644 index 000000000..498ebfd82 --- /dev/null +++ b/packages/secure_jwt_signer/.github/ISSUE_TEMPLATE/chore.md @@ -0,0 +1,14 @@ +--- +name: Chore +about: Other changes that don't modify src or test files +title: "chore: " +labels: chore +--- + +**Description** + +Clearly describe what change is needed and why. If this changes code then please use another issue type. + +**Requirements** + +- [ ] No functional changes to the code diff --git a/packages/secure_jwt_signer/.github/ISSUE_TEMPLATE/ci.md b/packages/secure_jwt_signer/.github/ISSUE_TEMPLATE/ci.md new file mode 100644 index 000000000..fa2dd9e2d --- /dev/null +++ b/packages/secure_jwt_signer/.github/ISSUE_TEMPLATE/ci.md @@ -0,0 +1,14 @@ +--- +name: Continuous Integration +about: Changes to the CI configuration files and scripts +title: "ci: " +labels: ci +--- + +**Description** + +Describe what changes need to be done to the ci/cd system and why. + +**Requirements** + +- [ ] The ci system is passing diff --git a/packages/secure_jwt_signer/.github/ISSUE_TEMPLATE/config.yml b/packages/secure_jwt_signer/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..ec4bb386b --- /dev/null +++ b/packages/secure_jwt_signer/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1 @@ +blank_issues_enabled: false \ No newline at end of file diff --git a/packages/secure_jwt_signer/.github/ISSUE_TEMPLATE/documentation.md b/packages/secure_jwt_signer/.github/ISSUE_TEMPLATE/documentation.md new file mode 100644 index 000000000..f494a4d98 --- /dev/null +++ b/packages/secure_jwt_signer/.github/ISSUE_TEMPLATE/documentation.md @@ -0,0 +1,14 @@ +--- +name: Documentation +about: Improve the documentation so all collaborators have a common understanding +title: "docs: " +labels: documentation +--- + +**Description** + +Clearly describe what documentation you are looking to add or improve. + +**Requirements** + +- [ ] Requirements go here diff --git a/packages/secure_jwt_signer/.github/ISSUE_TEMPLATE/feature_request.md b/packages/secure_jwt_signer/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..ddd2fcca9 --- /dev/null +++ b/packages/secure_jwt_signer/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,18 @@ +--- +name: Feature Request +about: A new feature to be added to the project +title: "feat: " +labels: feature +--- + +**Description** + +Clearly describe what you are looking to add. The more context the better. + +**Requirements** + +- [ ] Checklist of requirements to be fulfilled + +**Additional Context** + +Add any other context or screenshots about the feature request go here. diff --git a/packages/secure_jwt_signer/.github/ISSUE_TEMPLATE/performance.md b/packages/secure_jwt_signer/.github/ISSUE_TEMPLATE/performance.md new file mode 100644 index 000000000..699b8d45f --- /dev/null +++ b/packages/secure_jwt_signer/.github/ISSUE_TEMPLATE/performance.md @@ -0,0 +1,14 @@ +--- +name: Performance Update +about: A code change that improves performance +title: "perf: " +labels: performance +--- + +**Description** + +Clearly describe what code needs to be changed and what the performance impact is going to be. Bonus point's if you can tie this directly to user experience. + +**Requirements** + +- [ ] There is no drop in test coverage. diff --git a/packages/secure_jwt_signer/.github/ISSUE_TEMPLATE/refactor.md b/packages/secure_jwt_signer/.github/ISSUE_TEMPLATE/refactor.md new file mode 100644 index 000000000..1626c5704 --- /dev/null +++ b/packages/secure_jwt_signer/.github/ISSUE_TEMPLATE/refactor.md @@ -0,0 +1,14 @@ +--- +name: Refactor +about: A code change that neither fixes a bug nor adds a feature +title: "refactor: " +labels: refactor +--- + +**Description** + +Clearly describe what needs to be refactored and why. Please provide links to related issues (bugs or upcoming features) in order to help prioritize. + +**Requirements** + +- [ ] There is no drop in test coverage. diff --git a/packages/secure_jwt_signer/.github/ISSUE_TEMPLATE/revert.md b/packages/secure_jwt_signer/.github/ISSUE_TEMPLATE/revert.md new file mode 100644 index 000000000..9d121dc56 --- /dev/null +++ b/packages/secure_jwt_signer/.github/ISSUE_TEMPLATE/revert.md @@ -0,0 +1,16 @@ +--- +name: Revert Commit +about: Reverts a previous commit +title: "revert: " +labels: revert +--- + +**Description** + +Provide a link to a PR/Commit that you are looking to revert and why. + +**Requirements** + +- [ ] Change has been reverted +- [ ] No change in test coverage has happened +- [ ] A new ticket is created for any follow on work that needs to happen diff --git a/packages/secure_jwt_signer/.github/ISSUE_TEMPLATE/style.md b/packages/secure_jwt_signer/.github/ISSUE_TEMPLATE/style.md new file mode 100644 index 000000000..02244a7bd --- /dev/null +++ b/packages/secure_jwt_signer/.github/ISSUE_TEMPLATE/style.md @@ -0,0 +1,14 @@ +--- +name: Style Changes +about: Changes that do not affect the meaning of the code (white space, formatting, missing semi-colons, etc) +title: "style: " +labels: style +--- + +**Description** + +Clearly describe what you are looking to change and why. + +**Requirements** + +- [ ] There is no drop in test coverage. diff --git a/packages/secure_jwt_signer/.github/ISSUE_TEMPLATE/test.md b/packages/secure_jwt_signer/.github/ISSUE_TEMPLATE/test.md new file mode 100644 index 000000000..431a7ea76 --- /dev/null +++ b/packages/secure_jwt_signer/.github/ISSUE_TEMPLATE/test.md @@ -0,0 +1,14 @@ +--- +name: Test +about: Adding missing tests or correcting existing tests +title: "test: " +labels: test +--- + +**Description** + +List out the tests that need to be added or changed. Please also include any information as to why this was not covered in the past. + +**Requirements** + +- [ ] There is no drop in test coverage. diff --git a/packages/secure_jwt_signer/.github/PULL_REQUEST_TEMPLATE.md b/packages/secure_jwt_signer/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..116993637 --- /dev/null +++ b/packages/secure_jwt_signer/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,27 @@ + + +## Status + +**READY/IN DEVELOPMENT/HOLD** + +## Description + + + +## Type of Change + + + +- [ ] ✨ New feature (non-breaking change which adds functionality) +- [ ] 🛠️ Bug fix (non-breaking change which fixes an issue) +- [ ] ❌ Breaking change (fix or feature that would cause existing functionality to change) +- [ ] 🧹 Code refactor +- [ ] ✅ Build configuration change +- [ ] 📝 Documentation +- [ ] 🗑️ Chore diff --git a/packages/secure_jwt_signer/.github/cspell.json b/packages/secure_jwt_signer/.github/cspell.json new file mode 100644 index 000000000..be80a6290 --- /dev/null +++ b/packages/secure_jwt_signer/.github/cspell.json @@ -0,0 +1,21 @@ +{ + "version": "0.2", + "$schema": "https://raw.githubusercontent.com/streetsidesoftware/cspell/main/cspell.schema.json", + "dictionaries": ["vgv_allowed", "vgv_forbidden"], + "dictionaryDefinitions": [ + { + "name": "vgv_allowed", + "path": "https://raw.githubusercontent.com/verygoodopensource/very_good_dictionaries/main/allowed.txt", + "description": "Allowed VGV Spellings" + }, + { + "name": "vgv_forbidden", + "path": "https://raw.githubusercontent.com/verygoodopensource/very_good_dictionaries/main/forbidden.txt", + "description": "Forbidden VGV Spellings" + } + ], + "useGitignore": true, + "words": [ + "secure_jwt_signer" + ] +} diff --git a/packages/secure_jwt_signer/.github/dependabot.yaml b/packages/secure_jwt_signer/.github/dependabot.yaml new file mode 100644 index 000000000..63b035cde --- /dev/null +++ b/packages/secure_jwt_signer/.github/dependabot.yaml @@ -0,0 +1,11 @@ +version: 2 +enable-beta-ecosystems: true +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" + - package-ecosystem: "pub" + directory: "/" + schedule: + interval: "daily" diff --git a/packages/secure_jwt_signer/.github/workflows/main.yaml b/packages/secure_jwt_signer/.github/workflows/main.yaml new file mode 100644 index 000000000..c7146c3cb --- /dev/null +++ b/packages/secure_jwt_signer/.github/workflows/main.yaml @@ -0,0 +1,27 @@ +name: ci + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + semantic_pull_request: + uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/semantic_pull_request.yml@v1 + + spell-check: + uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/spell_check.yml@v1 + with: + includes: "**/*.md" + modified_files_only: false + + build: + uses: VeryGoodOpenSource/very_good_workflows/.github/workflows/dart_package.yml@v1 + diff --git a/packages/secure_jwt_signer/.gitignore b/packages/secure_jwt_signer/.gitignore new file mode 100644 index 000000000..526da1584 --- /dev/null +++ b/packages/secure_jwt_signer/.gitignore @@ -0,0 +1,7 @@ +# See https://www.dartlang.org/guides/libraries/private-files + +# Files and directories created by pub +.dart_tool/ +.packages +build/ +pubspec.lock \ No newline at end of file diff --git a/packages/secure_jwt_signer/README.md b/packages/secure_jwt_signer/README.md new file mode 100644 index 000000000..148264463 --- /dev/null +++ b/packages/secure_jwt_signer/README.md @@ -0,0 +1,62 @@ +# Secure Jwt Signer + +[![style: very good analysis][very_good_analysis_badge]][very_good_analysis_link] +[![Powered by Mason](https://img.shields.io/endpoint?url=https%3A%2F%2Ftinyurl.com%2Fmason-badge)](https://github.com/felangel/mason) +[![License: MIT][license_badge]][license_link] + +Sign jwt with keys from TEE/Strongbox or Secure Element + +## Installation 💻 + +**❗ In order to start using Secure Jwt Signer you must have the [Dart SDK][dart_install_link] installed on your machine.** + +Install via `dart pub add`: + +```sh +dart pub add secure_jwt_signer +``` + +--- + +## Continuous Integration 🤖 + +Secure Jwt Signer comes with a built-in [GitHub Actions workflow][github_actions_link] powered by [Very Good Workflows][very_good_workflows_link] but you can also add your preferred CI/CD solution. + +Out of the box, on each pull request and push, the CI `formats`, `lints`, and `tests` the code. This ensures the code remains consistent and behaves correctly as you add functionality or make changes. The project uses [Very Good Analysis][very_good_analysis_link] for a strict set of analysis options used by our team. Code coverage is enforced using the [Very Good Workflows][very_good_coverage_link]. + +--- + +## Running Tests 🧪 + +To run all unit tests: + +```sh +dart pub global activate coverage 1.2.0 +dart test --coverage=coverage +dart pub global run coverage:format_coverage --lcov --in=coverage --out=coverage/lcov.info +``` + +To view the generated coverage report you can use [lcov](https://github.com/linux-test-project/lcov). + +```sh +# Generate Coverage Report +genhtml coverage/lcov.info -o coverage/ + +# Open Coverage Report +open coverage/index.html +``` + +[dart_install_link]: https://dart.dev/get-dart +[github_actions_link]: https://docs.github.com/en/actions/learn-github-actions +[license_badge]: https://img.shields.io/badge/license-MIT-blue.svg +[license_link]: https://opensource.org/licenses/MIT +[logo_black]: https://raw.githubusercontent.com/VGVentures/very_good_brand/main/styles/README/vgv_logo_black.png#gh-light-mode-only +[logo_white]: https://raw.githubusercontent.com/VGVentures/very_good_brand/main/styles/README/vgv_logo_white.png#gh-dark-mode-only +[mason_link]: https://github.com/felangel/mason +[very_good_analysis_badge]: https://img.shields.io/badge/style-very_good_analysis-B22C89.svg +[very_good_analysis_link]: https://pub.dev/packages/very_good_analysis +[very_good_coverage_link]: https://github.com/marketplace/actions/very-good-coverage +[very_good_ventures_link]: https://verygood.ventures +[very_good_ventures_link_light]: https://verygood.ventures#gh-light-mode-only +[very_good_ventures_link_dark]: https://verygood.ventures#gh-dark-mode-only +[very_good_workflows_link]: https://github.com/VeryGoodOpenSource/very_good_workflows diff --git a/packages/secure_jwt_signer/analysis_options.yaml b/packages/secure_jwt_signer/analysis_options.yaml new file mode 100644 index 000000000..a608bb333 --- /dev/null +++ b/packages/secure_jwt_signer/analysis_options.yaml @@ -0,0 +1,5 @@ +include: package:very_good_analysis/analysis_options.yaml +linter: + rules: + public_member_api_docs: false + constant_identifier_names: false diff --git a/packages/secure_jwt_signer/coverage_badge.svg b/packages/secure_jwt_signer/coverage_badge.svg new file mode 100644 index 000000000..499e98ce2 --- /dev/null +++ b/packages/secure_jwt_signer/coverage_badge.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + coverage + coverage + 100% + 100% + + diff --git a/packages/secure_jwt_signer/lib/secure_jwt_signer.dart b/packages/secure_jwt_signer/lib/secure_jwt_signer.dart new file mode 100644 index 000000000..3df2dd988 --- /dev/null +++ b/packages/secure_jwt_signer/lib/secure_jwt_signer.dart @@ -0,0 +1,4 @@ +/// Sign jwt with keys from TEE/Strongbox or Secure Element +library secure_jwt_signer; + +export 'src/secure_jwt_signer.dart'; diff --git a/packages/secure_jwt_signer/lib/src/secure_jwt_signer.dart b/packages/secure_jwt_signer/lib/src/secure_jwt_signer.dart new file mode 100644 index 000000000..8393b33a1 --- /dev/null +++ b/packages/secure_jwt_signer/lib/src/secure_jwt_signer.dart @@ -0,0 +1,38 @@ +import 'dart:convert'; +import 'dart:typed_data'; +import 'package:crypto/crypto.dart'; +import 'package:secp256r1/secp256r1.dart'; + +/// Sign jwt with keys from TEE/Strongbox or Secure Element +class SecureJwtSigner { + const SecureJwtSigner(); + + Future signJwt({ + required Map header, + required Map payload, + required String privateKey, + }) async { + // Base64 url safe encoding of header and payload without padding + final headerB64 = + base64Encode(utf8.encode(jsonEncode(header))).replaceAll('=', ''); + final payloadB64 = + base64Encode(utf8.encode(jsonEncode(payload))).replaceAll('=', ''); + + // Calculate the message digest with SHA-256 + final message = '$headerB64.$payloadB64'; + final bytes = utf8.encode(message); + final digest = sha256.convert(bytes); + + // Sign the message digest with ECDSA key (P-256) + final signature = await SecureP256.sign( + privateKey, + Uint8List.fromList(digest.bytes), + ); + + // Encode signature with base64 url safe and no padding + final signatureB64 = base64Url.encode(signature).replaceAll('=', ''); + + // Return JWT + return '$headerB64.$payloadB64.$signatureB64'; + } +} diff --git a/packages/secure_jwt_signer/pubspec.yaml b/packages/secure_jwt_signer/pubspec.yaml new file mode 100644 index 000000000..16a7dfa4c --- /dev/null +++ b/packages/secure_jwt_signer/pubspec.yaml @@ -0,0 +1,16 @@ +name: secure_jwt_signer +description: Sign jwt with keys from TEE/Strongbox or Secure Element +version: 0.1.0+1 +publish_to: none + +environment: + sdk: ">=3.1.0 <4.0.0" + +dependencies: + crypto: ^3.0.3 + secp256r1: ^0.1.0-dev.7 + +dev_dependencies: + mocktail: ^1.0.3 + test: ^1.19.2 + very_good_analysis: ^5.0.0+1 diff --git a/packages/secure_jwt_signer/test/src/secure_jwt_signer_test.dart b/packages/secure_jwt_signer/test/src/secure_jwt_signer_test.dart new file mode 100644 index 000000000..1733de055 --- /dev/null +++ b/packages/secure_jwt_signer/test/src/secure_jwt_signer_test.dart @@ -0,0 +1,10 @@ +import 'package:secure_jwt_signer/secure_jwt_signer.dart'; +import 'package:test/test.dart'; + +void main() { + group('SecureJwtSigner', () { + test('can be instantiated', () { + expect(const SecureJwtSigner(), isNotNull); + }); + }); +} diff --git a/packages/secure_storage/test/src/secure_storage_test.dart b/packages/secure_storage/test/src/secure_storage_test.dart index 247ba18d2..5f3f13e6c 100644 --- a/packages/secure_storage/test/src/secure_storage_test.dart +++ b/packages/secure_storage/test/src/secure_storage_test.dart @@ -16,12 +16,16 @@ void main() { setUpAll(() async { mockFlutterSecureStorage = MockFlutterSecureStorage(); - secureStorageProvider = SecureStorageProvider(); + secureStorageProvider = + SecureStorageProvider(storage: mockFlutterSecureStorage); }); group('SecureStorage', () { test('can be instantiated', () { - expect(getSecureStorage, isNotNull); + expect(secureStorageProvider, isNotNull); + }); + test('getSecureStorage returns SecureStorageProvider instance', () { + expect(getSecureStorage, isA()); }); test('get method works correctly', () async { @@ -45,10 +49,9 @@ void main() { final result = await secureStorageProvider.getAllValues(); expect(result, equals(allValue)); verify( - () => mockFlutterSecureStorage.readAll( - iOptions: any(named: 'iOptions'), - ), - ).called(1); + () => + mockFlutterSecureStorage.readAll(iOptions: any(named: 'iOptions')), + ); }); test('set method works correctly', () async { @@ -97,5 +100,47 @@ void main() { await secureStorageProvider.deleteAll(); verify(mockFlutterSecureStorage.deleteAll).called(1); }); + + group('deleteAllExceptsSomeKeys method', () { + test('delete all when list is null', () async { + when(mockFlutterSecureStorage.deleteAll) + .thenAnswer((invocation) async {}); + + await secureStorageProvider.deleteAllExceptsSomeKeys(null); + verify(mockFlutterSecureStorage.deleteAll).called(1); + }); + + test('delete all when list is empty', () async { + when(mockFlutterSecureStorage.deleteAll) + .thenAnswer((invocation) async {}); + + await secureStorageProvider.deleteAllExceptsSomeKeys([]); + verify(mockFlutterSecureStorage.deleteAll).called(1); + }); + + test('delete all except sent in the list', () async { + when( + () => mockFlutterSecureStorage.readAll( + iOptions: any(named: 'iOptions'), + ), + ).thenAnswer((invocation) async { + return { + 'key1': '1', + 'key2': '2', + }; + }); + + when( + () => mockFlutterSecureStorage.delete( + key: 'key1', + iOptions: any(named: 'iOptions'), + ), + ).thenAnswer((_) async {}); + + await secureStorageProvider.deleteAllExceptsSomeKeys(['key2']); + verify(() => mockFlutterSecureStorage.delete(key: 'key1')).called(1); + verifyNever(() => mockFlutterSecureStorage.delete(key: 'key2')); + }); + }); }); } diff --git a/pubspec.lock b/pubspec.lock index f8ff05023..c8fac4ae5 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -214,10 +214,10 @@ packages: dependency: transitive description: name: build_daemon - sha256: "0343061a33da9c5810b2d6cee51945127d8f4c060b7fbdd9d54917f0a3feaaa1" + sha256: "79b2aef6ac2ed00046867ed354c88778c9c0f029df8a20fe10b5436826721ef9" url: "https://pub.dev" source: hosted - version: "4.0.1" + version: "4.0.2" build_resolvers: dependency: transitive description: @@ -230,18 +230,18 @@ packages: dependency: "direct dev" description: name: build_runner - sha256: "3ac61a79bfb6f6cc11f693591063a7f19a7af628dc52f141743edac5c16e8c22" + sha256: "644dc98a0f179b872f612d3eb627924b578897c629788e858157fa5e704ca0c7" url: "https://pub.dev" source: hosted - version: "2.4.9" + version: "2.4.11" build_runner_core: dependency: transitive description: name: build_runner_core - sha256: "4ae8ffe5ac758da294ecf1802f2aff01558d8b1b00616aa7538ea9a8a5d50799" + sha256: e3c79f69a64bdfcd8a776a3c28db4eb6e3fb5356d013ae5eb2e52007706d5dbe url: "https://pub.dev" source: hosted - version: "7.3.0" + version: "7.3.1" built_collection: dependency: transitive description: @@ -294,10 +294,10 @@ packages: dependency: transitive description: name: camera_android - sha256: b350ac087f111467e705b2b76cc1322f7f5bdc122aa83b4b243b0872f390d229 + sha256: "3af7f0b55f184d392d2eec238aaa30552ebeef2915e5e094f5488bf50d6d7ca2" url: "https://pub.dev" source: hosted - version: "0.10.9+2" + version: "0.10.9+3" camera_avfoundation: dependency: transitive description: @@ -537,14 +537,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.3" - device_frame: - dependency: transitive - description: - name: device_frame - sha256: afe76182aec178d171953d9b4a50a43c57c7cf3c77d8b09a48bf30c8fa04dd9d - url: "https://pub.dev" - source: hosted - version: "1.1.0" device_info: dependency: transitive description: @@ -577,14 +569,6 @@ packages: url: "https://pub.dev" source: hosted version: "7.0.0" - device_preview: - dependency: "direct main" - description: - name: device_preview - sha256: "2f097bf31b929e15e6756dbe0ec1bcb63952ab9ed51c25dc5a2c722d2b21fdaf" - url: "https://pub.dev" - source: hosted - version: "1.1.0" devicelocale: dependency: "direct main" description: @@ -736,7 +720,7 @@ packages: source: hosted version: "2.1.2" fake_async: - dependency: transitive + dependency: "direct dev" description: name: fake_async sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" @@ -840,10 +824,10 @@ packages: dependency: "direct main" description: name: flutter_bloc - sha256: f0ecf6e6eb955193ca60af2d5ca39565a86b8a142452c5b24d96fb477428f4d2 + sha256: b594505eac31a0518bdcb4b5b79573b8d9117b193cc80cc12e17d639b10aa27a url: "https://pub.dev" source: hosted - version: "8.1.5" + version: "8.1.6" flutter_cache_manager: dependency: transitive description: @@ -956,30 +940,6 @@ packages: url: "https://pub.dev" source: hosted version: "6.0.0" - flutter_local_notifications: - dependency: "direct main" - description: - name: flutter_local_notifications - sha256: "55b9b229307a10974b26296ff29f2e132256ba4bd74266939118eaefa941cb00" - url: "https://pub.dev" - source: hosted - version: "16.3.3" - flutter_local_notifications_linux: - dependency: transitive - description: - name: flutter_local_notifications_linux - sha256: "33f741ef47b5f63cc7f78fe75eeeac7e19f171ff3c3df054d84c1e38bedb6a03" - url: "https://pub.dev" - source: hosted - version: "4.0.0+1" - flutter_local_notifications_platform_interface: - dependency: transitive - description: - name: flutter_local_notifications_platform_interface - sha256: "340abf67df238f7f0ef58f4a26d2a83e1ab74c77ab03cd2b2d5018ac64db30b7" - url: "https://pub.dev" - source: hosted - version: "7.1.0" flutter_localizations: dependency: "direct main" description: flutter @@ -1029,10 +989,10 @@ packages: dependency: transitive description: name: flutter_plugin_android_lifecycle - sha256: "8cf40eebf5dec866a6d1956ad7b4f7016e6c0cc69847ab946833b7d43743809f" + sha256: c6b0b4c05c458e1c01ad9bcc14041dd7b1f6783d487be4386f793f47a8a4d03e url: "https://pub.dev" source: hosted - version: "2.0.19" + version: "2.0.20" flutter_secure_storage: dependency: transitive description: @@ -1119,10 +1079,10 @@ packages: dependency: transitive description: name: frontend_server_client - sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612" + sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 url: "https://pub.dev" source: hosted - version: "3.2.0" + version: "4.0.0" get_it: dependency: transitive description: @@ -1263,10 +1223,10 @@ packages: dependency: transitive description: name: image_picker_android - sha256: "0f57fee1e8bfadf8cc41818bbcd7f72e53bb768a54d9496355d5e8a5681a19f1" + sha256: "4161e1f843d8480d2e9025ee22411778c3c9eb7e40076dcf2da23d8242b7b51c" url: "https://pub.dev" source: hosted - version: "0.8.12+1" + version: "0.8.12+3" image_picker_for_web: dependency: transitive description: @@ -1324,13 +1284,13 @@ packages: source: hosted version: "2.4.2" intl: - dependency: transitive + dependency: "direct overridden" description: name: intl - sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf url: "https://pub.dev" source: hosted - version: "0.18.1" + version: "0.19.0" io: dependency: transitive description: @@ -1405,26 +1365,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" url: "https://pub.dev" source: hosted - version: "10.0.0" + version: "10.0.4" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.3" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.1" linkify: dependency: transitive description: @@ -1453,10 +1413,10 @@ packages: dependency: transitive description: name: local_auth_android - sha256: e0e5b1ea247c5a0951c13a7ee13dc1beae69750e6a2e1910d1ed6a3cd4d56943 + sha256: "48dfb2d954da8ef6a77adfc93a29998f7729e9308eaa817e91dea4500317b2c8" url: "https://pub.dev" source: hosted - version: "1.0.38" + version: "1.0.39" local_auth_darwin: dependency: transitive description: @@ -1549,10 +1509,10 @@ packages: dependency: transitive description: name: meta - sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.12.0" mime: dependency: "direct main" description: @@ -1589,10 +1549,10 @@ packages: dependency: "direct dev" description: name: mocktail - sha256: c4b5007d91ca4f67256e720cb1b6d704e79a510183a12fa551021f652577dce6 + sha256: "890df3f9688106f25755f26b1c60589a92b3ab91a22b8b224947ad041bf172d8" url: "https://pub.dev" source: hosted - version: "1.0.3" + version: "1.0.4" nested: dependency: transitive description: @@ -1724,10 +1684,10 @@ packages: dependency: transitive description: name: path_provider_android - sha256: a248d8146ee5983446bf03ed5ea8f6533129a12b11f12057ad1b4a67a2b3b41d + sha256: "9c96da072b421e98183f9ea7464898428e764bc0ce5567f27ec8693442e72514" url: "https://pub.dev" source: hosted - version: "2.2.4" + version: "2.2.5" path_provider_foundation: dependency: transitive description: @@ -1772,10 +1732,10 @@ packages: dependency: transitive description: name: permission_handler_android - sha256: "8bb852cd759488893805c3161d0b2b5db55db52f773dbb014420b304055ba2c5" + sha256: b29a799ca03be9f999aa6c39f7de5209482d638e6f857f6b93b0875c618b7e54 url: "https://pub.dev" source: hosted - version: "12.0.6" + version: "12.0.7" permission_handler_apple: dependency: transitive description: @@ -2036,10 +1996,10 @@ packages: dependency: "direct main" description: name: screenshot - sha256: d448f43130f49bc7eead1b267b3ea0291cb2450f037bb0a0ecce7aa4c65e2aee + sha256: "63817697a7835e6ce82add4228e15d233b74d42975c143ad8cfe07009fab866b" url: "https://pub.dev" source: hosted - version: "2.5.0" + version: "3.0.0" scroll_to_index: dependency: transitive description: @@ -2123,10 +2083,10 @@ packages: dependency: transitive description: name: shared_preferences_android - sha256: "1ee8bf911094a1b592de7ab29add6f826a7331fb854273d55918693d5364a1f2" + sha256: "93d0ec9dd902d85f326068e6a899487d1f65ffcd5798721a95330b26c8131577" url: "https://pub.dev" source: hosted - version: "2.2.2" + version: "2.2.3" shared_preferences_foundation: dependency: transitive description: @@ -2344,26 +2304,26 @@ packages: dependency: transitive description: name: test - sha256: a1f7595805820fcc05e5c52e3a231aedd0b72972cb333e8c738a8b1239448b6f + sha256: "7ee446762c2c50b3bd4ea96fe13ffac69919352bd3b4b17bac3f3465edc58073" url: "https://pub.dev" source: hosted - version: "1.24.9" + version: "1.25.2" test_api: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.7.0" test_core: dependency: transitive description: name: test_core - sha256: a757b14fc47507060a162cc2530d9a4a2f92f5100a952c7443b5cad5ef5b106a + sha256: "2bc4b4ecddd75309300d8096f781c0e3280ca1ef85beda558d33fcbedc2eead4" url: "https://pub.dev" source: hosted - version: "0.5.9" + version: "0.6.0" tezart: dependency: "direct main" description: @@ -2449,10 +2409,10 @@ packages: dependency: transitive description: name: url_launcher_android - sha256: "17cd5e205ea615e2c6ea7a77323a11712dffa0720a8a90540db57a01347f9ad9" + sha256: ceb2625f0c24ade6ef6778d1de0b2e44f2db71fded235eb52295247feba8c5cf url: "https://pub.dev" source: hosted - version: "6.3.2" + version: "6.3.3" url_launcher_ios: dependency: transitive description: @@ -2561,10 +2521,10 @@ packages: dependency: transitive description: name: vm_service - sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 + sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" url: "https://pub.dev" source: hosted - version: "13.0.0" + version: "14.2.1" wallet: dependency: transitive description: @@ -2641,10 +2601,10 @@ packages: dependency: "direct main" description: name: webview_flutter_android - sha256: "0d21cfc3bfdd2e30ab2ebeced66512b91134b39e72e97b43db2d47dda1c4e53a" + sha256: f42447ca49523f11d8f70abea55ea211b3cafe172dd7a0e7ac007bb35dd356dc url: "https://pub.dev" source: hosted - version: "3.16.3" + version: "3.16.4" webview_flutter_platform_interface: dependency: transitive description: @@ -2665,10 +2625,10 @@ packages: dependency: transitive description: name: win32 - sha256: "0eaf06e3446824099858367950a813472af675116bf63f008a4c2a75ae13e9cb" + sha256: a79dbe579cb51ecd6d30b17e0cae4e0ea15e2c0e66f69ad4198f22a6789e94f4 url: "https://pub.dev" source: hosted - version: "5.5.0" + version: "5.5.1" win32_registry: dependency: transitive description: @@ -2734,5 +2694,5 @@ packages: source: hosted version: "2.2.1" sdks: - dart: ">=3.3.0 <4.0.0" - flutter: ">=3.19.0" + dart: ">=3.4.0 <4.0.0" + flutter: ">=3.22.0" diff --git a/pubspec.yaml b/pubspec.yaml index 6318675b4..d763c869b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: altme description: AltMe Flutter App version: 2.7.0+463 - +publish_to: "none" # Remove this line if you wish to publish to pub.dev environment: sdk: ">=3.1.0 <4.0.0" @@ -35,7 +35,6 @@ dependencies: ref: main decimal: ^2.3.3 device_info_plus: ^9.0.1 - device_preview: ^1.1.0 devicelocale: ^0.7.0 did_kit: path: packages/did_kit @@ -55,7 +54,6 @@ dependencies: flutter_dotenv: ^5.0.2 flutter_html: ^3.0.0-beta.1 flutter_image_compress: ^2.0.3 - flutter_local_notifications: ^16.3.0 flutter_localizations: sdk: flutter flutter_markdown: ^0.6.9 #flutter_markdown ^0.6.14 requires markdown ^7.0.0 @@ -95,7 +93,7 @@ dependencies: pretty_qr_code: ^3.1.0 qr_flutter: ^4.1.0 random_string: ^2.3.1 - screenshot: ^2.1.0 + screenshot: ^3.0.0 secure_application: ^3.8.0 secure_storage: path: packages/secure_storage @@ -121,7 +119,7 @@ dependencies: dependency_overrides: ffi: 2.1.0 #didkit from path which depends on ffi ^1.0.0 http: ^1.0.0 #web3dart ^2.6.1 reqgetuires http ^0.13.5 - # intl: ^0.18.1 #flutter_localizations from sdk which depends on intl 0.18.0 + intl: ^0.19.0 #force pinned version of intl because polygonID use older version # logger: ^2.0.1 # path_provider: ^2.0.15 #polygonid_flutter_sdk from git which depends on path_provider 2.0.1 # web3dart: ^2.6.1 #polygonid depends on 2.3.5 @@ -129,6 +127,7 @@ dependency_overrides: dev_dependencies: bloc_test: ^9.1.2 build_runner: ^2.4.4 + fake_async: ^1.3.1 flutter_launcher_icons: ^0.13.1 flutter_test: sdk: flutter diff --git a/script.sh b/script.sh index 7983327a4..f8835be7c 100755 --- a/script.sh +++ b/script.sh @@ -1,17 +1,17 @@ function pub { - fvm flutter clean + flutter clean for d in `ls packages`; do ( cd "packages/$d" - fvm flutter clean - fvm flutter pub get - fvm flutter pub upgrade + flutter clean + flutter pub get + flutter pub upgrade ) done - fvm flutter pub get - fvm flutter pub upgrade + flutter pub get + flutter pub upgrade } function buildRunner { @@ -21,10 +21,11 @@ function buildRunner { ( echo "$d" cd "packages/$d" - fvm flutter packages pub run build_runner build --delete-conflicting-outputs + dart pub run build_runner build --delete-conflicting-outputs ) done - fvm flutter packages pub run build_runner build --delete-conflicting-outputs + dart pub run build_runner build --delete-conflicting-outputs + flutter pub get } function podUpdate { @@ -41,17 +42,17 @@ function podUpdate { if [[ "$*" == *-runDev* ]]; then echo "flutter run development" - fvm flutter run --flavor development --target lib/main_development.dart + flutter run --flavor development --target lib/main_development.dart elif [[ "$*" == *-runstage* ]]; then echo "flutter run staging" - fvm flutter run --flavor staging --target lib/main_staging.dart + flutter run --flavor staging --target lib/main_staging.dart elif [[ "$*" == *-run* ]]; then echo "flutter run production" - fvm flutter run --flavor production --target lib/main_production.dart + flutter run --flavor production --target lib/main_production.dart elif [[ "$*" == *-pod* ]]; then @@ -62,7 +63,7 @@ then buildRunner echo "deploy android" echo "Make sure you are in right branch" - fvm flutter build appbundle --flavor "production" --target "lib/main_production.dart" + flutter build appbundle --flavor "production" --target "lib/main_production.dart" # cd android # fastlane deploy echo "app bundle deployed on internal testing track" @@ -74,7 +75,7 @@ then podUpdate echo "deploy ios" echo "Make sure you are in right branch" - fvm flutter build ios --release --flavor "production" --target "lib/main_production.dart" + flutter build ios --release --flavor "production" --target "lib/main_production.dart" cd ios fastlane beta elif [[ "$*" == *-pub* ]]; diff --git a/test/app/shared/alert_message/alert_message_test.dart b/test/app/shared/alert_message/alert_message_test.dart new file mode 100644 index 000000000..78c176835 --- /dev/null +++ b/test/app/shared/alert_message/alert_message_test.dart @@ -0,0 +1,190 @@ +import 'package:altme/app/app.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../../../helpers/helpers.dart'; + +void main() { + testWidgets('AlertMessage shows SnackBar', (WidgetTester tester) async { + await tester.pumpApp( + Scaffold( + body: Builder( + builder: (BuildContext context) { + return ElevatedButton( + onPressed: () { + AlertMessage.showStateMessage( + context: context, + stateMessage: const StateMessage( + messageHandler: null, + stringMessage: 'Test Message', + injectedMessage: null, + showDialog: false, + duration: Duration(seconds: 2), + type: MessageType.info, + ), + ); + }, + child: const Text('Show SnackBar'), + ); + }, + ), + ), + ); + + await tester.tap(find.text('Show SnackBar')); + await tester.pumpAndSettle(); + + expect(find.text('Test Message'), findsOneWidget); + expect(find.byType(SnackBar), findsOneWidget); + expect(find.byType(SnackBarContent), findsOneWidget); + }); + + testWidgets('AlertMessage shows Dialog', (WidgetTester tester) async { + await tester.pumpApp( + Scaffold( + body: Builder( + builder: (BuildContext context) { + return ElevatedButton( + onPressed: () { + AlertMessage.showStateMessage( + context: context, + stateMessage: const StateMessage( + messageHandler: null, + stringMessage: 'Test Message', + injectedMessage: null, + showDialog: true, + type: MessageType.info, + ), + ); + }, + child: const Text('Show Dialog'), + ); + }, + ), + ), + ); + + await tester.tap(find.text('Show Dialog')); + await tester.pumpAndSettle(); + + expect(find.text('Test Message'), findsOneWidget); + expect(find.byType(ErrorDialog), findsOneWidget); + }); + testWidgets('SnackBarContent is displayed correctly', + (WidgetTester tester) async { + await tester.pumpApp( + const Scaffold( + body: SnackBarContent( + message: 'Test Message', + iconPath: 'assets/icon/add.png', + ), + ), + ); + + expect(find.text('Test Message'), findsOneWidget); + expect(find.byType(ElevatedButton), findsOneWidget); + }); + + testWidgets('AlertMessage shows Network error properly', + (WidgetTester tester) async { + await tester.pumpApp( + Scaffold( + body: Builder( + builder: (BuildContext context) { + return ElevatedButton( + onPressed: () { + AlertMessage.showStateMessage( + context: context, + stateMessage: StateMessage( + messageHandler: NetworkException( + data: 'Netwok Test', + ), + injectedMessage: null, + showDialog: false, + duration: const Duration(seconds: 2), + type: MessageType.info, + ), + ); + }, + child: const Text('Show SnackBar'), + ); + }, + ), + ), + ); + + await tester.tap(find.text('Show SnackBar')); + await tester.pumpAndSettle(); + + expect(find.text('Netwok Test'), findsOneWidget); + }); + + testWidgets('AlertMessage shows default message when nothing is provided', + (WidgetTester tester) async { + await tester.pumpApp( + Scaffold( + body: Builder( + builder: (BuildContext context) { + return ElevatedButton( + onPressed: () { + AlertMessage.showStateMessage( + context: context, + stateMessage: const StateMessage( + messageHandler: null, + injectedMessage: null, + showDialog: false, + duration: Duration(seconds: 2), + type: MessageType.info, + ), + ); + }, + child: const Text('Show SnackBar'), + ); + }, + ), + ), + ); + + await tester.tap(find.text('Show SnackBar')); + await tester.pumpAndSettle(); + + expect(find.text('This request is not supported'), findsOneWidget); + }); + testWidgets('AlertMessage shows ResponseMessage correctly', + (WidgetTester tester) async { + await tester.pumpApp( + Scaffold( + body: Builder( + builder: (BuildContext context) { + return ElevatedButton( + onPressed: () { + AlertMessage.showStateMessage( + context: context, + stateMessage: StateMessage( + messageHandler: ResponseMessage( + message: ResponseString + .RESPONSE_STRING_AN_ERROR_OCCURRED_WHILE_CONNECTING_TO_THE_SERVER, + ), + injectedMessage: null, + showDialog: false, + duration: const Duration(seconds: 2), + type: MessageType.info, + ), + ); + }, + child: const Text('Show SnackBar'), + ); + }, + ), + ), + ); + + await tester.tap(find.text('Show SnackBar')); + await tester.pumpAndSettle(); + + expect( + find.text('An error occurred while connecting to the server.'), + findsOneWidget, + ); + }); +} diff --git a/test/app/shared/constants/urls_test.dart b/test/app/shared/constants/urls_test.dart new file mode 100644 index 000000000..2216211dc --- /dev/null +++ b/test/app/shared/constants/urls_test.dart @@ -0,0 +1,11 @@ +import 'package:altme/app/app.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + test('ethPrice returns correct URL', () { + const symbol = 'ETH'; + const expectedUrl = + 'https://min-api.cryptocompare.com/data/price?fsym=$symbol&tsyms=USD'; + expect(Urls.ethPrice(symbol), expectedUrl); + }); +} diff --git a/test/app/shared/date/date_test.dart b/test/app/shared/date/date_test.dart new file mode 100644 index 000000000..cb447be14 --- /dev/null +++ b/test/app/shared/date/date_test.dart @@ -0,0 +1,84 @@ +import 'package:altme/app/app.dart'; +import 'package:altme/l10n/l10n.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; + +class MockAppLocalizations extends Mock implements AppLocalizations { + @override + final localeName = 'en_US'; +} + +void main() { + group('UiDate', () { + group('displayRegionalDate', () { + test('should return formatted date for valid input', () { + const dateString = '2024-05-20'; + final localizations = MockAppLocalizations(); + + final result = UiDate.displayRegionalDate(localizations, dateString); + expect(result, '5/20/2024'); + }); + + test('should return empty string for empty input', () { + final localizations = MockAppLocalizations(); + + final result = UiDate.displayRegionalDate(localizations, ''); + + expect(result, ''); + }); + + test('should return empty string for invalid input', () { + final localizations = MockAppLocalizations(); + + final result = UiDate.displayRegionalDate(localizations, 'invalid'); + + expect(result, ''); + }); + }); + + test('formatStringDate returns formatted date', () { + const dateTime = '2022-05-10T10:00:00Z'; + expect(UiDate.formatStringDate(dateTime), '2022-05-10'); + }); + + test('formatDate returns formatted date', () { + final dateTime = DateTime(2022, 5, 10); + expect(UiDate.formatDate(dateTime), '2022-05-10'); + }); + + test( + 'formatDateForCredentialCard returns formatted date for credential card', + () { + const timestamp = '1643738400'; + expect(UiDate.formatDateForCredentialCard(timestamp), '2022-02-01'); + }); + + group('isTimestampString', () { + test('returns true for valid timestamp string', () { + const timestamp = '1643738400'; + expect(UiDate.isTimestampString(timestamp), true); + }); + + test('returns false for invalid timestamp string', () { + const invalidTimestamp = 'abc'; + expect(UiDate.isTimestampString(invalidTimestamp), false); + }); + }); + + group('normalFormat', () { + test('returns formatted date and time', () { + const dateTime = '2022-05-10T10:00:00Z'; + expect(UiDate.normalFormat(dateTime), '10-05-2022 15:45'); + }); + test('returns null if format is empty', () { + const dateTime = ''; + expect(UiDate.normalFormat(dateTime), null); + }); + + test('returns null if format is incorrect', () { + const dateTime = 'asdf'; + expect(UiDate.normalFormat(dateTime), null); + }); + }); + }); +} diff --git a/test/app/shared/enum/credential_category_test.dart b/test/app/shared/enum/credential_category_test.dart new file mode 100644 index 000000000..58e99e8d7 --- /dev/null +++ b/test/app/shared/enum/credential_category_test.dart @@ -0,0 +1,307 @@ +import 'package:altme/app/app.dart'; +import 'package:altme/l10n/l10n.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; + +class MockAppLocalizations extends Mock implements AppLocalizations { + @override + final my = 'My'; + @override + final get = 'Get'; + @override + final advantagesCards = 'A1'; + @override + final advantagesCredentialHomeSubtitle = 'A2'; + @override + final advantagesDiscoverCards = 'A3'; + @override + final advantagesCredentialDiscoverSubtitle = 'A4'; + @override + final identityCards = 'A1'; + @override + final identityCredentialHomeSubtitle = 'A2'; + @override + final identityDiscoverCards = 'A3'; + @override + final identityCredentialDiscoverSubtitle = 'A4'; + + @override + final myProfessionalCards = 'A1'; + @override + final myProfessionalrCardsSubtitle = 'A2'; + @override + final myProfessionalCredentialDiscoverSubtitle = 'A3'; + + @override + final contactInfoCredentials = 'A1'; + @override + final contactInfoCredentialHomeSubtitle = 'A2'; + @override + final contactInfoDiscoverCredentials = 'A3'; + @override + final contactInfoCredentialDiscoverSubtitle = 'A4'; + + @override + final blockchainAccounts = 'A1'; + @override + final blockchainAccountsCredentialHomeSubtitle = 'A2'; + @override + final blockchainCardsDiscoverTitle = 'A3'; + @override + final blockchainCardsDiscoverSubtitle = 'A4'; + + @override + final educationCredentials = 'A1'; + @override + final educationCredentialHomeSubtitle = 'A2'; + @override + final educationDiscoverCredentials = 'A3'; + @override + final educationCredentialsDiscoverSubtitle = 'A4'; + + @override + final otherCards = 'A1'; + @override + final otherCredentialHomeSubtitle = 'A2'; + @override + final otherCredentialDiscoverSubtitle = 'A4'; + + @override + final financeCredentialsHomeTitle = 'A1'; + @override + final financeCredentialsHomeSubtitle = 'A2'; + @override + final financeCredentialsDiscoverTitle = 'A3'; + @override + final financeCredentialsDiscoverSubtitle = 'A4'; + + @override + final hummanityProofCredentialsHomeTitle = 'A1'; + @override + final hummanityProofCredentialsHomeSubtitle = 'A2'; + @override + final hummanityProofCredentialsDiscoverTitle = 'A3'; + @override + final hummanityProofCredentialsDiscoverSubtitle = 'A4'; + + @override + final socialMediaCredentialsHomeTitle = 'A1'; + @override + final socialMediaCredentialsHomeSubtitle = 'A2'; + @override + final socialMediaCredentialsDiscoverTitle = 'A3'; + @override + final socialMediaCredentialsDiscoverSubtitle = 'A4'; + + @override + final walletIntegrityCredentialsHomeTitle = 'A1'; + @override + final walletIntegrityCredentialsHomeSubtitle = 'A2'; + @override + final walletIntegrityCredentialsDiscoverTitle = 'A3'; + @override + final walletIntegrityCredentialsDiscoverSubtitle = 'A4'; + + @override + final polygonCredentialsHomeTitle = 'A1'; + @override + final polygonCredentialsHomeSubtitle = 'A2'; + @override + final polygonCredentialsDiscoverTitle = 'A3'; + @override + final polygonCredentialsDiscoverSubtitle = 'A4'; + + @override + final pendingCredentialsHomeTitle = 'A1'; + @override + final pendingCredentialsHomeSubtitle = 'A2'; +} + +void main() { + test('getCredentialCategorySorted should return categories in sorted order', + () { + final sortedCategories = [ + CredentialCategory.identityCards, + CredentialCategory.professionalCards, + CredentialCategory.contactInfoCredentials, + CredentialCategory.educationCards, + CredentialCategory.financeCards, + CredentialCategory.humanityProofCards, + CredentialCategory.socialMediaCards, + CredentialCategory.walletIntegrity, + CredentialCategory.polygonidCards, + CredentialCategory.blockchainAccountsCards, + CredentialCategory.othersCards, + CredentialCategory.pendingCards, + CredentialCategory.advantagesCards, + ]; + + expect(getCredentialCategorySorted, sortedCategories); + }); + + group('CredentialCategoryExtension', () { + test('order should return correct order for each category', () { + expect(CredentialCategory.advantagesCards.order, 8); + expect(CredentialCategory.identityCards.order, 140); + expect(CredentialCategory.professionalCards.order, 135); + expect(CredentialCategory.contactInfoCredentials.order, 130); + expect(CredentialCategory.blockchainAccountsCards.order, 70); + expect(CredentialCategory.educationCards.order, 120); + expect(CredentialCategory.othersCards.order, 10); + expect(CredentialCategory.financeCards.order, 110); + expect(CredentialCategory.humanityProofCards.order, 100); + expect(CredentialCategory.socialMediaCards.order, 90); + expect(CredentialCategory.walletIntegrity.order, 80); + expect(CredentialCategory.polygonidCards.order, 75); + expect(CredentialCategory.pendingCards.order, 9); + }); + + test('showInHomeIfListEmpty should return correct value for each category', + () { + expect(CredentialCategory.identityCards.showInHomeIfListEmpty, true); + expect(CredentialCategory.professionalCards.showInHomeIfListEmpty, false); + expect(CredentialCategory.advantagesCards.showInHomeIfListEmpty, false); + expect( + CredentialCategory.contactInfoCredentials.showInHomeIfListEmpty, + false, + ); + expect( + CredentialCategory.blockchainAccountsCards.showInHomeIfListEmpty, + false, + ); + expect(CredentialCategory.educationCards.showInHomeIfListEmpty, false); + expect(CredentialCategory.othersCards.showInHomeIfListEmpty, false); + expect(CredentialCategory.financeCards.showInHomeIfListEmpty, false); + expect( + CredentialCategory.humanityProofCards.showInHomeIfListEmpty, + false, + ); + expect(CredentialCategory.socialMediaCards.showInHomeIfListEmpty, false); + expect(CredentialCategory.walletIntegrity.showInHomeIfListEmpty, false); + expect(CredentialCategory.polygonidCards.showInHomeIfListEmpty, false); + expect(CredentialCategory.pendingCards.showInHomeIfListEmpty, false); + }); + + test('config should return correct configuration for each category', () { + final localizations = MockAppLocalizations(); + + expect( + CredentialCategory.advantagesCards.config(localizations), + const CredentialCategoryConfig( + homeTitle: 'My a1', + homeSubTitle: 'A2', + discoverTitle: 'A3', + discoverSubTitle: 'A4', + ), + ); + expect( + CredentialCategory.identityCards.config(localizations), + const CredentialCategoryConfig( + homeTitle: 'My a1', + homeSubTitle: 'A2', + discoverTitle: 'A3', + discoverSubTitle: 'A4', + ), + ); + expect( + CredentialCategory.professionalCards.config(localizations), + const CredentialCategoryConfig( + homeTitle: 'My a1', + homeSubTitle: 'A2', + discoverTitle: 'A3', + discoverSubTitle: 'A3', + ), + ); + expect( + CredentialCategory.contactInfoCredentials.config(localizations), + const CredentialCategoryConfig( + homeTitle: 'My a1', + homeSubTitle: 'A2', + discoverTitle: 'A3', + discoverSubTitle: 'A4', + ), + ); + expect( + CredentialCategory.blockchainAccountsCards.config(localizations), + const CredentialCategoryConfig( + homeTitle: 'My a1', + homeSubTitle: 'A2', + discoverTitle: 'A3', + discoverSubTitle: 'A4', + ), + ); + expect( + CredentialCategory.educationCards.config(localizations), + const CredentialCategoryConfig( + homeTitle: 'My a1', + homeSubTitle: 'A2', + discoverTitle: 'A3', + discoverSubTitle: 'A4', + ), + ); + expect( + CredentialCategory.othersCards.config(localizations), + const CredentialCategoryConfig( + homeTitle: 'My a1', + homeSubTitle: 'A2', + discoverTitle: 'Get a1', + discoverSubTitle: 'A4', + ), + ); + expect( + CredentialCategory.financeCards.config(localizations), + const CredentialCategoryConfig( + homeTitle: 'A1', + homeSubTitle: 'A2', + discoverTitle: 'A3', + discoverSubTitle: 'A4', + ), + ); + expect( + CredentialCategory.humanityProofCards.config(localizations), + const CredentialCategoryConfig( + homeTitle: 'A1', + homeSubTitle: 'A2', + discoverTitle: 'A3', + discoverSubTitle: 'A4', + ), + ); + expect( + CredentialCategory.socialMediaCards.config(localizations), + const CredentialCategoryConfig( + homeTitle: 'A1', + homeSubTitle: 'A2', + discoverTitle: 'A3', + discoverSubTitle: 'A4', + ), + ); + expect( + CredentialCategory.walletIntegrity.config(localizations), + const CredentialCategoryConfig( + homeTitle: 'A1', + homeSubTitle: 'A2', + discoverTitle: 'A3', + discoverSubTitle: 'A4', + ), + ); + expect( + CredentialCategory.polygonidCards.config(localizations), + const CredentialCategoryConfig( + homeTitle: 'A1', + homeSubTitle: 'A2', + discoverTitle: 'A3', + discoverSubTitle: 'A4', + ), + ); + expect( + CredentialCategory.pendingCards.config(localizations), + const CredentialCategoryConfig( + homeTitle: 'A1', + homeSubTitle: 'A2', + discoverTitle: '', + discoverSubTitle: '', + ), + ); + }); + }); +} diff --git a/test/app/shared/enum/credential_subject_type/credential_subject_type_extension_test.dart b/test/app/shared/enum/credential_subject_type/credential_subject_type_extension_test.dart new file mode 100644 index 000000000..b01c5937b --- /dev/null +++ b/test/app/shared/enum/credential_subject_type/credential_subject_type_extension_test.dart @@ -0,0 +1,1220 @@ +import 'package:altme/app/app.dart'; +import 'package:altme/dashboard/dashboard.dart'; +import 'package:flutter/material.dart'; +import 'package:oidc4vc/oidc4vc.dart'; +import 'package:test/test.dart'; + +void main() { + group('CredentialSubjectType Extension Tests', () { + test('CredentialSubjectType backgroundColor returns correct value', () { + final credentialModel = CredentialModel( + id: '', + credentialPreview: Credential.dummy(), + data: const {}, + image: '', + shareLink: '', + display: const Display(backgroundColor: '#FFFFFF'), + ); + final credentialModel2 = CredentialModel( + id: '', + credentialPreview: Credential.dummy(), + data: const {}, + image: '', + shareLink: '', + ); + + for (final value in CredentialSubjectType.values) { + expect( + value.backgroundColor(credentialModel), + equals(const Color(0xFFFFFFFF)), + ); + expect( + value.backgroundColor(credentialModel2), + equals(value.defaultBackgroundColor), + ); + } + }); + + test('CredentialSubjectType defaultBackgroundColor returns correct value', + () { + expect( + CredentialSubjectType.defiCompliance.defaultBackgroundColor, + equals(const Color.fromARGB(255, 62, 15, 163)), + ); + + expect( + CredentialSubjectType.identityPass.defaultBackgroundColor, + equals(const Color(0xffCAFFBF)), + ); + + expect( + CredentialSubjectType + .professionalExperienceAssessment.defaultBackgroundColor, + equals(const Color(0xFFFFADAD)), + ); + + expect( + CredentialSubjectType + .professionalSkillAssessment.defaultBackgroundColor, + equals(const Color(0xffCAFFBF)), + ); + + expect( + CredentialSubjectType.residentCard.defaultBackgroundColor, + equals(Colors.white), + ); + + expect( + CredentialSubjectType.selfIssued.defaultBackgroundColor, + equals(const Color(0xffEFF0F6)), + ); + + expect( + CredentialSubjectType.defaultCredential.defaultBackgroundColor, + equals(Colors.white), + ); + + expect( + CredentialSubjectType.professionalStudentCard.defaultBackgroundColor, + equals(const Color(0xffCAFFBF)), + ); + + expect( + CredentialSubjectType.kycAgeCredential.defaultBackgroundColor, + equals(const Color(0xff8247E5)), + ); + + expect( + CredentialSubjectType.kycCountryOfResidence.defaultBackgroundColor, + equals(const Color(0xff8247E5)), + ); + + expect( + CredentialSubjectType.walletCredential.defaultBackgroundColor, + equals(Colors.white), + ); + + expect( + CredentialSubjectType.livenessCard.defaultBackgroundColor, + equals(Colors.white), + ); + + expect( + CredentialSubjectType.nationality.defaultBackgroundColor, + equals(Colors.white), + ); + + expect( + CredentialSubjectType.tezotopiaMembership.defaultBackgroundColor, + equals(Colors.white), + ); + + expect( + CredentialSubjectType.chainbornMembership.defaultBackgroundColor, + equals(Colors.white), + ); + + expect( + CredentialSubjectType.twitterCard.defaultBackgroundColor, + equals(Colors.white), + ); + + expect( + CredentialSubjectType.gender.defaultBackgroundColor, + equals(Colors.white), + ); + + expect( + CredentialSubjectType.tezosAssociatedWallet.defaultBackgroundColor, + equals(Colors.white), + ); + + expect( + CredentialSubjectType.verifiableIdCard.defaultBackgroundColor, + equals(Colors.white), + ); + + expect( + CredentialSubjectType.linkedInCard.defaultBackgroundColor, + equals(Colors.white), + ); + + expect( + CredentialSubjectType.over13.defaultBackgroundColor, + equals(Colors.white), + ); + + expect( + CredentialSubjectType.over15.defaultBackgroundColor, + equals(Colors.white), + ); + + expect( + CredentialSubjectType.over18.defaultBackgroundColor, + equals(Colors.white), + ); + + expect( + CredentialSubjectType.over21.defaultBackgroundColor, + equals(Colors.white), + ); + + expect( + CredentialSubjectType.over50.defaultBackgroundColor, + equals(Colors.white), + ); + + expect( + CredentialSubjectType.over65.defaultBackgroundColor, + equals(Colors.white), + ); + + expect( + CredentialSubjectType.passportFootprint.defaultBackgroundColor, + equals(Colors.white), + ); + + expect( + CredentialSubjectType.certificateOfEmployment.defaultBackgroundColor, + equals(Colors.white), + ); + + expect( + CredentialSubjectType.emailPass.defaultBackgroundColor, + equals(Colors.white), + ); + + expect( + CredentialSubjectType.ageRange.defaultBackgroundColor, + equals(Colors.white), + ); + + expect( + CredentialSubjectType.phonePass.defaultBackgroundColor, + equals(Colors.white), + ); + + expect( + CredentialSubjectType.learningAchievement.defaultBackgroundColor, + equals(Colors.white), + ); + + expect( + CredentialSubjectType.studentCard.defaultBackgroundColor, + equals(Colors.white), + ); + + expect( + CredentialSubjectType.voucher.defaultBackgroundColor, + equals(Colors.white), + ); + + expect( + CredentialSubjectType.tezVoucher.defaultBackgroundColor, + equals(Colors.white), + ); + + expect( + CredentialSubjectType.diplomaCard.defaultBackgroundColor, + equals(Colors.white), + ); + + expect( + CredentialSubjectType.aragoPass.defaultBackgroundColor, + equals(Colors.white), + ); + + expect( + CredentialSubjectType.aragoIdentityCard.defaultBackgroundColor, + equals(Colors.white), + ); + + expect( + CredentialSubjectType.aragoLearningAchievement.defaultBackgroundColor, + equals(Colors.white), + ); + + expect( + CredentialSubjectType.aragoEmailPass.defaultBackgroundColor, + equals(Colors.white), + ); + + expect( + CredentialSubjectType.aragoOver18.defaultBackgroundColor, + equals(Colors.white), + ); + + expect( + CredentialSubjectType.ethereumAssociatedWallet.defaultBackgroundColor, + equals(Colors.white), + ); + + expect( + CredentialSubjectType.fantomAssociatedWallet.defaultBackgroundColor, + equals(Colors.white), + ); + + expect( + CredentialSubjectType.polygonAssociatedWallet.defaultBackgroundColor, + equals(Colors.white), + ); + + expect( + CredentialSubjectType.binanceAssociatedWallet.defaultBackgroundColor, + equals(Colors.white), + ); + + expect( + CredentialSubjectType.ethereumPooAddress.defaultBackgroundColor, + equals(Colors.white), + ); + + expect( + CredentialSubjectType.fantomPooAddress.defaultBackgroundColor, + equals(Colors.white), + ); + + expect( + CredentialSubjectType.polygonPooAddress.defaultBackgroundColor, + equals(Colors.white), + ); + + expect( + CredentialSubjectType.binancePooAddress.defaultBackgroundColor, + equals(Colors.white), + ); + + expect( + CredentialSubjectType.tezosPooAddress.defaultBackgroundColor, + equals(Colors.white), + ); + + expect( + CredentialSubjectType.pcdsAgentCertificate.defaultBackgroundColor, + equals(Colors.white), + ); + + expect( + CredentialSubjectType.euDiplomaCard.defaultBackgroundColor, + equals(Colors.white), + ); + + expect( + CredentialSubjectType.euVerifiableId.defaultBackgroundColor, + equals(Colors.white), + ); + + expect( + CredentialSubjectType.proofOfTwitterStats.defaultBackgroundColor, + equals(Colors.white), + ); + + expect( + CredentialSubjectType.civicPassCredential.defaultBackgroundColor, + equals(Colors.white), + ); + + expect( + CredentialSubjectType.employeeCredential.defaultBackgroundColor, + equals(Colors.white), + ); + + expect( + CredentialSubjectType.legalPersonalCredential.defaultBackgroundColor, + equals(Colors.white), + ); + + expect( + CredentialSubjectType.identityCredential.defaultBackgroundColor, + equals(Colors.white), + ); + + expect( + CredentialSubjectType.eudiPid.defaultBackgroundColor, + equals(Colors.white), + ); + + expect( + CredentialSubjectType.pid.defaultBackgroundColor, + equals(Colors.white), + ); + }); + + test('CredentialSubjectType name returns correct value', () { + expect( + CredentialSubjectType.defiCompliance.name, + equals('DefiCompliance'), + ); + expect(CredentialSubjectType.livenessCard.name, equals('Liveness')); + expect( + CredentialSubjectType.tezotopiaMembership.name, + equals('MembershipCard_1'), + ); + expect( + CredentialSubjectType.chainbornMembership.name, + equals('Chainborn_MembershipCard'), + ); + expect( + CredentialSubjectType.twitterCard.name, + equals('TwitterAccountProof'), + ); + expect(CredentialSubjectType.ageRange.name, equals('AgeRange')); + expect(CredentialSubjectType.nationality.name, equals('Nationality')); + expect(CredentialSubjectType.gender.name, equals('Gender')); + expect( + CredentialSubjectType.walletCredential.name, + equals('WalletCredential'), + ); + expect( + CredentialSubjectType.tezosAssociatedWallet.name, + equals('TezosAssociatedAddress'), + ); + expect( + CredentialSubjectType.ethereumAssociatedWallet.name, + equals('EthereumAssociatedAddress'), + ); + expect( + CredentialSubjectType.fantomAssociatedWallet.name, + equals('FantomAssociatedAddress'), + ); + expect( + CredentialSubjectType.polygonAssociatedWallet.name, + equals('PolygonAssociatedAddress'), + ); + expect( + CredentialSubjectType.binanceAssociatedWallet.name, + equals('BinanceAssociatedAddress'), + ); + expect( + CredentialSubjectType.ethereumPooAddress.name, + equals('EthereumPooAddress'), + ); + expect( + CredentialSubjectType.fantomPooAddress.name, + equals('FantomPooAddress'), + ); + expect( + CredentialSubjectType.polygonPooAddress.name, + equals('PolygonPooAddress'), + ); + expect( + CredentialSubjectType.binancePooAddress.name, + equals('BinancePooAddress'), + ); + expect( + CredentialSubjectType.tezosPooAddress.name, + equals('TezosPooAddress'), + ); + expect( + CredentialSubjectType.certificateOfEmployment.name, + equals('CertificateOfEmployment'), + ); + expect(CredentialSubjectType.emailPass.name, equals('EmailPass')); + expect(CredentialSubjectType.identityPass.name, equals('IdentityPass')); + expect( + CredentialSubjectType.verifiableIdCard.name, + equals('VerifiableId'), + ); + expect(CredentialSubjectType.linkedInCard.name, equals('LinkedinCard')); + expect( + CredentialSubjectType.learningAchievement.name, + equals('LearningAchievement'), + ); + expect(CredentialSubjectType.over13.name, equals('Over13')); + expect(CredentialSubjectType.over15.name, equals('Over15')); + expect(CredentialSubjectType.over18.name, equals('Over18')); + expect(CredentialSubjectType.over21.name, equals('Over21')); + expect(CredentialSubjectType.over50.name, equals('Over50')); + expect(CredentialSubjectType.over65.name, equals('Over65')); + expect( + CredentialSubjectType.passportFootprint.name, + equals('PassportNumber'), + ); + expect(CredentialSubjectType.phonePass.name, equals('PhoneProof')); + expect( + CredentialSubjectType.professionalExperienceAssessment.name, + equals('ProfessionalExperienceAssessment'), + ); + expect( + CredentialSubjectType.professionalSkillAssessment.name, + equals('ProfessionalSkillAssessment'), + ); + expect( + CredentialSubjectType.professionalStudentCard.name, + equals('ProfessionalStudentCard'), + ); + expect(CredentialSubjectType.residentCard.name, equals('ResidentCard')); + expect( + CredentialSubjectType.employeeCredential.name, + equals('EmployeeCredential'), + ); + expect( + CredentialSubjectType.legalPersonalCredential.name, + equals('LegalPersonCredential'), + ); + expect(CredentialSubjectType.selfIssued.name, equals('SelfIssued')); + expect(CredentialSubjectType.studentCard.name, equals('StudentCard')); + expect(CredentialSubjectType.voucher.name, equals('Voucher')); + expect(CredentialSubjectType.tezVoucher.name, equals('TezVoucher_1')); + expect( + CredentialSubjectType.diplomaCard.name, + equals('VerifiableDiploma'), + ); + expect(CredentialSubjectType.aragoPass.name, equals('AragoPass')); + expect( + CredentialSubjectType.aragoEmailPass.name, + equals('AragoEmailPass'), + ); + expect( + CredentialSubjectType.aragoIdentityCard.name, + equals('AragoIdCard'), + ); + expect( + CredentialSubjectType.aragoLearningAchievement.name, + equals('AragoLearningAchievement'), + ); + expect(CredentialSubjectType.aragoOver18.name, equals('AragoOver18')); + expect( + CredentialSubjectType.pcdsAgentCertificate.name, + equals('PCDSAgentCertificate'), + ); + expect( + CredentialSubjectType.euDiplomaCard.name, + equals( + 'https://api.preprod.ebsi.eu/trusted-schemas-registry/v1/schemas/0xbf78fc08a7a9f28f5479f58dea269d3657f54f13ca37d380cd4e92237fb691dd', + ), + ); + expect( + CredentialSubjectType.euVerifiableId.name, + equals( + 'https://api-conformance.ebsi.eu/trusted-schemas-registry/v2/schemas/z22ZAMdQtNLwi51T2vdZXGGZaYyjrsuP1yzWyXZirCAHv', + ), + ); + expect( + CredentialSubjectType.kycAgeCredential.name, + equals('KYCAgeCredential'), + ); + expect( + CredentialSubjectType.kycCountryOfResidence.name, + equals('KYCCountryOfResidenceCredential'), + ); + expect( + CredentialSubjectType.proofOfTwitterStats.name, + equals('ProofOfTwitterStats'), + ); + expect( + CredentialSubjectType.civicPassCredential.name, + equals('CivicPassCredential'), + ); + expect( + CredentialSubjectType.identityCredential.name, + equals('IdentityCredential'), + ); + expect(CredentialSubjectType.eudiPid.name, equals('EudiPid')); + expect(CredentialSubjectType.pid.name, equals('Pid')); + expect(CredentialSubjectType.defaultCredential.name, equals('')); + }); + + test('CredentialSubjectType checkForAIKYC returns correct value', () { + for (final value in CredentialSubjectType.values) { + if (value == CredentialSubjectType.over18 || + value == CredentialSubjectType.over13 || + value == CredentialSubjectType.over15 || + value == CredentialSubjectType.over21 || + value == CredentialSubjectType.over50 || + value == CredentialSubjectType.over65 || + value == CredentialSubjectType.ageRange || + value == CredentialSubjectType.defiCompliance || + value == CredentialSubjectType.livenessCard) { + expect(value.checkForAIKYC, isTrue); + } else { + expect(value.checkForAIKYC, isFalse); + } + } + }); + + test('CredentialSubjectType getKycVcType returns correct value', () { + for (final value in CredentialSubjectType.values) { + if (value == CredentialSubjectType.over18) { + expect(value.getKycVcType, equals(KycVcType.over18)); + } else if (value == CredentialSubjectType.over13) { + expect(value.getKycVcType, equals(KycVcType.over13)); + } else if (value == CredentialSubjectType.over15) { + expect(value.getKycVcType, equals(KycVcType.over15)); + } else if (value == CredentialSubjectType.over21) { + expect(value.getKycVcType, equals(KycVcType.over21)); + } else if (value == CredentialSubjectType.over50) { + expect(value.getKycVcType, equals(KycVcType.over50)); + } else if (value == CredentialSubjectType.over65) { + expect(value.getKycVcType, equals(KycVcType.over65)); + } else if (value == CredentialSubjectType.ageRange) { + expect(value.getKycVcType, equals(KycVcType.ageRange)); + } else if (value == CredentialSubjectType.defiCompliance) { + expect(value.getKycVcType, equals(KycVcType.defiCompliance)); + } else { + expect(value.getKycVcType, equals(KycVcType.verifiableId)); + } + } + }); + + test('CredentialSubjectType aiValidationUrl returns correct value', () { + for (final value in CredentialSubjectType.values) { + if (value == CredentialSubjectType.over13) { + expect(value.aiValidationUrl, equals(Urls.over13AIValidationUrl)); + } else if (value == CredentialSubjectType.over15) { + expect(value.aiValidationUrl, equals(Urls.over15AIValidationUrl)); + } else if (value == CredentialSubjectType.over18) { + expect(value.aiValidationUrl, equals(Urls.over18AIValidationUrl)); + } else if (value == CredentialSubjectType.over21) { + expect(value.aiValidationUrl, equals(Urls.over21AIValidationUrl)); + } else if (value == CredentialSubjectType.over50) { + expect(value.aiValidationUrl, equals(Urls.over50AIValidationUrl)); + } else if (value == CredentialSubjectType.over65) { + expect(value.aiValidationUrl, equals(Urls.over65AIValidationUrl)); + } else if (value == CredentialSubjectType.ageRange) { + expect(value.aiValidationUrl, equals(Urls.ageRangeAIValidationUrl)); + } else { + expect( + () => CredentialSubjectType.defaultCredential.aiValidationUrl, + throwsA( + predicate( + (e) => + e is ResponseMessage && + e.data['error'] == 'invalid_request', + ), + ), + ); + } + } + }); + + test('CredentialSubjectType byPassDeepLink returns correct value', () { + for (final value in CredentialSubjectType.values) { + if (value == CredentialSubjectType.tezotopiaMembership || + value == CredentialSubjectType.chainbornMembership || + value == CredentialSubjectType.twitterCard || + value == CredentialSubjectType.over13 || + value == CredentialSubjectType.over15 || + value == CredentialSubjectType.over18 || + value == CredentialSubjectType.over21 || + value == CredentialSubjectType.over50 || + value == CredentialSubjectType.over65 || + value == CredentialSubjectType.verifiableIdCard || + value == CredentialSubjectType.ageRange || + value == CredentialSubjectType.nationality || + value == CredentialSubjectType.gender || + value == CredentialSubjectType.passportFootprint || + value == CredentialSubjectType.linkedInCard) { + expect(value.byPassDeepLink, isTrue); + } else { + expect(value.byPassDeepLink, isFalse); + } + } + }); + + test('CredentialSubjectType isEbsiCard returns correct value', () { + for (final value in CredentialSubjectType.values) { + if (value == CredentialSubjectType.euDiplomaCard || + value == CredentialSubjectType.euVerifiableId) { + expect(value.isEbsiCard, isTrue); + } else { + expect(value.isEbsiCard, isFalse); + } + } + }); + + test('CredentialSubjectType isBlockchainAccount returns correct value', () { + for (final value in CredentialSubjectType.values) { + if (value == CredentialSubjectType.tezosAssociatedWallet || + value == CredentialSubjectType.ethereumAssociatedWallet || + value == CredentialSubjectType.binanceAssociatedWallet || + value == CredentialSubjectType.fantomAssociatedWallet || + value == CredentialSubjectType.polygonAssociatedWallet) { + expect(value.isBlockchainAccount, isTrue); + } else { + expect(value.isBlockchainAccount, isFalse); + } + } + }); + + test('CredentialSubjectType blockchainWidget returns correct value', () { + for (final value in CredentialSubjectType.values) { + if (value == CredentialSubjectType.tezosAssociatedWallet) { + expect(value.blockchainWidget, isA()); + } else if (value == CredentialSubjectType.ethereumAssociatedWallet) { + expect( + value.blockchainWidget, + isA(), + ); + } else if (value == CredentialSubjectType.polygonAssociatedWallet) { + expect(value.blockchainWidget, isA()); + } else if (value == CredentialSubjectType.binanceAssociatedWallet) { + expect(value.blockchainWidget, isA()); + } else if (value == CredentialSubjectType.fantomAssociatedWallet) { + expect(value.blockchainWidget, isA()); + } else { + expect(value.blockchainWidget, isNull); + } + } + }); + + test('CredentialSubjectType title returns correct value', () { + expect(CredentialSubjectType.defiCompliance.title, 'Defi Compliance'); + expect(CredentialSubjectType.livenessCard.title, 'Liveness'); + expect( + CredentialSubjectType.tezotopiaMembership.title, + 'Membership Card', + ); + expect(CredentialSubjectType.chainbornMembership.title, 'Chainborn'); + expect(CredentialSubjectType.twitterCard.title, 'Twitter Account Proof'); + expect(CredentialSubjectType.ageRange.title, 'Age Range'); + expect(CredentialSubjectType.nationality.title, 'Nationality'); + expect(CredentialSubjectType.gender.title, 'Gender'); + expect(CredentialSubjectType.walletCredential.title, 'Wallet Credential'); + expect( + CredentialSubjectType.tezosAssociatedWallet.title, + 'Tezos Associated Address', + ); + expect( + CredentialSubjectType.ethereumAssociatedWallet.title, + 'Ethereum Associated Address', + ); + expect( + CredentialSubjectType.fantomAssociatedWallet.title, + 'Fantom Associated Address', + ); + expect( + CredentialSubjectType.polygonAssociatedWallet.title, + 'Polygon Associated Address', + ); + expect( + CredentialSubjectType.binanceAssociatedWallet.title, + 'BNB Chain Associated Address', + ); + expect( + CredentialSubjectType.ethereumPooAddress.title, + 'Ethereum Poo Address', + ); + expect( + CredentialSubjectType.fantomPooAddress.title, + 'Fantom Poo Address', + ); + expect( + CredentialSubjectType.polygonPooAddress.title, + 'Polygon Poo Address', + ); + expect( + CredentialSubjectType.binancePooAddress.title, + 'BNB Chain Poo Address', + ); + expect(CredentialSubjectType.tezosPooAddress.title, 'Tezos Poo Address'); + expect( + CredentialSubjectType.certificateOfEmployment.title, + 'Certificate of Employment', + ); + expect(CredentialSubjectType.emailPass.title, 'Email Pass'); + expect(CredentialSubjectType.identityPass.title, 'Identity Pass'); + expect(CredentialSubjectType.verifiableIdCard.title, 'VerifiableId'); + expect(CredentialSubjectType.linkedInCard.title, 'Linkedin Card'); + expect( + CredentialSubjectType.learningAchievement.title, + 'Learning Achievement', + ); + expect(CredentialSubjectType.over13.title, 'Over13'); + expect(CredentialSubjectType.over15.title, 'Over15'); + expect(CredentialSubjectType.over18.title, 'Over18'); + expect(CredentialSubjectType.over21.title, 'Over18'); + expect(CredentialSubjectType.over50.title, 'Over18'); + expect(CredentialSubjectType.over65.title, 'Over18'); + expect(CredentialSubjectType.passportFootprint.title, 'Passport Number'); + expect(CredentialSubjectType.phonePass.title, 'Phone Proof'); + expect( + CredentialSubjectType.professionalExperienceAssessment.title, + 'Professional Experience Assessment', + ); + expect( + CredentialSubjectType.professionalSkillAssessment.title, + 'Professional Skill Assessment', + ); + expect( + CredentialSubjectType.professionalStudentCard.title, + 'Professional Student Card', + ); + expect(CredentialSubjectType.residentCard.title, 'Resident Card'); + expect(CredentialSubjectType.selfIssued.title, 'Self Issued'); + expect(CredentialSubjectType.studentCard.title, 'Student Card'); + expect(CredentialSubjectType.voucher.title, 'Voucher'); + expect(CredentialSubjectType.tezVoucher.title, 'TezVoucher'); + expect(CredentialSubjectType.diplomaCard.title, 'Verifiable Diploma'); + expect(CredentialSubjectType.aragoPass.title, 'Arago Pass'); + expect(CredentialSubjectType.aragoEmailPass.title, 'Arago Email Pass'); + expect(CredentialSubjectType.aragoIdentityCard.title, 'Arago Id Card'); + expect( + CredentialSubjectType.aragoLearningAchievement.title, + 'Arago Learning Achievement', + ); + expect(CredentialSubjectType.aragoOver18.title, 'Arago Over18'); + expect( + CredentialSubjectType.pcdsAgentCertificate.title, + 'PCDS Agent Certificate', + ); + expect(CredentialSubjectType.euDiplomaCard.title, 'EU Diploma'); + expect(CredentialSubjectType.euVerifiableId.title, 'EU VerifiableID'); + expect( + CredentialSubjectType.kycAgeCredential.title, + 'KYC Age Credential', + ); + expect( + CredentialSubjectType.kycCountryOfResidence.title, + 'KYC Country of Residence', + ); + expect( + CredentialSubjectType.proofOfTwitterStats.title, + 'Proof Of Twitter Stats', + ); + expect( + CredentialSubjectType.civicPassCredential.title, + 'Civic Pass Credential', + ); + expect( + CredentialSubjectType.employeeCredential.title, + 'Employee Credential', + ); + expect( + CredentialSubjectType.legalPersonalCredential.title, + 'Legal Person Credential', + ); + expect( + CredentialSubjectType.identityCredential.title, + 'Identity Credential', + ); + expect(CredentialSubjectType.eudiPid.title, 'EudiPid'); + expect(CredentialSubjectType.pid.title, 'Pid'); + expect(CredentialSubjectType.defaultCredential.title, ''); + }); + + test('CredentialSubjectType supportSingleOnly returns correct value', () { + expect(CredentialSubjectType.defiCompliance.supportSingleOnly, true); + expect(CredentialSubjectType.livenessCard.supportSingleOnly, true); + expect(CredentialSubjectType.tezotopiaMembership.supportSingleOnly, true); + expect(CredentialSubjectType.chainbornMembership.supportSingleOnly, true); + expect(CredentialSubjectType.ageRange.supportSingleOnly, true); + expect(CredentialSubjectType.nationality.supportSingleOnly, true); + expect(CredentialSubjectType.gender.supportSingleOnly, true); + expect(CredentialSubjectType.identityPass.supportSingleOnly, true); + expect(CredentialSubjectType.verifiableIdCard.supportSingleOnly, true); + expect(CredentialSubjectType.over13.supportSingleOnly, true); + expect(CredentialSubjectType.over15.supportSingleOnly, true); + expect(CredentialSubjectType.over18.supportSingleOnly, true); + expect(CredentialSubjectType.over21.supportSingleOnly, true); + expect(CredentialSubjectType.over50.supportSingleOnly, true); + expect(CredentialSubjectType.over65.supportSingleOnly, true); + expect(CredentialSubjectType.passportFootprint.supportSingleOnly, true); + expect(CredentialSubjectType.residentCard.supportSingleOnly, true); + expect(CredentialSubjectType.voucher.supportSingleOnly, true); + expect(CredentialSubjectType.tezVoucher.supportSingleOnly, true); + expect(CredentialSubjectType.diplomaCard.supportSingleOnly, true); + expect(CredentialSubjectType.twitterCard.supportSingleOnly, true); + expect( + CredentialSubjectType.tezosAssociatedWallet.supportSingleOnly, + true, + ); + expect( + CredentialSubjectType.ethereumAssociatedWallet.supportSingleOnly, + true, + ); + expect( + CredentialSubjectType.fantomAssociatedWallet.supportSingleOnly, + true, + ); + expect( + CredentialSubjectType.polygonAssociatedWallet.supportSingleOnly, + true, + ); + expect( + CredentialSubjectType.binanceAssociatedWallet.supportSingleOnly, + true, + ); + + expect(CredentialSubjectType.walletCredential.supportSingleOnly, false); + expect(CredentialSubjectType.tezosPooAddress.supportSingleOnly, false); + expect(CredentialSubjectType.ethereumPooAddress.supportSingleOnly, false); + expect(CredentialSubjectType.fantomPooAddress.supportSingleOnly, false); + expect(CredentialSubjectType.polygonPooAddress.supportSingleOnly, false); + expect(CredentialSubjectType.binancePooAddress.supportSingleOnly, false); + expect( + CredentialSubjectType.certificateOfEmployment.supportSingleOnly, + false, + ); + expect(CredentialSubjectType.defaultCredential.supportSingleOnly, false); + expect(CredentialSubjectType.emailPass.supportSingleOnly, false); + expect(CredentialSubjectType.linkedInCard.supportSingleOnly, false); + expect( + CredentialSubjectType.learningAchievement.supportSingleOnly, + false, + ); + expect(CredentialSubjectType.phonePass.supportSingleOnly, false); + expect( + CredentialSubjectType + .professionalExperienceAssessment.supportSingleOnly, + false, + ); + expect( + CredentialSubjectType.professionalSkillAssessment.supportSingleOnly, + false, + ); + expect( + CredentialSubjectType.professionalStudentCard.supportSingleOnly, + false, + ); + expect(CredentialSubjectType.selfIssued.supportSingleOnly, false); + expect(CredentialSubjectType.studentCard.supportSingleOnly, false); + expect(CredentialSubjectType.aragoPass.supportSingleOnly, false); + expect(CredentialSubjectType.aragoEmailPass.supportSingleOnly, false); + expect(CredentialSubjectType.aragoIdentityCard.supportSingleOnly, false); + expect( + CredentialSubjectType.aragoLearningAchievement.supportSingleOnly, + false, + ); + expect(CredentialSubjectType.aragoOver18.supportSingleOnly, false); + expect( + CredentialSubjectType.pcdsAgentCertificate.supportSingleOnly, + false, + ); + expect(CredentialSubjectType.euDiplomaCard.supportSingleOnly, false); + expect(CredentialSubjectType.euVerifiableId.supportSingleOnly, false); + expect(CredentialSubjectType.kycAgeCredential.supportSingleOnly, false); + expect( + CredentialSubjectType.kycCountryOfResidence.supportSingleOnly, + false, + ); + expect( + CredentialSubjectType.proofOfTwitterStats.supportSingleOnly, + false, + ); + expect( + CredentialSubjectType.civicPassCredential.supportSingleOnly, + false, + ); + expect(CredentialSubjectType.employeeCredential.supportSingleOnly, false); + expect( + CredentialSubjectType.legalPersonalCredential.supportSingleOnly, + false, + ); + expect(CredentialSubjectType.identityCredential.supportSingleOnly, false); + expect(CredentialSubjectType.eudiPid.supportSingleOnly, false); + expect(CredentialSubjectType.pid.supportSingleOnly, false); + }); + + test('CredentialSubjectType getVCFormatType returns correct value', () { + expect( + CredentialSubjectType.ethereumAssociatedWallet.getVCFormatType, + VCFormatType.values, + ); + expect( + CredentialSubjectType.fantomAssociatedWallet.getVCFormatType, + VCFormatType.values, + ); + expect( + CredentialSubjectType.polygonAssociatedWallet.getVCFormatType, + VCFormatType.values, + ); + expect( + CredentialSubjectType.binanceAssociatedWallet.getVCFormatType, + VCFormatType.values, + ); + expect( + CredentialSubjectType.tezosAssociatedWallet.getVCFormatType, + VCFormatType.values, + ); + + expect( + CredentialSubjectType.over13.getVCFormatType, + [VCFormatType.ldpVc], + ); + expect( + CredentialSubjectType.over15.getVCFormatType, + [VCFormatType.ldpVc], + ); + expect( + CredentialSubjectType.over21.getVCFormatType, + [VCFormatType.ldpVc], + ); + expect( + CredentialSubjectType.over50.getVCFormatType, + [VCFormatType.ldpVc], + ); + expect( + CredentialSubjectType.over65.getVCFormatType, + [VCFormatType.ldpVc], + ); + expect( + CredentialSubjectType.gender.getVCFormatType, + [VCFormatType.ldpVc], + ); + expect( + CredentialSubjectType.ageRange.getVCFormatType, + [VCFormatType.ldpVc], + ); + expect( + CredentialSubjectType.defiCompliance.getVCFormatType, + [VCFormatType.ldpVc], + ); + expect( + CredentialSubjectType.tezotopiaMembership.getVCFormatType, + [VCFormatType.ldpVc], + ); + expect( + CredentialSubjectType.chainbornMembership.getVCFormatType, + [VCFormatType.ldpVc], + ); + + expect(CredentialSubjectType.verifiableIdCard.getVCFormatType, [ + VCFormatType.ldpVc, + VCFormatType.jwtVcJson, + VCFormatType.vcSdJWT, + VCFormatType.jwtVc, + ]); + + expect( + CredentialSubjectType.identityCredential.getVCFormatType, + [VCFormatType.vcSdJWT], + ); + expect( + CredentialSubjectType.eudiPid.getVCFormatType, + [VCFormatType.vcSdJWT], + ); + expect(CredentialSubjectType.pid.getVCFormatType, [VCFormatType.vcSdJWT]); + + expect(CredentialSubjectType.over18.getVCFormatType, [ + VCFormatType.ldpVc, + VCFormatType.jwtVcJson, + ]); + + expect( + CredentialSubjectType.phonePass.getVCFormatType, + [VCFormatType.ldpVc, VCFormatType.jwtVcJson], + ); + expect( + CredentialSubjectType.livenessCard.getVCFormatType, + [VCFormatType.ldpVc, VCFormatType.jwtVcJson], + ); + expect( + CredentialSubjectType.emailPass.getVCFormatType, + [VCFormatType.ldpVc, VCFormatType.jwtVcJson], + ); + + expect( + CredentialSubjectType.nationality.getVCFormatType, + [VCFormatType.jwtVc], + ); + expect( + CredentialSubjectType.identityPass.getVCFormatType, + [VCFormatType.jwtVc], + ); + expect( + CredentialSubjectType.passportFootprint.getVCFormatType, + [VCFormatType.jwtVc], + ); + expect( + CredentialSubjectType.residentCard.getVCFormatType, + [VCFormatType.jwtVc], + ); + expect( + CredentialSubjectType.voucher.getVCFormatType, + [VCFormatType.jwtVc], + ); + expect( + CredentialSubjectType.tezVoucher.getVCFormatType, + [VCFormatType.jwtVc], + ); + expect( + CredentialSubjectType.diplomaCard.getVCFormatType, + [VCFormatType.jwtVc], + ); + expect( + CredentialSubjectType.twitterCard.getVCFormatType, + [VCFormatType.jwtVc], + ); + expect( + CredentialSubjectType.walletCredential.getVCFormatType, + [VCFormatType.jwtVc], + ); + expect( + CredentialSubjectType.tezosPooAddress.getVCFormatType, + [VCFormatType.jwtVc], + ); + expect( + CredentialSubjectType.ethereumPooAddress.getVCFormatType, + [VCFormatType.jwtVc], + ); + expect( + CredentialSubjectType.fantomPooAddress.getVCFormatType, + [VCFormatType.jwtVc], + ); + expect( + CredentialSubjectType.polygonPooAddress.getVCFormatType, + [VCFormatType.jwtVc], + ); + expect( + CredentialSubjectType.binancePooAddress.getVCFormatType, + [VCFormatType.jwtVc], + ); + expect( + CredentialSubjectType.certificateOfEmployment.getVCFormatType, + [VCFormatType.jwtVc], + ); + expect( + CredentialSubjectType.defaultCredential.getVCFormatType, + [VCFormatType.jwtVc], + ); + expect( + CredentialSubjectType.linkedInCard.getVCFormatType, + [VCFormatType.jwtVc], + ); + expect( + CredentialSubjectType.learningAchievement.getVCFormatType, + [VCFormatType.jwtVc], + ); + expect( + CredentialSubjectType.professionalExperienceAssessment.getVCFormatType, + [VCFormatType.jwtVc], + ); + expect( + CredentialSubjectType.professionalSkillAssessment.getVCFormatType, + [VCFormatType.jwtVc], + ); + expect( + CredentialSubjectType.professionalStudentCard.getVCFormatType, + [VCFormatType.jwtVc], + ); + expect( + CredentialSubjectType.selfIssued.getVCFormatType, + [VCFormatType.jwtVc], + ); + expect( + CredentialSubjectType.studentCard.getVCFormatType, + [VCFormatType.jwtVc], + ); + expect( + CredentialSubjectType.aragoPass.getVCFormatType, + [VCFormatType.jwtVc], + ); + expect( + CredentialSubjectType.aragoEmailPass.getVCFormatType, + [VCFormatType.jwtVc], + ); + expect( + CredentialSubjectType.aragoIdentityCard.getVCFormatType, + [VCFormatType.jwtVc], + ); + expect( + CredentialSubjectType.aragoLearningAchievement.getVCFormatType, + [VCFormatType.jwtVc], + ); + expect( + CredentialSubjectType.aragoOver18.getVCFormatType, + [VCFormatType.jwtVc], + ); + expect( + CredentialSubjectType.pcdsAgentCertificate.getVCFormatType, + [VCFormatType.jwtVc], + ); + expect( + CredentialSubjectType.euDiplomaCard.getVCFormatType, + [VCFormatType.jwtVc], + ); + expect( + CredentialSubjectType.euVerifiableId.getVCFormatType, + [VCFormatType.jwtVc], + ); + expect( + CredentialSubjectType.kycAgeCredential.getVCFormatType, + [VCFormatType.jwtVc], + ); + expect( + CredentialSubjectType.kycCountryOfResidence.getVCFormatType, + [VCFormatType.jwtVc], + ); + expect( + CredentialSubjectType.proofOfTwitterStats.getVCFormatType, + [VCFormatType.jwtVc], + ); + expect( + CredentialSubjectType.civicPassCredential.getVCFormatType, + [VCFormatType.jwtVc], + ); + expect( + CredentialSubjectType.employeeCredential.getVCFormatType, + [VCFormatType.jwtVc], + ); + expect( + CredentialSubjectType.legalPersonalCredential.getVCFormatType, + [VCFormatType.jwtVc], + ); + }); + + test('CredentialSubjectType order returns correct value', () { + expect(CredentialSubjectType.defiCompliance.order, 0); + expect(CredentialSubjectType.livenessCard.order, 75); + expect(CredentialSubjectType.tezotopiaMembership.order, 79); + expect(CredentialSubjectType.chainbornMembership.order, 72); + expect(CredentialSubjectType.ageRange.order, 94); + expect(CredentialSubjectType.nationality.order, 97.3); + expect(CredentialSubjectType.gender.order, 93); + expect(CredentialSubjectType.walletCredential.order, 0); + expect(CredentialSubjectType.tezosAssociatedWallet.order, 68); + expect(CredentialSubjectType.ethereumAssociatedWallet.order, 69); + expect(CredentialSubjectType.fantomAssociatedWallet.order, 67); + expect(CredentialSubjectType.polygonAssociatedWallet.order, 71); + expect(CredentialSubjectType.binanceAssociatedWallet.order, 70); + expect(CredentialSubjectType.tezosPooAddress.order, 0); + expect(CredentialSubjectType.ethereumPooAddress.order, 0); + expect(CredentialSubjectType.fantomPooAddress.order, 0); + expect(CredentialSubjectType.polygonPooAddress.order, 0); + expect(CredentialSubjectType.binancePooAddress.order, 0); + expect(CredentialSubjectType.certificateOfEmployment.order, 85); + expect(CredentialSubjectType.defaultCredential.order, 100); + expect(CredentialSubjectType.emailPass.order, 99); + expect(CredentialSubjectType.identityPass.order, 90); + expect(CredentialSubjectType.verifiableIdCard.order, 97.5); + expect(CredentialSubjectType.linkedInCard.order, 86); + expect(CredentialSubjectType.learningAchievement.order, 0); + expect(CredentialSubjectType.over13.order, 97.3); + expect(CredentialSubjectType.over15.order, 97.2); + expect(CredentialSubjectType.over18.order, 97.1); + expect(CredentialSubjectType.over21.order, 97); + expect(CredentialSubjectType.over50.order, 96); + expect(CredentialSubjectType.over65.order, 95); + expect(CredentialSubjectType.passportFootprint.order, 91); + expect(CredentialSubjectType.phonePass.order, 98); + expect(CredentialSubjectType.professionalExperienceAssessment.order, 0); + expect(CredentialSubjectType.professionalSkillAssessment.order, 0); + expect(CredentialSubjectType.professionalStudentCard.order, 87); + expect(CredentialSubjectType.residentCard.order, 0); + expect(CredentialSubjectType.selfIssued.order, 0); + expect(CredentialSubjectType.studentCard.order, 88); + expect(CredentialSubjectType.voucher.order, 81); + expect(CredentialSubjectType.tezVoucher.order, 80); + expect(CredentialSubjectType.diplomaCard.order, 89); + expect(CredentialSubjectType.aragoPass.order, 81); + expect(CredentialSubjectType.aragoEmailPass.order, 0); + expect(CredentialSubjectType.aragoIdentityCard.order, 0); + expect(CredentialSubjectType.aragoLearningAchievement.order, 0); + expect(CredentialSubjectType.aragoOver18.order, 0); + expect(CredentialSubjectType.pcdsAgentCertificate.order, 82); + expect(CredentialSubjectType.twitterCard.order, 83); + expect(CredentialSubjectType.euDiplomaCard.order, 67); + expect(CredentialSubjectType.euVerifiableId.order, 92); + expect(CredentialSubjectType.kycAgeCredential.order, 0); + expect(CredentialSubjectType.kycCountryOfResidence.order, 0); + expect(CredentialSubjectType.proofOfTwitterStats.order, 0); + expect(CredentialSubjectType.civicPassCredential.order, 0); + expect(CredentialSubjectType.employeeCredential.order, 0); + expect(CredentialSubjectType.legalPersonalCredential.order, 0); + expect(CredentialSubjectType.identityCredential.order, 0); + expect(CredentialSubjectType.eudiPid.order, 0); + expect(CredentialSubjectType.pid.order, 0); + }); + }); +} diff --git a/test/app/shared/enum/polygon_id_network_test.dart b/test/app/shared/enum/polygon_id_network_test.dart new file mode 100644 index 000000000..e69762883 --- /dev/null +++ b/test/app/shared/enum/polygon_id_network_test.dart @@ -0,0 +1,24 @@ +import 'package:altme/app/app.dart'; +import 'package:test/test.dart'; + +void main() { + group('PolygonIdNetwork Extension Tests', () { + test('name should return correct network name', () { + expect(PolygonIdNetwork.PolygonMainnet.name, 'Polygon Main'); + expect(PolygonIdNetwork.PolygonMumbai.name, 'Polygon Mumbai'); + }); + + test('tester should return correct tester string', () { + expect(PolygonIdNetwork.PolygonMainnet.tester, 'polygon:main'); + expect(PolygonIdNetwork.PolygonMumbai.tester, 'polygon:mumbai'); + }); + + test('oppositeNetwork should return correct opposite network', () { + expect( + PolygonIdNetwork.PolygonMainnet.oppositeNetwork, + 'mumbai(testnet)', + ); + expect(PolygonIdNetwork.PolygonMumbai.oppositeNetwork, 'mainnet'); + }); + }); +} diff --git a/test/app/shared/enum/status/credential_status_extension_test.dart b/test/app/shared/enum/status/credential_status_extension_test.dart new file mode 100644 index 000000000..a7527a7e7 --- /dev/null +++ b/test/app/shared/enum/status/credential_status_extension_test.dart @@ -0,0 +1,21 @@ +import 'package:altme/app/app.dart'; +import 'package:flutter/material.dart'; +import 'package:test/test.dart'; + +void main() { + group('CredentialStatusExtension', () { + test('icon returns correct icon for each status', () { + expect(CredentialStatus.active.icon, Icons.check_circle); + expect(CredentialStatus.invalidStatus.icon, Icons.circle_outlined); + expect(CredentialStatus.expired.icon, Icons.circle_outlined); + expect(CredentialStatus.pending.icon, Icons.circle_outlined); + expect(CredentialStatus.unknown.icon, Icons.circle_outlined); + expect(CredentialStatus.invalidSignature.icon, Icons.circle_outlined); + expect( + CredentialStatus.statusListInvalidSignature.icon, + Icons.circle_outlined, + ); + expect(CredentialStatus.noStatus.icon, Icons.circle_outlined); + }); + }); +} diff --git a/test/app/shared/enum/status/mnemonic_status.dart b/test/app/shared/enum/status/mnemonic_status.dart new file mode 100644 index 000000000..47d3ae4a6 --- /dev/null +++ b/test/app/shared/enum/status/mnemonic_status.dart @@ -0,0 +1,30 @@ +import 'dart:ui'; + +enum MnemonicStatus { + unselected, + selected, + wrongSelection, +} + +extension MnemonicStatusX on MnemonicStatus { + bool get showOrder { + switch (this) { + case MnemonicStatus.unselected: + case MnemonicStatus.wrongSelection: + return false; + case MnemonicStatus.selected: + return true; + } + } + + Color get color { + switch (this) { + case MnemonicStatus.unselected: + return const Color(0xff86809D); + case MnemonicStatus.wrongSelection: + return const Color(0xffFF0045); + case MnemonicStatus.selected: + return const Color(0xff6600FF); + } + } +} diff --git a/test/app/shared/enum/type/blockchain_type_test.dart b/test/app/shared/enum/type/blockchain_type_test.dart new file mode 100644 index 000000000..d7ac7856c --- /dev/null +++ b/test/app/shared/enum/type/blockchain_type_test.dart @@ -0,0 +1,241 @@ +import 'dart:convert'; + +import 'package:altme/app/app.dart'; +import 'package:credential_manifest/credential_manifest.dart'; +import 'package:key_generator/key_generator.dart'; +import 'package:test/test.dart'; + +void main() { + group('BlockchainType Extension Tests', () { + test('BlockchainType icon returns correct value', () { + expect(BlockchainType.tezos.icon, equals(IconStrings.tezos)); + expect(BlockchainType.ethereum.icon, equals(IconStrings.ethereum)); + expect(BlockchainType.fantom.icon, equals(IconStrings.fantom)); + expect(BlockchainType.polygon.icon, equals(IconStrings.polygon)); + expect(BlockchainType.binance.icon, equals(IconStrings.binance)); + }); + + test('BlockchainType accountType returns correct value', () { + expect(BlockchainType.tezos.accountType, equals(AccountType.tezos)); + expect(BlockchainType.ethereum.accountType, equals(AccountType.ethereum)); + expect(BlockchainType.fantom.accountType, equals(AccountType.fantom)); + expect(BlockchainType.polygon.accountType, equals(AccountType.polygon)); + expect(BlockchainType.binance.accountType, equals(AccountType.binance)); + }); + + test('BlockchainType symbol returns correct value', () { + expect(BlockchainType.tezos.symbol, equals('XTZ')); + expect(BlockchainType.ethereum.symbol, equals('ETH')); + expect(BlockchainType.fantom.symbol, equals('FTM')); + expect(BlockchainType.polygon.symbol, equals('MATIC')); + expect(BlockchainType.binance.symbol, equals('BNB')); + }); + + test('BlockchainType chain returns correct value', () { + expect(() => BlockchainType.tezos.chain, throwsA(isA())); + expect( + BlockchainType.ethereum.chain, + equals('${Parameters.NAMESPACE}:1'), + ); + expect( + BlockchainType.fantom.chain, + equals('${Parameters.NAMESPACE}:250'), + ); + expect( + BlockchainType.polygon.chain, + equals('${Parameters.NAMESPACE}:137'), + ); + expect( + BlockchainType.binance.chain, + equals('${Parameters.NAMESPACE}:56'), + ); + }); + + test('BlockchainType chainId returns correct value', () { + expect( + () => BlockchainType.tezos.chainId, + throwsA(isA()), + ); + expect(BlockchainType.ethereum.chainId, equals(1)); + expect(BlockchainType.fantom.chainId, equals(250)); + expect(BlockchainType.polygon.chainId, equals(137)); + expect(BlockchainType.binance.chainId, equals(56)); + }); + + test('BlockchainType derivePathIndexKey returns correct value', () { + expect( + BlockchainType.tezos.derivePathIndexKey, + equals(SecureStorageKeys.tezosDerivePathIndex), + ); + expect( + BlockchainType.ethereum.derivePathIndexKey, + equals(SecureStorageKeys.ethereumDerivePathIndex), + ); + expect( + BlockchainType.fantom.derivePathIndexKey, + equals(SecureStorageKeys.fantomDerivePathIndex), + ); + expect( + BlockchainType.polygon.derivePathIndexKey, + equals(SecureStorageKeys.polygonDerivePathIndex), + ); + expect( + BlockchainType.binance.derivePathIndexKey, + equals(SecureStorageKeys.binanceDerivePathIndex), + ); + }); + + test('BlockchainType credentialManifest returns correct value', () { + expect( + jsonEncode(BlockchainType.tezos.credentialManifest), + equals( + jsonEncode( + CredentialManifest.fromJson( + ConstantsJson.tezosAssociatedAddressCredentialManifestJson, + ), + ), + ), + ); + expect( + jsonEncode(BlockchainType.ethereum.credentialManifest), + equals( + jsonEncode( + CredentialManifest.fromJson( + ConstantsJson.ethereumAssociatedAddressCredentialManifestJson, + ), + ), + ), + ); + expect( + jsonEncode(BlockchainType.fantom.credentialManifest), + equals( + jsonEncode( + CredentialManifest.fromJson( + ConstantsJson.fantomAssociatedAddressCredentialManifestJson, + ), + ), + ), + ); + expect( + jsonEncode(BlockchainType.polygon.credentialManifest), + equals( + jsonEncode( + CredentialManifest.fromJson( + ConstantsJson.polygonAssociatedAddressCredentialManifestJson, + ), + ), + ), + ); + expect( + jsonEncode(BlockchainType.binance.credentialManifest), + equals( + jsonEncode( + CredentialManifest.fromJson( + ConstantsJson.binanceAssociatedAddressCredentialManifestJson, + ), + ), + ), + ); + }); + + test('BlockchainType filter returns correct value', () { + expect( + jsonEncode(BlockchainType.tezos.filter.toJson()), + equals( + jsonEncode( + Filter(type: 'String', pattern: 'TezosAssociatedAddress').toJson(), + ), + ), + ); + expect( + jsonEncode(BlockchainType.ethereum.filter.toJson()), + equals( + jsonEncode( + Filter(type: 'String', pattern: 'EthereumAssociatedAddress') + .toJson(), + ), + ), + ); + expect( + jsonEncode(BlockchainType.fantom.filter.toJson()), + equals( + jsonEncode( + Filter(type: 'String', pattern: 'FantomAssociatedAddress').toJson(), + ), + ), + ); + expect( + jsonEncode(BlockchainType.polygon.filter.toJson()), + equals( + jsonEncode( + Filter(type: 'String', pattern: 'PolygonAssociatedAddress') + .toJson(), + ), + ), + ); + expect( + jsonEncode(BlockchainType.binance.filter.toJson()), + equals( + jsonEncode( + Filter(type: 'String', pattern: 'BinanceAssociatedAddress') + .toJson(), + ), + ), + ); + }); + + test('BlockchainType connectionBridge returns correct value', () { + expect( + BlockchainType.tezos.connectionBridge, + equals(ConnectionBridgeType.beacon), + ); + expect( + BlockchainType.ethereum.connectionBridge, + equals(ConnectionBridgeType.walletconnect), + ); + expect( + BlockchainType.fantom.connectionBridge, + equals(ConnectionBridgeType.walletconnect), + ); + expect( + BlockchainType.polygon.connectionBridge, + equals(ConnectionBridgeType.walletconnect), + ); + expect( + BlockchainType.binance.connectionBridge, + equals(ConnectionBridgeType.walletconnect), + ); + }); + + test('BlockchainType networks returns correct value', () { + expect( + BlockchainType.tezos.networks, + equals([TezosNetwork.mainNet(), TezosNetwork.ghostnet()]), + ); + expect( + BlockchainType.ethereum.networks, + equals([EthereumNetwork.mainNet(), EthereumNetwork.testNet()]), + ); + expect( + BlockchainType.fantom.networks, + equals([FantomNetwork.mainNet(), FantomNetwork.testNet()]), + ); + expect( + BlockchainType.polygon.networks, + equals([PolygonNetwork.mainNet(), PolygonNetwork.testNet()]), + ); + expect( + BlockchainType.binance.networks, + equals([BinanceNetwork.mainNet(), BinanceNetwork.testNet()]), + ); + }); + + test('BlockchainType isDisabled returns correct value', () { + expect(BlockchainType.tezos.isDisabled, isFalse); + expect(BlockchainType.ethereum.isDisabled, isFalse); + expect(BlockchainType.fantom.isDisabled, isFalse); + expect(BlockchainType.polygon.isDisabled, isFalse); + expect(BlockchainType.binance.isDisabled, isFalse); + }); + }); +} diff --git a/test/app/shared/enum/type/kyc_vc_type_test.dart b/test/app/shared/enum/type/kyc_vc_type_test.dart new file mode 100644 index 000000000..53083c5bb --- /dev/null +++ b/test/app/shared/enum/type/kyc_vc_type_test.dart @@ -0,0 +1,18 @@ +import 'package:altme/app/app.dart'; +import 'package:test/test.dart'; + +void main() { + group('KycVcType Extension Tests', () { + test('KycVcType value returns correct value', () { + expect(KycVcType.verifiableId.value, equals('VerifiableId')); + expect(KycVcType.over13.value, equals('Over13')); + expect(KycVcType.over15.value, equals('Over15')); + expect(KycVcType.over18.value, equals('Over18')); + expect(KycVcType.over21.value, equals('Over21')); + expect(KycVcType.over50.value, equals('Over50')); + expect(KycVcType.over65.value, equals('Over65')); + expect(KycVcType.ageRange.value, equals('AgeRange')); + expect(KycVcType.defiCompliance.value, equals('DefiCompliance')); + }); + }); +} diff --git a/test/app/shared/enum/type/language_type_test.dart b/test/app/shared/enum/type/language_type_test.dart new file mode 100644 index 000000000..3a85ac106 --- /dev/null +++ b/test/app/shared/enum/type/language_type_test.dart @@ -0,0 +1,37 @@ +import 'package:altme/app/shared/enum/type/language_type.dart'; +import 'package:altme/l10n/l10n.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:test/test.dart'; + +class MockAppLocalizations extends Mock implements AppLocalizations { + @override + String get phoneLanguage => 'Phone'; + + @override + String get catalan => 'Catalan'; + + @override + String get english => 'English'; + + @override + String get spanish => 'Spanish'; + + @override + String get french => 'French'; +} + +void main() { + group('LanguageType Extension Tests', () { + test('LanguageType getTitle returns correct value', () { + final l10n = MockAppLocalizations(); + expect( + LanguageType.phone.getTitle(l10n: l10n, name: ''), + equals('Phone'), + ); + expect(LanguageType.ca.getTitle(l10n: l10n, name: ''), equals('Catalan')); + expect(LanguageType.en.getTitle(l10n: l10n, name: ''), equals('English')); + expect(LanguageType.es.getTitle(l10n: l10n, name: ''), equals('Spanish')); + expect(LanguageType.fr.getTitle(l10n: l10n, name: ''), equals('French')); + }); + }); +} diff --git a/test/app/shared/enum/type/oidc4vc_type_test.dart b/test/app/shared/enum/type/oidc4vc_type_test.dart new file mode 100644 index 000000000..0cde1154c --- /dev/null +++ b/test/app/shared/enum/type/oidc4vc_type_test.dart @@ -0,0 +1,15 @@ +import 'package:altme/app/app.dart'; +import 'package:test/test.dart'; + +void main() { + group('OIDC4VCType Extension Tests', () { + test('OIDC4VCType isEnabled returns correct value', () { + expect(OIDC4VCType.DEFAULT.isEnabled, equals(true)); + expect(OIDC4VCType.GAIAX.isEnabled, equals(true)); + expect(OIDC4VCType.GREENCYPHER.isEnabled, equals(true)); + expect(OIDC4VCType.EBSIV3.isEnabled, equals(true)); + expect(OIDC4VCType.JWTVC.isEnabled, equals(false)); + expect(OIDC4VCType.HAIP.isEnabled, equals(true)); + }); + }); +} diff --git a/test/app/shared/enum/type/profile/did_key_type_test.dart b/test/app/shared/enum/type/profile/did_key_type_test.dart new file mode 100644 index 000000000..314a25fd1 --- /dev/null +++ b/test/app/shared/enum/type/profile/did_key_type_test.dart @@ -0,0 +1,24 @@ +import 'package:altme/app/app.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('DidKeyType Extension', () { + test('Formatted String', () { + expect(DidKeyType.edDSA.formattedString, 'did:key edDSA'); + expect(DidKeyType.secp256k1.formattedString, 'did:key secp256k1'); + expect(DidKeyType.p256.formattedString, 'did:key P-256'); + expect(DidKeyType.ebsiv3.formattedString, 'did:key EBSI-V3'); + expect(DidKeyType.jwkP256.formattedString, 'did:jwk P-256'); + expect(DidKeyType.jwtClientAttestation.formattedString, ''); + }); + + test('Support Crypto Credential', () { + expect(DidKeyType.edDSA.supportCryptoCredential, true); + expect(DidKeyType.secp256k1.supportCryptoCredential, true); + expect(DidKeyType.p256.supportCryptoCredential, true); + expect(DidKeyType.ebsiv3.supportCryptoCredential, false); + expect(DidKeyType.jwkP256.supportCryptoCredential, true); + expect(DidKeyType.jwtClientAttestation.supportCryptoCredential, true); + }); + }); +} diff --git a/test/app/shared/enum/type/profile/profile_type_test.dart b/test/app/shared/enum/type/profile/profile_type_test.dart new file mode 100644 index 000000000..08ba5c82d --- /dev/null +++ b/test/app/shared/enum/type/profile/profile_type_test.dart @@ -0,0 +1,15 @@ +import 'package:altme/app/app.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('ProfileType Extension', () { + test('Show Sponsered By', () { + expect(ProfileType.custom.showSponseredBy, false); + expect(ProfileType.ebsiV3.showSponseredBy, true); + expect(ProfileType.dutch.showSponseredBy, false); + expect(ProfileType.enterprise.showSponseredBy, true); + expect(ProfileType.owfBaselineProfile.showSponseredBy, true); + expect(ProfileType.defaultOne.showSponseredBy, false); + }); + }); +} diff --git a/test/app/shared/enum/type/wallet_provider_type_test.dart b/test/app/shared/enum/type/wallet_provider_type_test.dart new file mode 100644 index 000000000..a0ab74b83 --- /dev/null +++ b/test/app/shared/enum/type/wallet_provider_type_test.dart @@ -0,0 +1,16 @@ +import 'package:altme/app/app.dart'; +import 'package:test/test.dart'; + +void main() { + group('WalletProviderType Extension Tests', () { + test('WalletProviderType url returns correct value', () { + expect(WalletProviderType.Talao.url, equals(Urls.walletProvider)); + expect(WalletProviderType.Test.url, equals(Urls.walletTestProvider)); + }); + + test('WalletProviderType formattedString returns correct value', () { + expect(WalletProviderType.Talao.formattedString, equals('Talao')); + expect(WalletProviderType.Test.formattedString, equals('Test')); + }); + }); +} diff --git a/test/app/shared/extension/bigint_extension_test.dart b/test/app/shared/extension/bigint_extension_test.dart new file mode 100644 index 000000000..ed997ff8f --- /dev/null +++ b/test/app/shared/extension/bigint_extension_test.dart @@ -0,0 +1,27 @@ +import 'package:altme/app/app.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('BigIntExtension', () { + test('toBytes converts BigInt to byte list', () { + final bigInt = BigInt.from(16909060); // Example value: 0x01020304 + final bytes = bigInt.toBytes; + expect(bytes, [1, 2, 3, 4]); + }); + + test('toBytes returns empty list for zero', () { + final bigInt = BigInt.zero; + final bytes = bigInt.toBytes; + expect(bytes, []); + }); + + test('toBytes handles large BigInt values', () { + final bigInt = BigInt.parse('123456789012345678901234567890'); + final bytes = bigInt.toBytes; + expect( + bytes, + [1, 142, 233, 15, 246, 195, 115, 224, 238, 78, 63, 10, 210], + ); + }); + }); +} diff --git a/test/app/shared/extension/double_extension_test.dart b/test/app/shared/extension/double_extension_test.dart new file mode 100644 index 000000000..41aee422c --- /dev/null +++ b/test/app/shared/extension/double_extension_test.dart @@ -0,0 +1,41 @@ +import 'package:altme/app/app.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('DoubleExtension', () { + test('decimalNumber returns string with specified decimal places', () { + const value = 3.14159; + expect(value.decimalNumber(2), '3.14'); + }); + + test('decimalNumber handles values with fewer decimal places', () { + const value = 123.4512; + expect(value.decimalNumber(4), '123.4512'); + }); + + test('decimalNumber handles values with fewer decimal places with 0s', () { + const value = 123.4500; + expect(value.decimalNumber(4), '123.45'); + }); + + test('decimalNumber handles values with more decimal places', () { + const value = 0.123456789; + expect(value.decimalNumber(5), '0.12345'); + }); + + test('decimalNumber returns 0.00 for zero', () { + const value = 0.0; + expect(value.decimalNumber(2), '0.0'); + }); + + test('decimalNumber handles negative values', () { + const value = -12.3456; + expect(value.decimalNumber(3), '-12.346'); + }); + + test('decimalNumber handles large values', () { + const value = 987654321.987656321; + expect(value.decimalNumber(5), '987654321.98765'); + }); + }); +} diff --git a/test/app/shared/extension/iterable_extension_test.dart b/test/app/shared/extension/iterable_extension_test.dart new file mode 100644 index 000000000..fd160f163 --- /dev/null +++ b/test/app/shared/extension/iterable_extension_test.dart @@ -0,0 +1,37 @@ +import 'package:altme/app/app.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('IterableExtension', () { + test('firstWhereOrNull returns first element that satisfies the condition', + () { + final list = [1, 2, 3, 4, 5]; + expect(list.firstWhereOrNull((element) => element.isOdd), 1); + expect(list.firstWhereOrNull((element) => element.isEven), 2); + }); + + test('firstWhereOrNull returns null if no element satisfies the condition', + () { + final list = [1, 3, 5, 7, 9]; + expect(list.firstWhereOrNull((element) => element.isEven), null); + }); + + test('lastWhereOrNull returns last element that satisfies the condition', + () { + final list = [1, 2, 3, 4, 5]; + expect(list.lastWhereOrNull((element) => element.isOdd), 5); + expect(list.lastWhereOrNull((element) => element.isEven), 4); + }); + + test('lastWhereOrNull returns null if no element satisfies the condition', + () { + final list = [1, 3, 5, 7, 9]; + expect(list.lastWhereOrNull((element) => element.isEven), null); + }); + + test('lastWhereOrNull returns null for empty iterable', () { + final list = []; + expect(list.lastWhereOrNull((element) => true), null); + }); + }); +} diff --git a/test/app/shared/extension/string_extension_test.dart b/test/app/shared/extension/string_extension_test.dart new file mode 100644 index 000000000..9ce281833 --- /dev/null +++ b/test/app/shared/extension/string_extension_test.dart @@ -0,0 +1,55 @@ +import 'package:altme/app/app.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('StringExtension', () { + test('formatNumber formats numbers correctly', () { + expect('1000'.formatNumber, '1,000'); + expect('123456789'.formatNumber, '123,456,789'); + expect('12345.6789'.formatNumber, '12,345.6789'); + expect('0.12345'.formatNumber, '0.12345'); + expect('123'.formatNumber, '123'); + expect('abc'.formatNumber, 'abc'); + }); + + test('isValidEmail validates email addresses', () { + expect('email@example.com'.isValidEmail(), true); + expect('invalid.email@'.isValidEmail(), false); + expect('another.invalid.email@domain'.isValidEmail(), false); + }); + + test('char2Bytes converts string to hex bytes', () { + expect('hello'.char2Bytes, '68656c6c6f'); + expect(''.char2Bytes, ''); + }); + + test('isEVM identifies EVM-based currencies', () { + expect('ETH'.isEVM, true); + expect('MATIC'.isEVM, true); + expect('FTM'.isEVM, true); + expect('BNB'.isEVM, true); + expect('BTC'.isEVM, false); + }); + + test('decimalNumber formats decimal numbers correctly', () { + expect('123.456'.decimalNumber(2), '123.45'); + expect('123.456'.decimalNumber(4), '123.456'); + expect('123'.decimalNumber(3), '123'); + }); + + test('invalid decimalNumber throws FormatException', () { + expect(() => 'abc'.decimalNumber(2), throwsFormatException); + }); + + test('Characters getter returns correct characters', () { + const input = 'Hello, world!'; + final characters = input.characters; + + expect(characters.length, input.length); + + for (int i = 0; i < input.length; i++) { + expect(characters.elementAt(i), input[i]); + } + }); + }); +} diff --git a/test/app/shared/extension/unit_8_list_extension_test.dart b/test/app/shared/extension/unit_8_list_extension_test.dart new file mode 100644 index 000000000..2ff3c43eb --- /dev/null +++ b/test/app/shared/extension/unit_8_list_extension_test.dart @@ -0,0 +1,40 @@ +import 'dart:typed_data'; +import 'package:altme/app/app.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('Uint8ListExtension', () { + test( + 'filterPayload returns the original list ' + 'if length is less than or equal to 6', () { + final list = Uint8List.fromList([1, 2, 3]); + expect(list.filterPayload, list); + + final shortList = Uint8List.fromList([1, 2, 3, 4, 5, 6]); + expect(shortList.filterPayload, shortList); + }); + + test( + 'filterPayload returns the original list ' + 'if the first two elements do not match the test list', () { + final list = Uint8List.fromList([1, 2, 3, 4, 5, 6, 7, 8, 9]); + expect(list.filterPayload, list); + + final differentFirstElement = + Uint8List.fromList([6, 1, 3, 4, 5, 6, 7, 8, 9]); + expect(differentFirstElement.filterPayload, differentFirstElement); + + final differentSecondElement = + Uint8List.fromList([5, 5, 3, 4, 5, 6, 7, 8, 9]); + expect(differentSecondElement.filterPayload, differentSecondElement); + }); + + test( + 'filterPayload returns a sublist starting from index 6 ' + 'if the first two elements match the test list', () { + final list = Uint8List.fromList([5, 1, 3, 4, 5, 6, 7, 8, 9]); + final expected = Uint8List.fromList([7, 8, 9]); + expect(list.filterPayload, expected); + }); + }); +} diff --git a/test/app/shared/helper_function.dart b/test/app/shared/helper_function_test.dart similarity index 100% rename from test/app/shared/helper_function.dart rename to test/app/shared/helper_function_test.dart diff --git a/test/app/shared/helper_functions/helper_functions_test.dart b/test/app/shared/helper_functions/helper_functions_test.dart new file mode 100644 index 000000000..cec8dd02a --- /dev/null +++ b/test/app/shared/helper_functions/helper_functions_test.dart @@ -0,0 +1,1652 @@ +// ignore_for_file: lines_longer_than_80_chars + +import 'dart:convert'; + +import 'package:altme/app/app.dart'; +import 'package:altme/dashboard/home/home.dart'; +import 'package:credential_manifest/credential_manifest.dart'; +import 'package:dio/dio.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:http_mock_adapter/http_mock_adapter.dart'; +import 'package:jwt_decode/jwt_decode.dart'; +import 'package:key_generator/key_generator.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:oidc4vc/oidc4vc.dart'; +import 'package:secure_storage/secure_storage.dart'; + +class MockSecureStorage extends Mock implements SecureStorageProvider {} + +class MockDotenv extends Mock implements DotEnv {} + +void main() { + late SecureStorageProvider mockSecureStorage; + + final client = Dio(); + late DioAdapter dioAdapter; + late DioClient mockClient; + + setUp(() { + TestWidgetsFlutterBinding.ensureInitialized(); + mockSecureStorage = MockSecureStorage(); + + dioAdapter = + DioAdapter(dio: Dio(BaseOptions()), matcher: const UrlRequestMatcher()); + client.httpClientAdapter = dioAdapter; + mockClient = DioClient( + baseUrl: 'https://example.com/', + secureStorageProvider: mockSecureStorage, + dio: client, + ); + }); + + group('HelperFunctions', () { + group('generateDefaultAccountName', () { + test('Should generate default account name', () { + expect(generateDefaultAccountName(0, []), 'My account 1'); + expect(generateDefaultAccountName(1, []), 'My account 2'); + expect(generateDefaultAccountName(2, []), 'My account 3'); + }); + + test('Should generate unique default account name', () { + final accountNameList = ['My account 1', 'My account 2']; + expect(generateDefaultAccountName(0, accountNameList), 'My account 3'); + }); + + test('Should handle existing default account name', () { + final accountNameList = [ + 'My account 1', + 'My account 2', + 'My account 3', + ]; + expect(generateDefaultAccountName(0, accountNameList), 'My account 4'); + }); + }); + + group('Platform checks', () { + test('isAndroid should return true on Android platform', () { + expect(isAndroid, false); + }); + + test('isIOS should return true on iOS platform', () { + expect(isIOS, false); + }); + }); + + group('getIssuerDid', () { + test('Should return empty string if no issuer in URI', () { + final uri = Uri.parse('https://example.com'); + expect(getIssuerDid(uriToCheck: uri), ''); + }); + + test('Should return issuer DID if present in URI', () { + final uri = Uri.parse('https://example.com?issuer=did:example'); + expect(getIssuerDid(uriToCheck: uri), 'did:example'); + }); + + test('Should return last issuer DID if multiple issuers in URI', () { + final uri = Uri.parse( + 'https://example.com?issuer=did:example1&issuer=did:example2', + ); + expect(getIssuerDid(uriToCheck: uri), 'did:example2'); + }); + }); + + group('isValidPrivateKey', () { + test('Should return true for valid Ethereum private key', () { + const validKey = + '0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; + expect(isValidPrivateKey(validKey), true); + }); + + test('Should return true for valid Tezos private key (edsk)', () { + const validKey = + 'edsk3nLLjZzUtwUmBzJbry8MZx8YmPfEzZaCFpSSKmCWUz1k5ehSJ8'; + expect(isValidPrivateKey(validKey), true); + }); + + test('Should return false for invalid private key', () { + const invalidKey = 'invalid_private_key'; + expect(isValidPrivateKey(invalidKey), false); + }); + }); + + test('stringToHexPrefixedWith05 returns correct value', () { + const payload = 'Bibash'; + expect( + stringToHexPrefixedWith05( + payload: payload, + dateTime: DateTime(2022, 1, 1, 0, 0, 0, 0), + ), + '05010031323254657a6f73205369676e6564204d6573736167653a20616c746d652e696f20323032322d30312d30312030303a30303a30302e30303020426962617368', + ); + }); + + test('getCredentialName returns correct credential name', () { + const constraints = { + 'fields': [ + { + 'path': r'[$.credentialSubject.type]', + 'filter': {'pattern': 'Bibash'}, + }, + ], + }; + + final result = getCredentialName(jsonEncode(constraints)); + expect(result, equals('Bibash')); + }); + + test('getIssuersName returns correct issuer name', () { + const constraints = { + 'fields': [ + { + 'path': r'[$.issuer]', + 'filter': {'pattern': 'Bibash'}, + }, + ], + }; + + final result = getIssuersName(jsonEncode(constraints)); + expect(result, equals('Bibash')); + }); + + test('getBlockchainType returns correct issuer name', () { + expect( + getBlockchainType(AccountType.tezos), + equals(BlockchainType.tezos), + ); + expect( + getBlockchainType(AccountType.ethereum), + equals(BlockchainType.ethereum), + ); + expect( + getBlockchainType(AccountType.fantom), + equals(BlockchainType.fantom), + ); + expect( + getBlockchainType(AccountType.polygon), + equals(BlockchainType.polygon), + ); + expect( + getBlockchainType(AccountType.binance), + equals(BlockchainType.binance), + ); + expect( + () => getBlockchainType(AccountType.ssi), + throwsA(isA()), + ); + }); + + test('getCredTypeFromName returns correct type', () { + expect( + getCredTypeFromName('DefiCompliance'), + equals(CredentialSubjectType.defiCompliance), + ); + expect(getCredTypeFromName('afsd'), equals(isNull)); + }); + + test('timeFormatter formats time correctly', () { + expect(timeFormatter(timeInSecond: 65), equals('01 : 05')); + expect(timeFormatter(timeInSecond: 3600), equals('60 : 00')); + expect(timeFormatter(timeInSecond: 3665), equals('61 : 05')); + expect(timeFormatter(timeInSecond: 0), equals('00 : 00')); + }); + + test('getssiMnemonicsInList returns list of words', () async { + const mockMnemonic = + 'word1 word2 word3 word4 word5 word6 word7 word8 word9 word10 word11 word12'; + + when(() => mockSecureStorage.get(SecureStorageKeys.ssiMnemonic)) + .thenAnswer((_) => Future.value(mockMnemonic)); + + final result = await getssiMnemonicsInList(mockSecureStorage); + + expect( + result, + containsAll([ + 'word1', + 'word2', + 'word3', + 'word4', + 'word5', + 'word6', + 'word7', + 'word8', + 'word9', + 'word10', + 'word11', + 'word12', + ]), + ); + }); + + test('getDateTimeWithoutSpace replaces spaces with dashes', () { + final formattedDateTime = + getDateTimeWithoutSpace(dateTime: DateTime(2022, 1, 1, 1, 1, 1, 1)); + expect(formattedDateTime, '2022-01-01-01:01:01.001'); + }); + + test('getIndexValue returns correct index for each DidKeyType', () { + expect( + getIndexValue(isEBSIV3: true, didKeyType: DidKeyType.secp256k1), + 3, + ); + expect( + getIndexValue(isEBSIV3: false, didKeyType: DidKeyType.secp256k1), + 1, + ); + + expect(getIndexValue(isEBSIV3: false, didKeyType: DidKeyType.p256), 4); + expect(getIndexValue(isEBSIV3: false, didKeyType: DidKeyType.ebsiv3), 5); + expect(getIndexValue(isEBSIV3: false, didKeyType: DidKeyType.jwkP256), 6); + expect(getIndexValue(isEBSIV3: false, didKeyType: DidKeyType.edDSA), 0); + expect( + getIndexValue( + isEBSIV3: false, + didKeyType: DidKeyType.jwtClientAttestation, + ), + 0, + ); + }); + + group('getWalletAttestationP256Key', () { + test('returns existing key', () async { + const existingKey = 'existing_key'; + when( + () => + mockSecureStorage.get(SecureStorageKeys.p256PrivateKeyForWallet), + ).thenAnswer((_) => Future.value(existingKey)); + + final result = await getWalletAttestationP256Key(mockSecureStorage); + + expect(result, existingKey.replaceAll('=', '')); + }); + + test('generates and returns new key', () async { + when( + () => + mockSecureStorage.get(SecureStorageKeys.p256PrivateKeyForWallet), + ).thenAnswer((_) => Future.value(null)); + + when(() => mockSecureStorage.set(any(), any())) + .thenAnswer((_) async {}); + + final result = await getWalletAttestationP256Key(mockSecureStorage); + final data = jsonDecode(result) as Map; + + expect(data['alg'], 'ES256'); + expect(data['crv'], 'P-256'); + expect(data['d'], isA()); + expect(data['kty'], 'EC'); + expect(data['use'], 'sig'); + expect(data['x'], isA()); + expect(data['y'], isA()); + }); + }); + + group('getP256KeyToGetAndPresentVC', () { + test('returns existing key', () async { + const existingKey = 'existing_key'; + when( + () => mockSecureStorage + .get(SecureStorageKeys.p256PrivateKeyToGetAndPresentVC), + ).thenAnswer((_) => Future.value(existingKey)); + + final result = await getP256KeyToGetAndPresentVC(mockSecureStorage); + + expect(result, existingKey.replaceAll('=', '')); + }); + + test('generates and returns new key', () async { + when( + () => mockSecureStorage + .get(SecureStorageKeys.p256PrivateKeyToGetAndPresentVC), + ).thenAnswer((_) => Future.value(null)); + + when(() => mockSecureStorage.set(any(), any())) + .thenAnswer((_) async {}); + + final result = await getP256KeyToGetAndPresentVC(mockSecureStorage); + final data = jsonDecode(result) as Map; + + expect(data['alg'], 'ES256'); + expect(data['crv'], 'P-256'); + expect(data['d'], isA()); + expect(data['kty'], 'EC'); + expect(data['use'], 'sig'); + expect(data['x'], isA()); + expect(data['y'], isA()); + }); + }); + + test('generateRandomP256Key returns a valid P-256 key', () { + final key = generateRandomP256Key(); + + final data = jsonDecode(key) as Map; + + expect(data['alg'], 'ES256'); + expect(data['crv'], 'P-256'); + expect(data['d'], isA()); + expect(data['kty'], 'EC'); + expect(data['use'], 'sig'); + expect(data['x'], isA()); + expect(data['y'], isA()); + }); + + test('getDidKeyFromString returns correct enum value', () { + expect(getDidKeyFromString('DidKeyType.edDSA'), DidKeyType.edDSA); + expect(getDidKeyFromString('DidKeyType.secp256k1'), DidKeyType.secp256k1); + expect(getDidKeyFromString('DidKeyType.p256'), DidKeyType.p256); + expect(getDidKeyFromString('DidKeyType.ebsiv3'), DidKeyType.ebsiv3); + expect(getDidKeyFromString('DidKeyType.jwkP256'), DidKeyType.jwkP256); + expect( + getDidKeyFromString('DidKeyType.jwtClientAttestation'), + DidKeyType.jwtClientAttestation, + ); + expect(getDidKeyFromString('InvalidKeyType'), null); + expect(getDidKeyFromString(null), null); + }); + + group('JWT Decode Payload and Header', () { + final jwtDecode = JWTDecode(); + + const jwt = + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFt' + 'ZSI6IkJpYmFzaCIsImlhdCI6MTUxNjIzOTAyMn0.ILWacv8Ed_PmWsLBEIK1mM-wOrt4w' + 'AU7_OUNLXQqtwI'; + + test('decodePayload correctly decodes a JWT token', () { + final decodedData = decodePayload(jwtDecode: jwtDecode, token: jwt); + final expectedData = { + 'sub': '1234567890', + 'name': 'Bibash', + 'iat': 1516239022, + }; + expect(decodedData, expectedData); + }); + + test('decodeHeader correctly decodes a JWT token', () { + final decodedData = decodeHeader(jwtDecode: jwtDecode, token: jwt); + final expectedData = { + 'alg': 'HS256', + 'typ': 'JWT', + }; + expect(decodedData, expectedData); + }); + + test('birthDateFormater formats the birth date correctly', () { + const birthData = 19900101; + final formattedBirthdate = birthDateFormater(birthData); + + expect(formattedBirthdate, '1990-01-01'); + }); + + test('getSignatureType returns correct signature type for circuitId', () { + expect(getSignatureType('credentialAtomicQuerySigV2'), 'BJJ Signature'); + expect( + getSignatureType('credentialAtomicQuerySigV2OnChain'), + 'BJJ Signature', + ); + expect(getSignatureType('credentialAtomicQueryMTPV2'), 'SMT Signature'); + expect( + getSignatureType('credentialAtomicQueryMTPV2OnChain'), + 'SMT Signature', + ); + expect(getSignatureType('unknownCircuitId'), ''); + }); + + test('splitUppercase splits PascalCase string correctly', () { + const input = 'BibashManShrestha'; + final result = splitUppercase(input); + expect(result, 'Bibash Man Shrestha'); + }); + + test('generateUriList returns list of URIs from "uri_list" parameter', + () { + const url = + 'https://example.com?uri_list=https%3A%2F%2Fexample.com%2Fpath1&uri_list=https%3A%2F%2Fexample.com%2Fpath2'; + final result = generateUriList(url); + expect( + result, + ['https://example.com/path1', 'https://example.com/path2'], + ); + }); + + test('sortedPublcJwk returns sorted public JWK without private key', () { + final privateKey = { + 'kty': 'EC', + 'use': 'sig', + 'crv': 'P-256K', + 'kid': '1234567890', + 'x': 'SjTww7i4eF-JKBYlShJqJ3lWQIVJF5y1g5uHY3gfAro', + 'y': '1bNb6uA0gKClEFhodSfgcW8FvfSHTgxE8WyFvSZ8bxc', + }; + final result = sortedPublcJwk(jsonEncode(privateKey)); + const expected = + '{"crv":"secp256k1","kid":"1234567890","kty":"EC","x":"SjTww7i4eF-JKBYlShJqJ3lWQIVJF5y1g5uHY3gfAro","y":"1bNb6uA0gKClEFhodSfgcW8FvfSHTgxE8WyFvSZ8bxc"}'; + expect(result, expected); + }); + + test('isPolygonIdUrl returns correct value for valid Polygon ID URLs', + () { + expect(isPolygonIdUrl('{"id":'), true); + expect(isPolygonIdUrl('{"body":{"'), true); + expect(isPolygonIdUrl('{"from": "did:polygonid:'), true); + expect(isPolygonIdUrl('{"to": "did:polygonid:'), true); + expect(isPolygonIdUrl('{"thid":'), true); + expect(isPolygonIdUrl('{"typ":'), true); + expect(isPolygonIdUrl('{"type":'), true); + }); + + test('isOIDC4VCIUrl returns true for valid OIDC4VCI URLs', () { + expect(isOIDC4VCIUrl(Uri.parse('openid://some/path')), true); + expect(isOIDC4VCIUrl(Uri.parse('haip://another/path')), true); + }); + + group('handleErrorForOID4VCI throws correct errors', () { + test('Test tokenEndpoint is null', () { + expect( + () async => handleErrorForOID4VCI( + url: 'example', + openIdConfiguration: const OpenIdConfiguration( + requirePushedAuthorizationRequests: false, + authorizationServer: 'example', + tokenEndpoint: null, + ), + authorizationServerConfiguration: const OpenIdConfiguration( + requirePushedAuthorizationRequests: false, + tokenEndpoint: null, + ), + ), + throwsA( + isA().having((e) => e.data, '', { + 'error': 'invalid_issuer_metadata', + 'error_description': 'The issuer configuration is invalid. ' + 'The token_endpoint is missing.', + }), + ), + ); + }); + + test('Test credentialEndpoint is null', () { + expect( + () async => handleErrorForOID4VCI( + url: 'example', + openIdConfiguration: const OpenIdConfiguration( + requirePushedAuthorizationRequests: false, + authorizationServer: 'example', + tokenEndpoint: null, + credentialEndpoint: null, + ), + authorizationServerConfiguration: const OpenIdConfiguration( + tokenEndpoint: 'https://example.com/token', + requirePushedAuthorizationRequests: false, + ), + ), + throwsA( + isA().having((e) => e.data, '', { + 'error': 'invalid_issuer_metadata', + 'error_description': 'The issuer configuration is invalid. ' + 'The credential_endpoint is missing.', + }), + ), + ); + }); + + test('Test credentialIssuer is null', () { + expect( + () async => handleErrorForOID4VCI( + url: 'example', + openIdConfiguration: const OpenIdConfiguration( + requirePushedAuthorizationRequests: false, + authorizationServer: 'example', + tokenEndpoint: null, + credentialEndpoint: 'https://example.com/cred', + credentialIssuer: null, + ), + authorizationServerConfiguration: const OpenIdConfiguration( + requirePushedAuthorizationRequests: false, + tokenEndpoint: 'https://example.com/token', + ), + ), + throwsA( + isA().having((e) => e.data, '', { + 'error': 'invalid_issuer_metadata', + 'error_description': 'The issuer configuration is invalid. ' + 'The credential_issuer is missing.', + }), + ), + ); + }); + + test( + 'Test credentialsSupported and credentialConfigurationsSupported are null', + () { + expect( + () async => handleErrorForOID4VCI( + url: 'example', + openIdConfiguration: const OpenIdConfiguration( + requirePushedAuthorizationRequests: false, + authorizationServer: 'example', + tokenEndpoint: null, + credentialEndpoint: 'https://example.com/cred', + credentialIssuer: 'issuer', + credentialsSupported: null, + credentialConfigurationsSupported: null, + ), + authorizationServerConfiguration: const OpenIdConfiguration( + requirePushedAuthorizationRequests: false, + tokenEndpoint: 'https://example.com/token', + ), + ), + throwsA( + isA().having((e) => e.data, '', { + 'error': 'invalid_issuer_metadata', + 'error_description': 'The issuer configuration is invalid. ' + 'The credentials_supported is missing.', + }), + ), + ); + }); + + test( + 'Test credentialsSupported and credentialConfigurationsSupported are null', + () { + expect( + () async => handleErrorForOID4VCI( + url: 'example', + openIdConfiguration: const OpenIdConfiguration( + requirePushedAuthorizationRequests: false, + authorizationServer: 'example', + tokenEndpoint: null, + credentialEndpoint: 'https://example.com/cred', + credentialIssuer: 'issuer', + credentialsSupported: null, + credentialConfigurationsSupported: 'asdf', + subjectSyntaxTypesSupported: ['asd'], + ), + authorizationServerConfiguration: const OpenIdConfiguration( + requirePushedAuthorizationRequests: false, + tokenEndpoint: 'https://example.com/token', + ), + ), + throwsA( + isA().having((e) => e.data, '', { + 'error': 'subject_syntax_type_not_supported', + 'error_description': + 'The subject syntax type is not supported.', + }), + ), + ); + }); + }); + + group('getPresentationDefinition', () { + test('returns presentation definition from URI', () async { + final uri = Uri.parse( + "https://example.com?presentation_definition={'title':'Test'}", + ); + final presentationDefinition = + await getPresentationDefinition(uri: uri, client: mockClient); + + expect(presentationDefinition, {'title': 'Test'}); + }); + + test('returns null for invalid URI', () async { + final uri = Uri.parse('https://example.com'); + final presentationDefinition = + await getPresentationDefinition(uri: uri, client: mockClient); + + expect(presentationDefinition, isNull); + }); + + test( + 'returns presentation definition from URI with ' + 'presentation_definition_uri', () async { + final uri = Uri.parse( + 'https://example.com?presentation_definition_uri=https://example.com/presentation.com', + ); + + dioAdapter.onGet( + 'https://example.com/presentation.com', + (request) => request.reply(200, {'title': 'Test'}), + ); + + final presentationDefinition = + await getPresentationDefinition(uri: uri, client: mockClient); + + expect(presentationDefinition, {'title': 'Test'}); + }); + + test('returns null for invalid presentation_definition_uri', () async { + final uri = Uri.parse( + 'https://example.com?presentation_definition_uri=https://example.com/presentation.com', + ); + + dioAdapter.onGet( + 'https://example.com/presentation.com', + (request) => request.reply(200, 'asfd'), + ); + + final presentationDefinition = + await getPresentationDefinition(uri: uri, client: mockClient); + + expect(presentationDefinition, isNull); + }); + }); + + group('getClientMetada', () { + test('returns client metadata from URI', () async { + final uri = + Uri.parse("https://example.com?client_metadata={'title':'Test'}"); + final clientMetadata = + await getClientMetada(uri: uri, client: mockClient); + + expect(clientMetadata, {'title': 'Test'}); + }); + + test('returns null for invalid URI', () async { + final uri = Uri.parse('https://example.com'); + final clientMetadata = + await getClientMetada(uri: uri, client: mockClient); + + expect(clientMetadata, isNull); + }); + + test( + 'returns client metadata from URI with ' + 'client_metadata_uri', () async { + final uri = Uri.parse( + 'https://example.com?client_metadata_uri=https://example.com.com', + ); + + dioAdapter.onGet( + 'https://example.com.com', + (request) => request.reply(200, {'title': 'Test'}), + ); + + final clientMetadata = + await getClientMetada(uri: uri, client: mockClient); + + expect(clientMetadata, {'title': 'Test'}); + }); + + test('returns null for invalid client_metadata_uri', () async { + final uri = Uri.parse( + 'https://example.com?client_metadata_uri=https://example.com.com', + ); + + dioAdapter.onGet( + 'https://example.com.com', + (request) => request.reply(200, 'asfd'), + ); + + expect( + () async => getClientMetada(uri: uri, client: mockClient), + throwsA( + isA().having((e) => e.data, '', { + 'error': 'invalid_request', + 'error_description': 'Client metaData is invalid', + }), + ), + ); + }); + }); + + group('getCredentialData', () { + test('getCredentialData returns credential if it is a String', () { + const credential = 'credentialData'; + final result = getCredentialData(credential); + expect(result, equals(credential)); + }); + + test('getCredentialData returns last credential type if it is a Map', + () { + final credential = { + 'types': ['type1', 'type2', 'type3'], + }; + final result = getCredentialData(credential); + expect(result, equals('type3')); + }); + + test('getCredentialData throws exception for invalid credential format', + () { + const credential = 123; + expect(() => getCredentialData(credential), throwsException); + }); + + test('getMessageHandler returns correct MessageHandler', () { + expect(getMessageHandler(MessageHandler), isA()); + expect( + getMessageHandler( + DioException( + requestOptions: RequestOptions(path: 'test/path'), + error: 'Test error', + response: Response( + data: 'Test data', + statusCode: 400, + requestOptions: RequestOptions(), + ), + ), + ), + isA(), + ); + expect( + getMessageHandler( + const FormatException('Test format exception', 'source'), + ), + isA().having((e) => e.data, '', { + 'error': 'unsupported_format', + 'error_description': 'Test format exception\n' + '\n' + 'source', + }), + ); + expect( + getMessageHandler(TypeError()), + isA().having((e) => e.data, '', { + 'error': 'unsupported_format', + 'error_description': + 'Some issue in the response from the server.', + }), + ); + expect( + getMessageHandler('Exception: CREDENTIAL_SUPPORT_DATA_ERROR'), + isA().having((e) => e.data, '', { + 'error': 'unsupported_credential_format', + 'error_description': + 'The credential support format has some issues.', + }), + ); + expect( + getMessageHandler('Exception: AUTHORIZATION_DETAIL_ERROR'), + isA().having((e) => e.data, '', { + 'error': 'unsupported_format', + 'error_description': 'Invalid token response format.', + }), + ); + expect( + getMessageHandler('Exception: INVALID_TOKEN'), + isA().having((e) => e.data, '', { + 'error': 'invalid_format', + 'error_description': 'Failed to extract header from jwt.', + }), + ); + expect( + getMessageHandler('Exception: INVALID_PAYLOAD'), + isA().having((e) => e.data, '', { + 'error': 'invalid_format', + 'error_description': 'Failed to extract payload from jwt.', + }), + ); + expect( + getMessageHandler('Exception: SSI_ISSUE'), + isA().having((e) => e.data, '', { + 'error': 'invalid_format', + 'error_description': 'SSI does not support this process.', + }), + ); + expect( + getMessageHandler('Exception: OPENID-CONFIGURATION-ISSUE'), + isA().having((e) => e.data, '', { + 'error': 'unsupported_format', + 'error_description': 'Openid configuration response issue.', + }), + ); + expect( + getMessageHandler('Exception: NOT_A_VALID_OPENID_URL'), + isA().having((e) => e.data, '', { + 'error': 'unsupported_format', + 'error_description': + 'Not a valid openid url to initiate issuance.', + }), + ); + expect( + getMessageHandler('Exception: JWKS_URI_IS_NULL'), + isA().having((e) => e.data, '', { + 'error': 'unsupported_format', + 'error_description': 'The jwks_uri is null.', + }), + ); + expect( + getMessageHandler('Exception: Issue while getting'), + isA().having((e) => e.data, '', { + 'error': 'invalid_request', + 'error_description': 'Issue while getting', + }), + ); + expect( + getMessageHandler('Exception: SECURE_STORAGE_ISSUE'), + isA().having((e) => e.data, '', { + 'error': 'invalid_request', + 'error_description': 'Secure Storage issue with this device', + }), + ); + expect( + getMessageHandler('Exception: ISSUE_WHILE_ADDING_IDENTITY'), + isA().having((e) => e.data, '', { + 'error': 'invalid_request', + 'error_description': 'Issue while adding identity.', + }), + ); + expect( + getMessageHandler('Exception: ISSUE_WHILE_GETTING_CLAIMS'), + isA().having((e) => e.data, '', { + 'error': 'invalid_request', + 'error_description': 'Issue while getting claims.', + }), + ); + expect( + getMessageHandler('Exception: ISSUE_WHILE_RESTORING_CLAIMS'), + isA().having((e) => e.data, '', { + 'error': 'invalid_request', + 'error_description': 'Issue while restoring claims.', + }), + ); + expect( + getMessageHandler('Exception: PUBLICKEYJWK_EXTRACTION_ERROR'), + isA().having((e) => e.data, '', { + 'error': 'invalid_request', + 'error_description': 'Issue while restoring claims.', + }), + ); + expect( + getMessageHandler('Exception: random'), + isA(), + ); + }); + + test('getErrorResponseString returns correct ResponseString', () { + expect( + getErrorResponseString('invalid_request'), + ResponseString.RESPONSE_STRING_invalidRequest, + ); + expect( + getErrorResponseString('invalid_request_uri'), + ResponseString.RESPONSE_STRING_invalidRequest, + ); + expect( + getErrorResponseString('invalid_request_object'), + ResponseString.RESPONSE_STRING_invalidRequest, + ); + + expect( + getErrorResponseString('unauthorized_client'), + ResponseString.RESPONSE_STRING_accessDenied, + ); + expect( + getErrorResponseString('access_denied'), + ResponseString.RESPONSE_STRING_accessDenied, + ); + expect( + getErrorResponseString('invalid_or_missing_proof'), + ResponseString.RESPONSE_STRING_accessDenied, + ); + expect( + getErrorResponseString('interaction_required'), + ResponseString.RESPONSE_STRING_accessDenied, + ); + + expect( + getErrorResponseString('unsupported_response_type'), + ResponseString.RESPONSE_STRING_thisRequestIsNotSupported, + ); + expect( + getErrorResponseString('invalid_scope'), + ResponseString.RESPONSE_STRING_thisRequestIsNotSupported, + ); + expect( + getErrorResponseString('request_not_supported'), + ResponseString.RESPONSE_STRING_thisRequestIsNotSupported, + ); + expect( + getErrorResponseString('request_uri_not_supported'), + ResponseString.RESPONSE_STRING_thisRequestIsNotSupported, + ); + + expect( + getErrorResponseString('unsupported_credential_type'), + ResponseString.RESPONSE_STRING_unsupportedCredential, + ); + expect( + getErrorResponseString('login_required'), + ResponseString.RESPONSE_STRING_aloginIsRequired, + ); + expect( + getErrorResponseString('account_selection_required'), + ResponseString.RESPONSE_STRING_aloginIsRequired, + ); + + expect( + getErrorResponseString('consent_required'), + ResponseString.RESPONSE_STRING_userConsentIsRequired, + ); + + expect( + getErrorResponseString('registration_not_supported'), + ResponseString.RESPONSE_STRING_theWalletIsNotRegistered, + ); + + expect( + getErrorResponseString('invalid_grant'), + ResponseString.RESPONSE_STRING_credentialIssuanceDenied, + ); + expect( + getErrorResponseString('invalid_client'), + ResponseString.RESPONSE_STRING_credentialIssuanceDenied, + ); + expect( + getErrorResponseString('invalid_token'), + ResponseString.RESPONSE_STRING_credentialIssuanceDenied, + ); + + expect( + getErrorResponseString('unsupported_credential_format'), + ResponseString.RESPONSE_STRING_thisCredentialFormatIsNotSupported, + ); + + expect( + getErrorResponseString('unsupported_format'), + ResponseString.RESPONSE_STRING_thisFormatIsNotSupported, + ); + + expect( + getErrorResponseString('invalid_issuer_metadata'), + ResponseString.RESPONSE_STRING_theCredentialOfferIsInvalid, + ); + + expect( + getErrorResponseString('server_error'), + ResponseString.RESPONSE_STRING_theServiceIsNotAvailable, + ); + + expect( + getErrorResponseString('issuance_pending'), + ResponseString.RESPONSE_STRING_theIssuanceOfThisCredentialIsPending, + ); + + expect( + getErrorResponseString('random'), + ResponseString.RESPONSE_STRING_thisRequestIsNotSupported, + ); + }); + + test('isIDTokenOnly', () { + expect(isIDTokenOnly('id_token'), true); + expect(isIDTokenOnly('id_token vp_token'), false); + expect(isIDTokenOnly('vp_token'), false); + expect(isIDTokenOnly(''), false); + }); + + test('isVPTokenOnly', () { + expect(isVPTokenOnly('id_token'), false); + expect(isVPTokenOnly('vp_token'), true); + expect(isVPTokenOnly('id_token vp_token'), false); + expect(isVPTokenOnly(''), false); + }); + + test('isIDTokenAndVPToken', () { + expect(isIDTokenAndVPToken('id_token'), false); + expect(isIDTokenAndVPToken('vp_token'), false); + expect(isIDTokenAndVPToken('id_token vp_token'), true); + expect(isIDTokenAndVPToken(''), false); + }); + + test('hasIDToken', () { + expect(hasIDToken('id_token'), true); + expect(hasIDToken('vp_token'), false); + expect(hasIDToken('id_token vp_token'), true); + expect(hasIDToken(''), false); + }); + + test('hasVPToken', () { + expect(hasVPToken('id_token'), false); + expect(hasVPToken('vp_token'), true); + expect(hasVPToken('id_token vp_token'), true); + expect(hasVPToken(''), false); + }); + + test('hasIDTokenOrVPToken returns correct url', () { + expect(hasIDTokenOrVPToken('id_token'), true); + expect(hasIDTokenOrVPToken('vp_token'), true); + expect(hasIDTokenOrVPToken('id_token vp_token'), true); + expect(hasIDTokenOrVPToken(''), false); + }); + + test('getUpdatedUrlForSIOPV2OIC4VP', () { + final uri = Uri.parse('https://example.com'); + final response = { + 'response_type': 'code', + 'redirect_uri': 'https://example.com/callback', + 'scope': 'openid profile', + 'response_uri': 'https://example.com/response', + 'response_mode': 'form_post', + 'nonce': '12345', + 'client_id': 'client123', + 'claims': { + 'id_token': { + 'email': {'essential': true}, + }, + }, + 'state': 'state123', + 'presentation_definition': { + 'id': 'cred-123', + 'format': 'vp', + 'input_descriptors': [], + }, + 'presentation_definition_uri': 'https://example.com/presentation', + 'registration': { + 'token_endpoint_auth_method': 'client_secret_basic', + }, + 'client_metadata': {'example_key': 'example_value'}, + 'client_metadata_uri': 'https://example.com/client_metadata', + }; + + final updatedUrl = + getUpdatedUrlForSIOPV2OIC4VP(uri: uri, response: response); + + expect( + updatedUrl.split('&'), + containsAll([ + 'scope=openid+profile', + 'client_id=client123', + 'redirect_uri=https%3A%2F%2Fexample.com%2Fcallback', + 'response_uri=https%3A%2F%2Fexample.com%2Fresponse', + 'response_mode=form_post', + 'nonce=12345', + 'state=state123', + 'response_type=code', + 'claims=%7B%27id_token%27%3A%7B%27email%27%3A%7B%27essential%27%3Atrue%7D%7D%7D', + 'presentation_definition=%7B%27id%27%3A%27cred-123%27%2C%27format%27%3A%27vp%27%2C%27input_descriptors%27%3A%5B%5D%7D', + 'presentation_definition_uri=https%3A%2F%2Fexample.com%2Fpresentation', + 'registration=%7B%22token_endpoint_auth_method%22%3A%22client_secret_basic%22%7D', + 'client_metadata=%7B%22example_key%22%3A%22example_value%22%7D', + 'client_metadata_uri=https%3A%2F%2Fexample.com%2Fclient_metadata', + ]), + ); + }); + + group('getPresentVCDetails', () { + test( + 'returns correct value when presentationDefinition ldp_vc formats', + () { + expect( + getPresentVCDetails( + vcFormatType: VCFormatType.ldpVc, + presentationDefinition: PresentationDefinition( + inputDescriptors: [], + format: Format.fromJson( + { + 'ldp_vc': { + 'proof_type': [], + }, + }, + ), + ), + clientMetaData: null, + ), + (true, false, false, false), + ); + }); + + test( + 'throws ResponseMessage when presentationDefinition has no formats', + () { + final presentationDefinition = PresentationDefinition( + inputDescriptors: [], + format: Format.fromJson({}), + ); + + expect( + () => getPresentVCDetails( + vcFormatType: VCFormatType.ldpVc, + presentationDefinition: presentationDefinition, + clientMetaData: null, + ), + throwsA( + isA().having((e) => e.data, '', { + 'error': 'invalid_request', + 'error_description': 'VC format is missing', + }), + ), + ); + }); + + test( + 'returns correct value(VCFormatType.jwtVc) when' + ' presentationDefinition.format' + ' and clientMetaData are null', () { + expect( + getPresentVCDetails( + vcFormatType: VCFormatType.jwtVc, + presentationDefinition: PresentationDefinition( + inputDescriptors: [], + format: null, + ), + clientMetaData: null, + ), + (false, true, false, false), + ); + }); + + test( + 'returns correct value(VCFormatType.jwtVcJson) when' + ' presentationDefinition.format' + ' and clientMetaData are null', () { + expect( + getPresentVCDetails( + vcFormatType: VCFormatType.jwtVcJson, + presentationDefinition: PresentationDefinition( + inputDescriptors: [], + format: null, + ), + clientMetaData: null, + ), + (false, false, true, false), + ); + }); + + test( + 'returns correct value(VCFormatType.vcSdJWT) when' + ' presentationDefinition.format' + ' and clientMetaData are null', () { + expect( + getPresentVCDetails( + vcFormatType: VCFormatType.vcSdJWT, + presentationDefinition: PresentationDefinition( + inputDescriptors: [], + format: null, + ), + clientMetaData: null, + ), + (false, false, false, true), + ); + }); + + test( + 'returns correct value(VCFormatType.jwtVcJson) when' + ' presentationDefinition.format is null' + ' and clientMetaData is provided', () { + expect( + getPresentVCDetails( + vcFormatType: VCFormatType.jwtVcJson, + presentationDefinition: PresentationDefinition( + inputDescriptors: [], + format: null, + ), + clientMetaData: { + 'vp_formats': { + 'jwt_vc_json': 'here', + }, + }, + ), + (false, false, true, false), + ); + }); + + test( + 'returns correct value(VCFormatType.vc+sd-jwt) when' + ' presentationDefinition.format is null' + ' and clientMetaData is provided', () { + expect( + getPresentVCDetails( + vcFormatType: VCFormatType.vcSdJWT, + presentationDefinition: PresentationDefinition( + inputDescriptors: [], + format: null, + ), + clientMetaData: { + 'vp_formats': { + 'vc+sd-jwt': 'here', + }, + }, + ), + (false, false, false, true), + ); + }); + }); + + group('collectSdValues', () { + test('returns an empty list when no _sd key is present', () { + final data = { + 'a': 1, + 'b': { + 'c': 2, + }, + }; + final result = collectSdValues(data); + expect(result, isEmpty); + }); + + test('collects values from _sd keys', () { + final data = { + '_sd': [1, 2, 3], + 'a': { + '_sd': [4, 5], + }, + 'b': { + 'c': { + '_sd': [6, 7], + }, + }, + }; + final result = collectSdValues(data); + expect(result, [1, 2, 3, 4, 5, 6, 7]); + }); + + test('collects values from ... keys within lists', () { + final data = { + 'a': [ + { + '...': 1, + }, + { + '...': 2, + }, + ], + 'c': [ + { + '...': 3, + }, + ], + }; + final result = collectSdValues(data); + expect(result, [1, 2, 3]); + }); + + test('handles complex nested structures', () { + final data = { + '_sd': [1], + 'a': { + '_sd': [2], + 'b': [ + { + '...': 3, + }, + ], + }, + 'd': [ + { + '...': 5, + }, + ], + }; + final result = collectSdValues(data); + expect(result, [1, 2, 3, 5]); + }); + + test('does not collect non-_sd or non-... values', () { + final data = { + 'a': 1, + 'b': { + 'c': 2, + 'd': [ + { + 'e': 3, + }, + ], + }, + }; + final result = collectSdValues(data); + expect(result, isEmpty); + }); + }); + + group('checkX509', () { + test('throws error for x5c is null', () async { + final result = await checkX509( + clientId: '', + header: {}, + encodedData: '', + ); + expect(result, isNull); + }); + + test('throws error for invalid x5c format', () async { + expect( + () => checkX509( + clientId: '', + header: { + 'x5c': 'i am not list', + }, + encodedData: '', + ), + throwsA( + isA().having((e) => e.data, '', { + 'error': 'invalid_format', + 'error_description': 'x509_san_dns scheme error', + }), + ), + ); + }); + + test('throws error for invalid x5c is empty', () async { + expect( + () => checkX509( + clientId: '', + header: { + 'x5c': [], + }, + encodedData: '', + ), + throwsA( + isA().having((e) => e.data, '', { + 'error': 'invalid_format', + 'error_description': 'x509_san_dns scheme error', + }), + ), + ); + }); + }); + + group('checkVerifierAttestation', () { + final jwtDecode = JWTDecode(); + test('throws error if JWT is missing', () async { + expect( + () => checkVerifierAttestation( + clientId: '', + header: {}, + jwtDecode: jwtDecode, + ), + throwsA( + isA().having((e) => e.data, '', { + 'error': 'invalid_format', + 'error_description': 'verifier_attestation scheme error', + }), + ), + ); + }); + + test('throws error when sub is null', () async { + expect( + () => checkVerifierAttestation( + clientId: '', + header: { + 'jwt': + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBEb2UiLCJpYXQiOjE1MTYyMzkwMjJ9.hqWGSaFpvbrXkOWc6lrnffhNWR19W_S1YKFBx2arWBk', + }, + jwtDecode: jwtDecode, + ), + throwsA( + isA().having((e) => e.data, '', { + 'error': 'invalid_format', + 'error_description': 'verifier_attestation scheme error', + }), + ), + ); + }); + + test('throws error when sub is not equal to clientId', () async { + expect( + () => checkVerifierAttestation( + clientId: 'random', + header: { + 'jwt': + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjQiLCJuYW1lIjoiSm9obiBEb2UiLCJpYXQiOjE1MTYyMzkwMjJ9.EyF5bWaxNMFda_CRAOHu3aagShvHlGpnSWK7uFsj23Q', + }, + jwtDecode: jwtDecode, + ), + throwsA( + isA().having((e) => e.data, '', { + 'error': 'invalid_format', + 'error_description': 'verifier_attestation scheme error', + }), + ), + ); + }); + + test('throws error when sub cnf is null', () async { + expect( + () => checkVerifierAttestation( + clientId: '124', + header: { + 'jwt': + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjQiLCJuYW1lIjoiSm9obiBEb2UiLCJpYXQiOjE1MTYyMzkwMjJ9.EyF5bWaxNMFda_CRAOHu3aagShvHlGpnSWK7uFsj23Q', + }, + jwtDecode: jwtDecode, + ), + throwsA( + isA().having((e) => e.data, '', { + 'error': 'invalid_format', + 'error_description': 'verifier_attestation scheme error', + }), + ), + ); + }); + + test('throws error when cnf is not Map', () async { + expect( + () => checkVerifierAttestation( + clientId: '124', + header: { + 'jwt': + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjQiLCJuYW1lIjoiSm9obiBEb2UiLCJpYXQiOjE1MTYyMzkwMjIsImNuZiI6IjEyMyJ9.xZYbK4Oe2jGyQwyOiK5Zv0sHDtfjgY-sdS7WKs-aZYQ', + }, + jwtDecode: jwtDecode, + ), + throwsA( + isA().having((e) => e.data, '', { + 'error': 'invalid_format', + 'error_description': 'verifier_attestation scheme error', + }), + ), + ); + }); + + test('throws error when cnf does not contain jwk', () async { + expect( + () => checkVerifierAttestation( + clientId: '124', + header: { + 'jwt': + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjQiLCJuYW1lIjoiSm9obiBEb2UiLCJpYXQiOjE1MTYyMzkwMjIsImNuZiI6eyJ0ZXN0IjoidGVzdCJ9fQ.tnYvd00vYuDxjTtC7goSB5EQUNGtSFR1JBpY1CTRzD0', + }, + jwtDecode: jwtDecode, + ), + throwsA( + isA().having((e) => e.data, '', { + 'error': 'invalid_format', + 'error_description': 'verifier_attestation scheme error', + }), + ), + ); + }); + + test('throws error when jwk is not Map', () async { + expect( + () => checkVerifierAttestation( + clientId: '124', + header: { + 'jwt': + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjQiLCJuYW1lIjoiSm9obiBEb2UiLCJpYXQiOjE1MTYyMzkwMjIsImNuZiI6eyJqd2siOiJ0ZXN0In19.qjqntbtQTnRcR60UvveWyv8t3Y1dFMI0DLSyB_pwqBk', + }, + jwtDecode: jwtDecode, + ), + throwsA( + isA().having((e) => e.data, '', { + 'error': 'invalid_format', + 'error_description': 'verifier_attestation scheme error', + }), + ), + ); + }); + + test('return correct jwk', () async { + final result = await checkVerifierAttestation( + clientId: '124', + header: { + 'jwt': + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjQiLCJuYW1lIjoiSm9obiBEb2UiLCJpYXQiOjE1MTYyMzkwMjIsImNuZiI6eyJqd2siOnsibmFtZSI6IkJpYmFzaCJ9fX0.daqYrO5JtoW9o0ZEDlG2a6ctAgQxaNxTT0iYyVBIIkY', + }, + jwtDecode: jwtDecode, + ); + + expect(result, {'name': 'Bibash'}); + }); + }); + + test('returns correct wallet Address', () { + expect( + getWalletAddress( + TezosAssociatedAddressModel( + id: 'id', + type: 'type', + issuedBy: const Author('name'), + associatedAddress: 'tezosAddress', + ), + ), + 'tezosAddress', + ); + expect( + getWalletAddress( + EthereumAssociatedAddressModel( + id: 'id', + type: 'type', + issuedBy: const Author('name'), + associatedAddress: 'ethereumAddress', + ), + ), + 'ethereumAddress', + ); + expect( + getWalletAddress( + PolygonAssociatedAddressModel( + id: 'id', + type: 'type', + issuedBy: const Author('name'), + associatedAddress: 'polygonAddress', + ), + ), + 'polygonAddress', + ); + expect( + getWalletAddress( + BinanceAssociatedAddressModel( + id: 'id', + type: 'type', + issuedBy: const Author('name'), + associatedAddress: 'address', + ), + ), + 'address', + ); + expect( + getWalletAddress( + FantomAssociatedAddressModel( + id: 'id', + type: 'type', + issuedBy: const Author('name'), + associatedAddress: 'fantomAddress', + ), + ), + 'fantomAddress', + ); + expect( + getWalletAddress( + AgeRangeModel( + id: 'id', + type: 'type', + issuedBy: const Author('name'), + ), + ), + isNull, + ); + }); + + group('fetchRpcUrl', () { + late DotEnv mockDotenv; + + setUpAll(() { + mockDotenv = MockDotenv(); + }); + + test('returns rpcNodeUrl for BinanceNetwork.mainNet()', () async { + final result = await fetchRpcUrl( + blockchainNetwork: BinanceNetwork.mainNet(), + dotEnv: mockDotenv, + ); + expect(result, 'https://bsc-dataseed.binance.org/'); + }); + + test('returns rpcNodeUrl for BinanceNetwork.testNet()', () async { + final result = await fetchRpcUrl( + blockchainNetwork: BinanceNetwork.testNet(), + dotEnv: mockDotenv, + ); + expect(result, 'https://bsc-testnet.public.blastapi.io'); + }); + + test('returns rpcNodeUrl for FantomNetwork.mainNet()', () async { + final result = await fetchRpcUrl( + blockchainNetwork: FantomNetwork.mainNet(), + dotEnv: mockDotenv, + ); + expect(result, 'https://rpcapi.fantom.network/'); + }); + + test('returns rpcNodeUrl for FantomNetwork.testNet()', () async { + final result = await fetchRpcUrl( + blockchainNetwork: FantomNetwork.testNet(), + dotEnv: mockDotenv, + ); + expect(result, 'https://rpc.testnet.fantom.network'); + }); + + test('returns infura URL for PolygonNetwork.mainNet()', () async { + when(() => mockDotenv.load()).thenAnswer((_) async => {}); + when(() => mockDotenv.get('INFURA_API_KEY')).thenReturn('123'); + final result = await fetchRpcUrl( + blockchainNetwork: PolygonNetwork.mainNet(), + dotEnv: mockDotenv, + ); + expect(result, '${Parameters.POLYGON_INFURA_URL}123'); + }); + + test('returns infura URL for PolygonNetwork on Testnet', () async { + final result = await fetchRpcUrl( + blockchainNetwork: PolygonNetwork.testNet(), + dotEnv: mockDotenv, + ); + expect(result, 'https://rpc-mumbai.maticvigil.com'); + }); + + test('returns infura URL for EthereumNetwork.mainNet()', () async { + when(() => mockDotenv.load()).thenAnswer((_) async => {}); + when(() => mockDotenv.get('INFURA_API_KEY')).thenReturn('123'); + final result = await fetchRpcUrl( + blockchainNetwork: EthereumNetwork.mainNet(), + dotEnv: mockDotenv, + ); + expect(result, '${Parameters.web3RpcMainnetUrl}123'); + }); + + test('returns infura URL for EthereumNetwork.testNet()', () async { + final result = await fetchRpcUrl( + blockchainNetwork: EthereumNetwork.testNet(), + dotEnv: mockDotenv, + ); + expect(result, 'https://rpc.sepolia.dev'); + }); + + test('returns infura URL for TezosNetwork.mainNet()', () async { + when(() => mockDotenv.load()).thenAnswer((_) async => {}); + when(() => mockDotenv.get('INFURA_API_KEY')).thenReturn('123'); + final result = await fetchRpcUrl( + blockchainNetwork: TezosNetwork.mainNet(), + dotEnv: mockDotenv, + ); + expect(result, '${Parameters.web3RpcMainnetUrl}123'); + }); + + test('returns infura URL for TezosNetwork.ghostnet()', () async { + final result = await fetchRpcUrl( + blockchainNetwork: TezosNetwork.ghostnet(), + dotEnv: mockDotenv, + ); + expect(result, 'https://rpc.tzkt.io/ghostnet'); + }); + }); + }); + }); + }); +} diff --git a/test/app/shared/issuer/check_issuer_test.dart b/test/app/shared/issuer/check_issuer_test.dart new file mode 100644 index 000000000..8ce2671bb --- /dev/null +++ b/test/app/shared/issuer/check_issuer_test.dart @@ -0,0 +1,174 @@ +import 'dart:convert'; + +import 'package:altme/app/app.dart'; +import 'package:altme/app/shared/issuer/models/organization_info.dart'; +import 'package:dio/dio.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:http_mock_adapter/http_mock_adapter.dart'; +import 'package:mockito/mockito.dart'; +import 'package:secure_storage/secure_storage.dart'; + +class MockSecureStorage extends Mock implements SecureStorageProvider {} + +void main() { + group('CheckIssuer', () { + late SecureStorageProvider mockSecureStorage; + + final client = Dio(); + late DioAdapter dioAdapter; + late DioClient mockClient; + + late CheckIssuer checkIssuer; + + const String checkIssuerServerUrl = 'https://example.com'; + final Uri uriToCheck = Uri.parse('https://example.com'); + + setUp(() { + TestWidgetsFlutterBinding.ensureInitialized(); + mockSecureStorage = MockSecureStorage(); + + dioAdapter = DioAdapter( + dio: Dio(BaseOptions()), + matcher: const UrlRequestMatcher(), + ); + client.httpClientAdapter = dioAdapter; + mockClient = DioClient( + baseUrl: 'https://example.com/', + secureStorageProvider: mockSecureStorage, + dio: client, + ); + checkIssuer = CheckIssuer( + mockClient, + checkIssuerServerUrl, + uriToCheck, + ); + }); + + test('should return empty issuer if checkIssuerServerUrl is empty', + () async { + final checkIssuer = CheckIssuer( + mockClient, + '', + uriToCheck, + ); + + final result = await checkIssuer.isIssuerInApprovedList(); + + expect( + jsonEncode(result), + equals(jsonEncode(Issuer.emptyIssuer(uriToCheck.host))), + ); + }); + + test('should return empty issuer if did does not start with did:ebsi', + () async { + final checkIssuer = CheckIssuer( + mockClient, + Urls.checkIssuerEbsiUrl, + Uri.parse('https://example.com'), + ); + + final result = await checkIssuer.isIssuerInApprovedList(); + + expect( + jsonEncode(result), + equals(jsonEncode(Issuer.emptyIssuer('example.com'))), + ); + }); + + test( + 'should return specific issuer for' + ' https://api.conformance.intebsi.xyz/trusted-issuers-registry/v2/issuers', + () async { + final checkIssuer = CheckIssuer( + mockClient, + 'https://api.conformance.intebsi.xyz/trusted-issuers-registry/v2/issuers', + Uri.parse('https://example.com?issuer=did:ebsi'), + ); + + final result = await checkIssuer.isIssuerInApprovedList(); + + final expected = Issuer( + preferredName: '', + did: [], + organizationInfo: OrganizationInfo( + legalName: 'sdf', + currentAddress: '', + id: '', + issuerDomain: [], + website: uriToCheck.host, + ), + ); + + expect( + jsonEncode(result), + equals(jsonEncode(expected)), + ); + }); + + test('should return valid issuer when issuer is in approved list', + () async { + final response = { + 'issuer': { + 'preferredName': 'Example Issuer', + 'did': ['did:example:123'], + 'organizationInfo': { + 'legalName': 'Example Org', + 'currentAddress': '123 Street', + 'id': 'org-123', + 'issuerDomain': ['example.com'], + 'website': 'example.com', + }, + }, + }; + + dioAdapter.onGet( + 'https://example.com/', + (request) => request.reply(200, response), + ); + + final result = await checkIssuer.isIssuerInApprovedList(); + + expect(result.preferredName, equals('Example Issuer')); + expect(result.organizationInfo.legalName, equals('Example Org')); + expect(result.organizationInfo.issuerDomain, contains('example.com')); + }); + + test( + 'should return empty issuer if organizationInfo.issuerDomain ' + 'does not contain uriToCheck.host', () async { + final response = { + 'issuer': { + 'preferredName': 'Example Issuer', + 'did': ['did:example:123'], + 'organizationInfo': { + 'legalName': 'Example Org', + 'currentAddress': '123 Street', + 'id': 'org-123', + 'issuerDomain': ['another.com'], + 'website': 'example.com', + }, + }, + }; + + dioAdapter.onGet( + 'https://example.com/', + (request) => request.reply(200, response), + ); + + final result = await checkIssuer.isIssuerInApprovedList(); + + expect( + jsonEncode(result), + equals(jsonEncode(Issuer.emptyIssuer(uriToCheck.host))), + ); + }); + + test('should throw exception when an error occurs', () async { + expect( + () async => checkIssuer.isIssuerInApprovedList(), + throwsA(isA()), + ); + }); + }); +} diff --git a/test/app/shared/issuer/models/issuer_test.dart b/test/app/shared/issuer/models/issuer_test.dart new file mode 100644 index 000000000..86b8ca1ab --- /dev/null +++ b/test/app/shared/issuer/models/issuer_test.dart @@ -0,0 +1,65 @@ +import 'package:altme/app/app.dart'; +import 'package:altme/app/shared/issuer/models/organization_info.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('Issuer', () { + test('Issuer fromJson should return a valid Issuer object', () { + final json = { + 'preferredName': 'Example Issuer', + 'did': ['did:example:123'], + 'organizationInfo': { + 'id': 'org-123', + 'legalName': 'Example Org', + 'currentAddress': '123 Street', + 'website': 'example.com', + 'issuerDomain': ['example.com'], + }, + }; + + final issuer = Issuer.fromJson(json); + + expect(issuer.preferredName, equals('Example Issuer')); + expect(issuer.did, equals(['did:example:123'])); + expect(issuer.organizationInfo.id, equals('org-123')); + expect(issuer.organizationInfo.legalName, equals('Example Org')); + expect(issuer.organizationInfo.currentAddress, equals('123 Street')); + expect(issuer.organizationInfo.website, equals('example.com')); + expect(issuer.organizationInfo.issuerDomain, equals(['example.com'])); + }); + + test('Issuer toJson should return a valid JSON map', () { + final organizationInfo = OrganizationInfo( + id: 'org-123', + legalName: 'Example Org', + currentAddress: '123 Street', + website: 'example.com', + issuerDomain: ['example.com'], + ); + + final issuer = Issuer( + preferredName: 'Example Issuer', + did: ['did:example:123'], + organizationInfo: organizationInfo, + ); + + final json = issuer.toJson(); + + expect(json['preferredName'], equals('Example Issuer')); + expect(json['did'], equals(['did:example:123'])); + expect(json['organizationInfo'], isA()); + }); + + test('Issuer.emptyIssuer should return an Issuer with default values', () { + final issuer = Issuer.emptyIssuer('example.com'); + + expect(issuer.preferredName, equals('')); + expect(issuer.did, isEmpty); + expect(issuer.organizationInfo.id, equals('')); + expect(issuer.organizationInfo.legalName, equals('')); + expect(issuer.organizationInfo.currentAddress, equals('')); + expect(issuer.organizationInfo.website, equals('example.com')); + expect(issuer.organizationInfo.issuerDomain, isEmpty); + }); + }); +} diff --git a/test/app/shared/issuer/models/organization_info_test.dart b/test/app/shared/issuer/models/organization_info_test.dart new file mode 100644 index 000000000..cf0c8b53a --- /dev/null +++ b/test/app/shared/issuer/models/organization_info_test.dart @@ -0,0 +1,57 @@ +import 'package:altme/app/shared/issuer/models/organization_info.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('OrganizationInfo', () { + test( + 'OrganizationInfo fromJson should ' + 'return a valid OrganizationInfo object', () { + final json = { + 'id': 'org-123', + 'legalName': 'Example Org', + 'currentAddress': '123 Street', + 'website': 'example.com', + 'issuerDomain': ['example.com'], + }; + + final organizationInfo = OrganizationInfo.fromJson(json); + + expect(organizationInfo.id, equals('org-123')); + expect(organizationInfo.legalName, equals('Example Org')); + expect(organizationInfo.currentAddress, equals('123 Street')); + expect(organizationInfo.website, equals('example.com')); + expect(organizationInfo.issuerDomain, equals(['example.com'])); + }); + + test('OrganizationInfo toJson should return a valid JSON map', () { + final organizationInfo = OrganizationInfo( + id: 'org-123', + legalName: 'Example Org', + currentAddress: '123 Street', + website: 'example.com', + issuerDomain: ['example.com'], + ); + + final json = organizationInfo.toJson(); + + expect(json['id'], equals('org-123')); + expect(json['legalName'], equals('Example Org')); + expect(json['currentAddress'], equals('123 Street')); + expect(json['website'], equals('example.com')); + expect(json['issuerDomain'], equals(['example.com'])); + }); + + test( + 'OrganizationInfo.emptyOrganizationInfo ' + 'should return an OrganizationInfo with default values', () { + final organizationInfo = + OrganizationInfo.emptyOrganizationInfo('example.com'); + + expect(organizationInfo.id, equals('')); + expect(organizationInfo.legalName, equals('')); + expect(organizationInfo.currentAddress, equals('')); + expect(organizationInfo.website, equals('example.com')); + expect(organizationInfo.issuerDomain, isEmpty); + }); + }); +} diff --git a/test/app/shared/loading/loading_view_test.dart b/test/app/shared/loading/loading_view_test.dart new file mode 100644 index 000000000..e7bad840b --- /dev/null +++ b/test/app/shared/loading/loading_view_test.dart @@ -0,0 +1,57 @@ +import 'package:altme/app/app.dart'; +import 'package:altme/dashboard/dashboard.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; + +class MockProfileCubit extends Mock implements ProfileCubit { + @override + final state = ProfileState(model: ProfileModel.empty()); +} + +void main() { + group('LoadingView', () { + late ProfileCubit profileCubit; + + setUp(() { + profileCubit = MockProfileCubit(); + }); + + testWidgets('shows loading view correctly', (tester) async { + await tester.pumpWidget( + BlocProvider( + create: (context) => profileCubit, + child: MaterialApp( + home: Builder( + builder: (context) { + return Column( + children: [ + ElevatedButton( + onPressed: () { + LoadingView() + .show(context: context, text: 'Loading...'); + }, + child: const Text('Show Loading View'), + ), + ElevatedButton( + onPressed: () { + LoadingView().hide(); + }, + child: const Text('Hide Loading View'), + ), + ], + ); + }, + ), + ), + ), + ); + + await tester.pump(); + + expect(find.byType(LoadingView), findsNothing); + expect(find.text('Show Loading View'), findsOneWidget); + }); + }); +} diff --git a/test/app/shared/message_handler/message_handler_test.dart b/test/app/shared/message_handler/message_handler_test.dart index 079fd3b23..c99c19826 100644 --- a/test/app/shared/message_handler/message_handler_test.dart +++ b/test/app/shared/message_handler/message_handler_test.dart @@ -1,17 +1,19 @@ -//import 'package:altme/app/shared/message_handler/message_handler.dart'; -//import 'package:flutter/material.dart'; +import 'package:altme/app/shared/message_handler/message_handler.dart'; +import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -//import '../../../helpers/helpers.dart'; +import '../../../helpers/helpers.dart'; + +class TestMessageHandler with MessageHandler {} void main() { group('MessageHandler Test', () { - // testWidgets('returns Unknown message', (tester) async { - // final MessageHandler messageHandler = MessageHandler(); - // await tester.pumpApp(Container()); - // final BuildContext context = tester.element(find.byType(Container)); - // final String text = messageHandler.getMessage(context, messageHandler); - // expect(text, 'Unknown message'); - // }); + testWidgets('returns Unknown message', (tester) async { + final TestMessageHandler messageHandler = TestMessageHandler(); + await tester.pumpApp(Container()); + final BuildContext context = tester.element(find.byType(Container)); + final String text = messageHandler.getMessage(context, messageHandler); + expect(text, 'Unknown message'); + }); }); } diff --git a/test/app/shared/models/blockchain_network/blockchain_network_test.dart b/test/app/shared/models/blockchain_network/blockchain_network_test.dart new file mode 100644 index 000000000..b484522cd --- /dev/null +++ b/test/app/shared/models/blockchain_network/blockchain_network_test.dart @@ -0,0 +1,50 @@ +import 'package:altme/app/app.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('BlockchainNetwork', () { + test('fromJson should create a BlockchainNetwork instance from JSON', () { + final json = { + 'networkname': 'TestNet', + 'apiUrl': 'https://example.com/api', + 'rpcNodeUrl': 'https://rpc.example.com', + 'title': 'Test Network', + 'subTitle': 'Subtitle', + 'type': 'ethereum', + 'apiKey': 'apikey', + }; + + final blockchainNetwork = BlockchainNetwork.fromJson(json); + + expect(blockchainNetwork.networkname, 'TestNet'); + expect(blockchainNetwork.apiUrl, 'https://example.com/api'); + expect(blockchainNetwork.rpcNodeUrl, 'https://rpc.example.com'); + expect(blockchainNetwork.title, 'Test Network'); + expect(blockchainNetwork.subTitle, 'Subtitle'); + expect(blockchainNetwork.type, BlockchainType.ethereum); + expect(blockchainNetwork.apiKey, 'apikey'); + }); + + test('toJson should convert a BlockchainNetwork instance to JSON', () { + const blockchainNetwork = BlockchainNetwork( + networkname: 'TestNet', + apiUrl: 'https://example.com/api', + rpcNodeUrl: 'https://rpc.example.com', + title: 'Test Network', + subTitle: 'Subtitle', + type: BlockchainType.ethereum, + apiKey: 'apikey', + ); + + final json = blockchainNetwork.toJson(); + + expect(json['networkname'], 'TestNet'); + expect(json['apiUrl'], 'https://example.com/api'); + expect(json['rpcNodeUrl'], 'https://rpc.example.com'); + expect(json['title'], 'Test Network'); + expect(json['subTitle'], 'Subtitle'); + expect(json['type'], 'ethereum'); + expect(json['apiKey'], 'apikey'); + }); + }); +} diff --git a/test/app/shared/models/blockchain_network/tezos_network_test.dart b/test/app/shared/models/blockchain_network/tezos_network_test.dart new file mode 100644 index 000000000..dab97499d --- /dev/null +++ b/test/app/shared/models/blockchain_network/tezos_network_test.dart @@ -0,0 +1,50 @@ +import 'package:altme/app/app.dart'; +import 'package:test/test.dart'; + +void main() { + group('TezosNetwork', () { + test('fromJson should create a TezosNetwork instance from JSON', () { + final json = { + 'networkname': 'TestNet', + 'apiUrl': 'https://example.com/api', + 'rpcNodeUrl': 'https://rpc.example.com', + 'title': 'Test Network', + 'subTitle': 'Subtitle', + 'type': 'tezos', + 'apiKey': 'apikey', + }; + + final tezosNetwork = TezosNetwork.fromJson(json); + + expect(tezosNetwork.networkname, 'TestNet'); + expect(tezosNetwork.apiUrl, 'https://example.com/api'); + expect(tezosNetwork.rpcNodeUrl, 'https://rpc.example.com'); + expect(tezosNetwork.title, 'Test Network'); + expect(tezosNetwork.subTitle, 'Subtitle'); + expect(tezosNetwork.type, BlockchainType.tezos); + expect(tezosNetwork.apiKey, 'apikey'); + }); + + test('toJson should convert a TezosNetwork instance to JSON', () { + const tezosNetwork = TezosNetwork( + networkname: 'TestNet', + apiUrl: 'https://example.com/api', + rpcNodeUrl: 'https://rpc.example.com', + title: 'Test Network', + subTitle: 'Subtitle', + type: BlockchainType.tezos, + apiKey: 'apikey', + ); + + final json = tezosNetwork.toJson(); + + expect(json['networkname'], 'TestNet'); + expect(json['apiUrl'], 'https://example.com/api'); + expect(json['rpcNodeUrl'], 'https://rpc.example.com'); + expect(json['title'], 'Test Network'); + expect(json['subTitle'], 'Subtitle'); + expect(json['type'], 'tezos'); + expect(json['apiKey'], 'apikey'); + }); + }); +} diff --git a/test/app/shared/models/state_message/state_message_test.dart b/test/app/shared/models/state_message/state_message_test.dart new file mode 100644 index 000000000..dfb1db98d --- /dev/null +++ b/test/app/shared/models/state_message/state_message_test.dart @@ -0,0 +1,63 @@ +import 'package:altme/app/app.dart'; +import 'package:test/test.dart'; + +void main() { + group('StateMessage', () { + test('fromJson should create a StateMessage instance from JSON', () { + final json = { + 'type': 'error', + 'stringMessage': 'Error message', + 'showDialog': true, + 'duration': 1600, + }; + + final stateMessage = StateMessage.fromJson(json); + + expect(stateMessage.type, MessageType.error); + expect(stateMessage.stringMessage, 'Error message'); + expect(stateMessage.showDialog, isTrue); + expect(stateMessage.duration, const Duration(microseconds: 1600)); + }); + + test('toJson should convert a StateMessage instance to JSON', () { + const stateMessage = StateMessage( + type: MessageType.warning, + stringMessage: 'Warning message', + showDialog: false, + duration: Duration(microseconds: 2000), + ); + + final json = stateMessage.toJson(); + + expect(json['type'], 'warning'); + expect(json['stringMessage'], 'Warning message'); + expect(json['showDialog'], isFalse); + expect(json['duration'], 2000); + }); + + test('equality should compare StateMessage instances correctly', () { + const stateMessage1 = StateMessage( + type: MessageType.info, + stringMessage: 'Info message', + ); + const stateMessage2 = StateMessage( + type: MessageType.info, + stringMessage: 'Info message', + ); + const stateMessage3 = StateMessage( + type: MessageType.warning, + stringMessage: 'Warning message', + ); + + expect(stateMessage1, equals(stateMessage2)); + expect(stateMessage1, isNot(equals(stateMessage3))); + }); + + test('message types should have correct values', () { + expect(const StateMessage.error().type, MessageType.error); + expect(const StateMessage.warning().type, MessageType.warning); + expect(const StateMessage.info().type, MessageType.info); + expect(const StateMessage.success().type, MessageType.success); + }); + }); +} diff --git a/test/app/shared/models/xtz_data/xtz_data_test.dart b/test/app/shared/models/xtz_data/xtz_data_test.dart new file mode 100644 index 000000000..75cbfa95b --- /dev/null +++ b/test/app/shared/models/xtz_data/xtz_data_test.dart @@ -0,0 +1,50 @@ +import 'package:altme/app/app.dart'; +import 'package:test/test.dart'; + +void main() { + group('XtzData', () { + test('fromJson should create a XtzData instance from JSON', () { + final json = { + 'price': 3.5, + 'price24H': 3.7, + 'marketCap': 1000000000.0, + 'market24H': 900000000.0, + 'volume': 5000000.0, + 'volume24H': 6000000.0, + 'updated': '2023-05-17T15:25:00Z', + }; + + final xtzData = XtzData.fromJson(json); + + expect(xtzData.price, 3.5); + expect(xtzData.price24H, 3.7); + expect(xtzData.marketCap, 1000000000.0); + expect(xtzData.market24H, 900000000.0); + expect(xtzData.volume, 5000000.0); + expect(xtzData.volume24H, 6000000.0); + expect(xtzData.updated, DateTime.utc(2023, 5, 17, 15, 25, 0)); + }); + + test('toJson should convert a XtzData instance to JSON', () { + final xtzData = XtzData( + price: 3.5, + price24H: 3.7, + marketCap: 1000000000, + market24H: 900000000, + volume: 5000000, + volume24H: 6000000, + updated: DateTime.utc(2023, 5, 17, 15, 25, 0), + ); + + final json = xtzData.toJson(); + + expect(json['price'], 3.5); + expect(json['price24H'], 3.7); + expect(json['marketCap'], 1000000000.0); + expect(json['market24H'], 900000000.0); + expect(json['volume'], 5000000.0); + expect(json['volume24H'], 6000000.0); + expect(json['updated'], equals('2023-05-17T15:25:00.000Z')); + }); + }); +} diff --git a/test/app/shared/network/network_exception_test.dart b/test/app/shared/network/network_exception_test.dart index 61e2f9f11..1ea1e3f49 100644 --- a/test/app/shared/network/network_exception_test.dart +++ b/test/app/shared/network/network_exception_test.dart @@ -102,7 +102,10 @@ void main() { ), ); final message = NetworkException.getDioException(error: error); - expect(message.message, NetworkError.NETWORK_ERROR_NOT_FOUND); + expect( + message.message, + NetworkError.NETWORK_ERROR_NO_INTERNET_CONNECTION, + ); }); test('return internalServerError response when statusCode is 500', () { @@ -119,7 +122,7 @@ void main() { final message = NetworkException.getDioException(error: error); expect( message.message, - NetworkError.NETWORK_ERROR_INTERNAL_SERVER_ERROR, + NetworkError.NETWORK_ERROR_UNEXPECTED_ERROR, ); }); @@ -249,7 +252,7 @@ void main() { test('return defaultError response when status code is not from our list', () { final message = NetworkException.handleResponse(410, null); - expect(message.message, NetworkError.NETWORK_ERROR_UNEXPECTED_ERROR); + expect(message.message, NetworkError.NETWORK_ERROR_NOT_READY); }); }); @@ -569,6 +572,37 @@ void main() { NetworkError.NETWORK_ERROR_NOT_FOUND.localise(context), ); }); + + testWidgets( + 'returns NetworkError.NETWORK_ERROR_PRECONDITION_FAILED for ' + 'NetworkException(message: NetworkError.NETWORK_ERROR_PRECONDITION_FAILED)', + (tester) async { + final MessageHandler messageHandler = NetworkException( + message: NetworkError.NETWORK_ERROR_PRECONDITION_FAILED, + ); + await tester.pumpApp(Container()); + final BuildContext context = tester.element(find.byType(Container)); + final String text = messageHandler.getMessage(context, messageHandler); + expect( + text, + NetworkError.NETWORK_ERROR_PRECONDITION_FAILED.localise(context), + ); + }); + + testWidgets( + 'returns NetworkError.NETWORK_ERROR_NOT_READY for ' + 'NetworkException(message: NetworkError.NETWORK_ERROR_NOT_READY)', + (tester) async { + final MessageHandler messageHandler = + NetworkException(message: NetworkError.NETWORK_ERROR_NOT_READY); + await tester.pumpApp(Container()); + final BuildContext context = tester.element(find.byType(Container)); + final String text = messageHandler.getMessage(context, messageHandler); + expect( + text, + NetworkError.NETWORK_ERROR_NOT_READY.localise(context), + ); + }); }); }); } diff --git a/test/app/shared/network/network_test.dart b/test/app/shared/network/network_test.dart index ae1ec7265..616ec5c15 100644 --- a/test/app/shared/network/network_test.dart +++ b/test/app/shared/network/network_test.dart @@ -1,105 +1,110 @@ -// import 'dart:convert'; - -// import 'package:altme/app/app.dart'; -// import 'package:dio/dio.dart'; -// import 'package:flutter_test/flutter_test.dart'; -// import 'package:http_mock_adapter/http_mock_adapter.dart'; - -// import 'test_constants.dart'; - -// void main() { -// group('DioClient Class', () { -// // test('can be instantiated', () { -// // expect(getDioClient(baseUrl: baseUrl), isNotNull); -// // }); - -// group('interceptors', () { -// final dio = Dio(BaseOptions(baseUrl: baseUrl)); -// final dioAdapter = DioAdapter(dio: Dio(BaseOptions(baseUrl: baseUrl))); -// dio.httpClientAdapter = dioAdapter; -// // final interceptor = DioInterceptor(dio: dio); -// //final service = DioClient(baseUrl, dio, interceptors: [interceptor]); - -// // test('set interceptors', () { -// // expect(service.interceptors?.length, greaterThan(0)); -// // }); -// }); - -// group('exceptions', () { -// final dio = Dio(BaseOptions(baseUrl: 'http://no.domain.com')); -// final service = DioClient('http://no.domain.com', dio); -// test('socket exception in get method', () async { -// try { -// await service.get('/path'); -// } catch (e) { -// if (e is NetworkException) { -// expect( -// e.message, -// NetworkError.NETWORK_ERROR_NO_INTERNET_CONNECTION, -// ); -// } -// } -// }); - -// test('socket exception in post method', () async { -// try { -// await service.post('/path'); -// } catch (e) { -// if (e is NetworkException) { -// expect( -// e.message, -// NetworkError.NETWORK_ERROR_NO_INTERNET_CONNECTION, -// ); -// } -// } -// }); -// }); - -// group('Get Method', () { -// final dio = Dio(BaseOptions(baseUrl: baseUrl)); -// final dioAdapter = DioAdapter(dio: Dio(BaseOptions(baseUrl: baseUrl))); -// dio.httpClientAdapter = dioAdapter; -// final service = DioClient(baseUrl, dio); - -// test('Get Method Success test', () async { -// dioAdapter.onGet( -// baseUrl + testPath, -// (request) { -// return request.reply(200, successMessage); -// }, -// ); - -// final dynamic response = await service.get(baseUrl + testPath); - -// expect(response, successMessage); -// }); -// }); - -// group('Post Method', () { -// final dio = Dio(BaseOptions(baseUrl: baseUrl)); -// final dioAdapter = DioAdapter(dio: Dio(BaseOptions(baseUrl: baseUrl))); -// dio.httpClientAdapter = dioAdapter; -// final service = DioClient(baseUrl, dio); - -// test('Post Method Success test', () async { -// dioAdapter.onPost( -// baseUrl + testPath, -// (request) { -// return request.reply(201, successMessage); -// }, -// data: json.encode(testData), -// queryParameters: {}, -// headers: header, -// ); - -// final dynamic response = await service.post( -// baseUrl + testPath, -// data: json.encode(testData), -// options: Options(headers: header), -// ); - -// expect(response, successMessage); -// }); -// }); -// }); -// } +import 'dart:convert'; + +import 'package:altme/app/app.dart'; +import 'package:dio/dio.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:http_mock_adapter/http_mock_adapter.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:secure_storage/secure_storage.dart'; + +import 'test_constants.dart'; + +class MockSecureStorageProvider extends Mock implements SecureStorageProvider {} + +class MockDio extends Mock implements Dio {} + +void main() { + late MockSecureStorageProvider mockSecureStorageProvider; + + final client = Dio(); + + late DioAdapter dioAdapter; + late DioClient service; + + setUp(() { + dioAdapter = + DioAdapter(dio: Dio(BaseOptions()), matcher: const UrlRequestMatcher()); + client.httpClientAdapter = dioAdapter; + mockSecureStorageProvider = MockSecureStorageProvider(); + service = DioClient( + baseUrl: baseUrl, + secureStorageProvider: mockSecureStorageProvider, + dio: client, + ); + }); + + group('DioClient Class', () { + test('can be instantiated', () { + expect(service, isNotNull); + }); + + test('set interceptors', () { + expect(service.dio.interceptors.length, greaterThan(0)); + }); + + group('exceptions', () { + test('socket exception in get method', () async { + try { + await service.get('/path'); + } catch (e) { + if (e is NetworkException) { + expect( + e.message, + NetworkError.NETWORK_ERROR_NO_INTERNET_CONNECTION, + ); + } + } + }); + + test('socket exception in post method', () async { + try { + await service.post('/path'); + } catch (e) { + if (e is NetworkException) { + expect( + e.message, + NetworkError.NETWORK_ERROR_NO_INTERNET_CONNECTION, + ); + } + } + }); + }); + + group('Get Method', () { + test('Get Method Success test', () async { + dioAdapter.onGet( + baseUrl + testPath, + (request) { + return request.reply(200, successMessage); + }, + ); + + final dynamic response = await service.get(baseUrl + testPath); + + expect(response, successMessage); + }); + }); + + group('Post Method', () { + test('Post Method Success test', () async { + dioAdapter.onPost( + baseUrl + testPath, + (request) { + return request.reply(201, successMessage); + }, + data: json.encode(testData), + queryParameters: {}, + headers: header, + ); + + final dynamic response = await service.post( + baseUrl + testPath, + data: json.encode(testData), + options: Options(headers: header), + ); + + expect(response, successMessage); + }); + }); + }); +} diff --git a/test/app/shared/network/test_constants.dart b/test/app/shared/network/test_constants.dart index 64ec8405b..5a0265582 100644 --- a/test/app/shared/network/test_constants.dart +++ b/test/app/shared/network/test_constants.dart @@ -2,7 +2,7 @@ const baseUrl = 'https://example.com/'; const successMessage = {'response': 'Success'}; const errorMessage = {'response': 'message'}; -const testPath = 'test'; +const testPath = '/test'; const testData = {'data': 'sample data'}; const header = { 'Content-Type': 'application/json', diff --git a/test/app/shared/translation/translation_test.dart b/test/app/shared/translation/translation_test.dart new file mode 100644 index 000000000..410ddf6ec --- /dev/null +++ b/test/app/shared/translation/translation_test.dart @@ -0,0 +1,51 @@ +import 'package:altme/app/app.dart'; +import 'package:altme/l10n/l10n.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; + +class MockAppLocalizations extends Mock implements AppLocalizations {} + +void main() { + group('GetTranslation', () { + late AppLocalizations l10n; + setUp(() { + l10n = MockAppLocalizations(); + }); + + test('returns translation for the locale if available', () { + final translations = [ + Translation('en', 'Hello'), + Translation('es', 'Hola'), + ]; + when(() => l10n.localeName).thenReturn('es'); + final result = GetTranslation.getTranslation(translations, l10n); + expect(result, 'Hola'); + }); + + test('falls back to English if locale translation is not available', () { + final translations = [ + Translation('en', 'Hello'), + Translation('es', 'Hola'), + ]; + when(() => l10n.localeName).thenReturn('fr'); + final result = GetTranslation.getTranslation(translations, l10n); + expect(result, 'Hello'); + }); + + test('returns empty string if no translations are available', () { + final translations = [ + Translation('fr', 'Bonjour'), + ]; + when(() => l10n.localeName).thenReturn('es'); + final result = GetTranslation.getTranslation(translations, l10n); + expect(result, ''); + }); + + test('returns empty string if translations list is empty', () { + final translations = []; + when(() => l10n.localeName).thenReturn('en'); + final result = GetTranslation.getTranslation(translations, l10n); + expect(result, ''); + }); + }); +} diff --git a/test/app/shared/validators/wallet_address_validator_test.dart b/test/app/shared/validators/wallet_address_validator_test.dart new file mode 100644 index 000000000..a55817624 --- /dev/null +++ b/test/app/shared/validators/wallet_address_validator_test.dart @@ -0,0 +1,50 @@ +import 'package:altme/app/app.dart'; +import 'package:flutter_test/flutter_test.dart'; + +class WalletAddressValidatorImpl with WalletAddressValidator {} + +void main() { + group('WalletAddressValidator', () { + late WalletAddressValidatorImpl validator; + + setUp(() { + validator = WalletAddressValidatorImpl(); + }); + + test('returns false for null address', () { + expect(validator.validateWalletAddress(null), isFalse); + }); + + test('returns false for empty address', () { + expect(validator.validateWalletAddress(''), isFalse); + }); + + test('returns true for valid Ethereum address', () { + expect( + validator.validateWalletAddress( + '0x742d35Cc6634C0532925a3b844Bc454e4438f44e', + ), + isTrue, + ); + }); + + test('returns false for invalid Ethereum address', () { + expect(validator.validateWalletAddress('0x123'), isFalse); + }); + + test('returns true for valid Tezos address', () { + expect( + validator.validateWalletAddress('tz1VSUr8wwNhLAzempoch5d6hLRiTh8Cjcjb'), + isTrue, + ); + }); + + test('returns false for invalid Tezos address', () { + expect(validator.validateWalletAddress('tz1short'), isFalse); + }); + + test('returns false for unsupported blockchain address', () { + expect(validator.validateWalletAddress('abc123'), isFalse); + }); + }); +} diff --git a/test/app/shared/widget/add_account/add_account_button_test.dart b/test/app/shared/widget/add_account/add_account_button_test.dart new file mode 100644 index 000000000..c54d24142 --- /dev/null +++ b/test/app/shared/widget/add_account/add_account_button_test.dart @@ -0,0 +1,52 @@ +import 'package:altme/app/app.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../../../../helpers/helpers.dart'; + +void main() { + group('AddAccountButton', () { + testWidgets('renders correctly', (tester) async { + await tester.pumpApp( + Scaffold( + body: AddAccountButton(onPressed: () {}), + ), + ); + + expect(find.byType(InkWell), findsOneWidget); + expect(find.byType(Container), findsOneWidget); + expect(find.byType(Column), findsOneWidget); + expect(find.byType(Row), findsOneWidget); + expect(find.byType(Image), findsOneWidget); + expect(find.byType(Text), findsNWidgets(2)); + }); + + testWidgets('displays correct text', (tester) async { + await tester.pumpApp( + Scaffold( + body: AddAccountButton(onPressed: () {}), + ), + ); + + expect(find.text('Add Account'), findsOneWidget); + expect(find.text('Create or import a new account.'), findsOneWidget); + }); + + testWidgets('triggers onPressed callback when tapped', (tester) async { + var click = false; + + await tester.pumpApp( + Scaffold( + body: AddAccountButton( + onPressed: () { + click = true; + }, + ), + ), + ); + + await tester.tap(find.byType(InkWell)); + expect(click, true); + }); + }); +} diff --git a/test/app/shared/widget/back_leading_button_test.dart b/test/app/shared/widget/back_leading_button_test.dart index 9d4440f4b..d007827aa 100644 --- a/test/app/shared/widget/back_leading_button_test.dart +++ b/test/app/shared/widget/back_leading_button_test.dart @@ -1,24 +1,25 @@ import 'package:altme/app/app.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mockingjay/mockingjay.dart'; import '../../../helpers/helpers.dart'; void main() { group('BackLeadingButton', () { testWidgets('pops when IconButton is tapped', (tester) async { - final MockNavigator navigator = MockNavigator(); + bool popTriggered = false; + await tester.pumpApp( - MockNavigatorProvider( - navigator: navigator, - child: const Material( - child: BackLeadingButton(), - ), + BackLeadingButton( + onPressed: () { + popTriggered = true; + }, ), ); await tester.tap(find.byType(IconButton)); - verify(navigator.pop).called(1); + await tester.pump(); + + expect(popTriggered, isTrue); }); }); } diff --git a/test/app/shared/widget/base/box_decoration_test.dart b/test/app/shared/widget/base/box_decoration_test.dart index cb24e7fa6..e02fd1775 100644 --- a/test/app/shared/widget/base/box_decoration_test.dart +++ b/test/app/shared/widget/base/box_decoration_test.dart @@ -175,8 +175,7 @@ void main() { }); testWidgets('hashCode function', (WidgetTester tester) async { - const hashCodeWithDefaultConstructor = 81498275; - expect(decoration.hashCode, hashCodeWithDefaultConstructor); + expect(decoration.hashCode, isA()); }); testWidgets('operator function', (WidgetTester tester) async { diff --git a/test/app/shared/widget/base/illustration_page_test.dart b/test/app/shared/widget/base/illustration_page_test.dart index 7aa16371a..ea5380ff6 100644 --- a/test/app/shared/widget/base/illustration_page_test.dart +++ b/test/app/shared/widget/base/illustration_page_test.dart @@ -1,3 +1,5 @@ +import 'dart:typed_data'; + import 'package:altme/app/app.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; @@ -24,6 +26,11 @@ class FakeAssetBundle extends Fake implements AssetBundle { '''; + @override + Future load(String key) async { + return ByteData.sublistView(Uint8List.fromList(svgStr.codeUnits)); + } + @override Future loadString(String key, {bool cache = true}) async { return svgStr; @@ -41,27 +48,27 @@ void main() { description: 'description', backgroundColor: Colors.blueGrey, onPressed: () {}, + key: GlobalKey(), ), ), ); } group('IllustrationPage widget', () { - testWidgets('all sub widgets founded', (WidgetTester tester) async { - await tester.pumpWidget( - makeTestableWidget(), - ); + testWidgets('all sub widgets found', (WidgetTester tester) async { + await tester.pumpWidget(makeTestableWidget()); + await tester.pumpAndSettle(); + expect(find.byType(BaseIllustrationPage), findsOneWidget); expect(find.byType(BasePage), findsOneWidget); - expect(find.byType(MyElevatedButton), findsOneWidget); + expect(find.byType(MyOutlinedButton), findsOneWidget); expect(find.byType(SvgPicture), findsOneWidget); }); testWidgets('verify property of widget set correctly', (WidgetTester tester) async { - await tester.pumpWidget( - makeTestableWidget(), - ); + await tester.pumpWidget(makeTestableWidget()); + await tester.pumpAndSettle(); expect(find.byType(BaseIllustrationPage), findsOneWidget); final baseIllustrationPage = tester .widget(find.byType(BaseIllustrationPage)); diff --git a/test/app/shared/widget/base/markdown_page_test.dart b/test/app/shared/widget/base/markdown_page_test.dart index 067f70fa1..1c034b907 100644 --- a/test/app/shared/widget/base/markdown_page_test.dart +++ b/test/app/shared/widget/base/markdown_page_test.dart @@ -1,10 +1,5 @@ -import 'dart:convert'; -import 'dart:typed_data'; - import 'package:altme/app/app.dart'; -import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart' as services; import 'package:flutter_markdown/flutter_markdown.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -13,95 +8,39 @@ void main() { return MaterialApp( home: MarkdownPage( title: 'title', - file: 'assets/notices.md', + file: 'assets/notices/notices_en.md', key: GlobalKey(), ), ); } - bool isFirstCase = true; - - setUpAll(() { - ///do mock the assets - TestWidgetsFlutterBinding.ensureInitialized(); - services.ServicesBinding.instance.defaultBinaryMessenger - .setMockMessageHandler('flutter/assets', (message) { - if (isFirstCase) { - final Uint8List encoded = - utf8.encoder.convert('[Link `with nested code` Text](href)'); - return Future.value(encoded.buffer.asByteData()); - } else { - return null; - } - }); - }); - - group('MarkdownPage widget', () { - late Widget widget; - - setUp(() { - widget = makeTestableWidget(); - }); - - testWidgets('verify property set correctly', (WidgetTester tester) async { - await tester.pumpWidget(widget); - expect(find.byType(MarkdownPage), findsOneWidget); - final markdownWidget = - tester.widget(find.byType(MarkdownPage)); - expect(markdownWidget.file, 'assets/notices.md'); - expect(markdownWidget.title, 'title'); - expect(markdownWidget.key, isA()); - }); - - testWidgets('sub widget founded', (WidgetTester tester) async { - await tester.pumpWidget(widget); - expect(find.byType(MarkdownPage), findsOneWidget); - expect(find.byType(BasePage), findsOneWidget); - expect(find.byType(BackLeadingButton), findsOneWidget); - expect(find.byType(Spinner), findsOneWidget); - await tester.pumpAndSettle(const Duration(seconds: 1)); - expect(find.byType(Markdown), findsOneWidget); - expect(find.byType(Spinner), findsNothing); - }); + testWidgets('MarkdownPage should load and display markdown content', + (WidgetTester tester) async { + await tester.pumpWidget(makeTestableWidget()); - testWidgets('tap on markdown widget', (WidgetTester tester) async { - await tester.pumpWidget(widget); - await tester.pumpAndSettle(); - expect(find.byType(Markdown), findsOneWidget); - final richText = tester.widget(find.byType(RichText).first); - final span = richText.text as TextSpan; + expect(find.byType(Spinner), findsOneWidget); - final gestureRecognizerTypes = []; - span.visitChildren((InlineSpan inlineSpan) { - if (inlineSpan is TextSpan) { - final recognizer = inlineSpan.recognizer; - gestureRecognizerTypes.add(recognizer?.runtimeType ?? Null); - if (recognizer != null && recognizer is TapGestureRecognizer) { - recognizer.onTap!(); - } - } - return true; - }); - - expect(span.children!.length, 3); - expect(gestureRecognizerTypes.length, 3); - expect(gestureRecognizerTypes, everyElement(TapGestureRecognizer)); - await tester.pumpAndSettle(); - }); + await tester.pumpAndSettle(); + expect(find.byType(Markdown), findsOneWidget); + expect( + find.text( + 'The Altme wallet is an open source project under Apache 2.0 licence', + ), + findsOneWidget, + ); }); - group('MarkdownPage bad state work properly', () { - final widget = MaterialApp( - home: MarkdownPage( - title: 'title', - file: 'assets1/notices.md', + testWidgets('MarkdownPage should handle loading errors', + (WidgetTester tester) async { + await tester.pumpWidget( + MaterialApp( + home: MarkdownPage(title: 'Test', file: 'invalid_file.md'), ), ); - testWidgets('trigger message ', (WidgetTester tester) async { - isFirstCase = false; - await tester.pumpWidget(widget); - await tester.pumpAndSettle(); - }); + expect(find.byType(Spinner), findsOneWidget); + + await tester.pumpAndSettle(); + expect(find.byType(Markdown), findsNothing); }); } diff --git a/test/app/shared/widget/base/otp_textfield_test.dart b/test/app/shared/widget/base/otp_textfield_test.dart new file mode 100644 index 000000000..34fca0ee4 --- /dev/null +++ b/test/app/shared/widget/base/otp_textfield_test.dart @@ -0,0 +1,99 @@ +import 'package:altme/app/app.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../../../../helpers/helpers.dart'; + +void main() { + group('OtpTextField', () { + late List controllers; + + setUp(() { + controllers = List.generate( + 6, + (index) => TextEditingController( + text: '', + ), + ); + }); + + testWidgets('renders correctly', (tester) async { + await tester.pumpApp( + Scaffold( + body: OtpTextField( + controllers: controllers, + index: 0, + autoFocus: false, + ), + ), + ); + + expect(find.byType(TextField), findsOneWidget); + }); + + testWidgets('autofocus works correctly', (tester) async { + await tester.pumpApp( + Scaffold( + body: OtpTextField( + controllers: controllers, + index: 0, + autoFocus: true, + ), + ), + ); + + await tester.pump(); + + final finder = find.byType(TextField); + expect(finder, findsOneWidget); + final textField = tester.widget(finder); + expect(textField.autofocus, true); + }); + + testWidgets('moves to next field when a digit is entered', (tester) async { + await tester.pumpApp( + Scaffold( + body: Row( + children: List.generate(6, (index) { + return OtpTextField( + controllers: controllers, + index: index, + autoFocus: index == 0, + ); + }), + ), + ), + ); + + await tester.enterText(find.byType(TextField).at(1), '1'); + await tester.pump(); + + expect(controllers[1].text, '1'); + expect( + FocusScope.of(tester.element(find.byType(TextField).at(1))).hasFocus, + true, + ); + }); + + testWidgets('onTap moves cursor to end of text', (tester) async { + await tester.pumpApp( + Scaffold( + body: OtpTextField( + controllers: controllers, + index: 0, + autoFocus: true, + ), + ), + ); + await tester.tap(find.byType(TextField)); + await tester.pump(); + + expect( + controllers[0].selection, + TextSelection.fromPosition( + TextPosition(offset: controllers[0].text.length), + ), + ); + }); + }); +} diff --git a/test/app/shared/widget/base/page_test.dart b/test/app/shared/widget/base/page_test.dart index 45491dc0c..f41f5c81c 100644 --- a/test/app/shared/widget/base/page_test.dart +++ b/test/app/shared/widget/base/page_test.dart @@ -7,18 +7,48 @@ import '../../../../helpers/helpers.dart'; void main() { group('BasePage', () { group('CustomAppBar', () { - testWidgets('does not renders CustomAppBar when title is null', - (tester) async { - await tester.pumpApp(BasePage(body: Container(), title: '')); + testWidgets( + 'does not renders CustomAppBar when title, titleTrailing and' + ' titleLeading are null', (tester) async { + await tester.pumpApp( + BasePage( + body: Container(), + title: null, + titleTrailing: null, + titleLeading: null, + ), + ); expect(find.byType(CustomAppBar), findsNothing); }); - testWidgets('does not renders CustomAppBar when title is empty', - (tester) async { - await tester.pumpApp(BasePage(body: Container(), title: '')); + testWidgets( + 'does not renders CustomAppBar when title, titleTrailing and' + ' titleLeading are null and secure screen is true', (tester) async { + await tester.pumpApp( + BasePage( + body: Container(), + title: null, + titleTrailing: null, + titleLeading: null, + secureScreen: true, + ), + ); expect(find.byType(CustomAppBar), findsNothing); }); + testWidgets( + 'renders CustomAppBar when title is provided ' + 'and secure screen is true', (tester) async { + await tester.pumpApp( + BasePage( + body: Container(), + title: 'I am title', + secureScreen: true, + ), + ); + expect(find.byType(CustomAppBar), findsOneWidget); + }); + testWidgets('renders CustomAppBar when title is provided', (tester) async { await tester.pumpApp(BasePage(body: Container(), title: 'I am title')); @@ -46,15 +76,151 @@ void main() { }); group('useSafeArea', () { - testWidgets('renders SafeArea when useSafeArea is true', (tester) async { - await tester.pumpApp(BasePage(body: Container(), useSafeArea: true)); - expect(find.byType(SafeArea), findsOneWidget); + group('renders SafeArea correctly when useSafeArea is true ', () { + testWidgets('navigation is provided', (tester) async { + await tester.pumpApp( + BasePage( + body: Container(), + useSafeArea: true, + navigation: Container(), + ), + ); + expect(find.byType(SafeArea), findsNWidgets(2)); + }); + + testWidgets('scrollView is true', (tester) async { + await tester.pumpApp( + BasePage( + body: Container(), + useSafeArea: true, + scrollView: true, + ), + ); + expect(find.byType(SafeArea), findsOneWidget); + }); + + testWidgets('scrollView is false', (tester) async { + await tester.pumpApp( + BasePage( + body: Container(), + useSafeArea: true, + scrollView: false, + ), + ); + expect(find.byType(SafeArea), findsOneWidget); + }); + testWidgets('navigation is provided and secure screen is true', + (tester) async { + await tester.pumpApp( + BasePage( + body: Container(), + useSafeArea: true, + secureScreen: true, + navigation: Container(), + ), + ); + expect(find.byType(SafeArea), findsNWidgets(2)); + }); + + testWidgets('scrollView is true and secure screen is true', + (tester) async { + await tester.pumpApp( + BasePage( + body: Container(), + useSafeArea: true, + secureScreen: true, + scrollView: true, + ), + ); + expect(find.byType(SafeArea), findsOneWidget); + }); + + testWidgets('scrollView is false and secure screen is true', + (tester) async { + await tester.pumpApp( + BasePage( + body: Container(), + useSafeArea: true, + scrollView: false, + secureScreen: true, + ), + ); + expect(find.byType(SafeArea), findsOneWidget); + }); }); - testWidgets('does not renders SafeArea when useSafeArea is false', - (tester) async { - await tester.pumpApp(BasePage(body: Container(), useSafeArea: false)); - expect(find.byType(SafeArea), findsNothing); + group('renders SafeArea correctly when useSafeArea is false ', () { + testWidgets(' navigation is provided', (tester) async { + await tester.pumpApp( + BasePage( + body: Container(), + useSafeArea: false, + navigation: Container(), + ), + ); + expect(find.byType(SafeArea), findsNothing); + }); + + testWidgets('scrollView is true', (tester) async { + await tester.pumpApp( + BasePage( + body: Container(), + useSafeArea: false, + scrollView: true, + ), + ); + expect(find.byType(SafeArea), findsNothing); + }); + + testWidgets('scrollView is false', (tester) async { + await tester.pumpApp( + BasePage( + body: Container(), + useSafeArea: false, + scrollView: false, + ), + ); + expect(find.byType(SafeArea), findsNothing); + }); + + testWidgets('navigation is provided and secure screen is true', + (tester) async { + await tester.pumpApp( + BasePage( + body: Container(), + useSafeArea: false, + secureScreen: true, + navigation: Container(), + ), + ); + expect(find.byType(SafeArea), findsNothing); + }); + + testWidgets('scrollView is true and secure screen is true', + (tester) async { + await tester.pumpApp( + BasePage( + body: Container(), + useSafeArea: false, + secureScreen: true, + scrollView: true, + ), + ); + expect(find.byType(SafeArea), findsNothing); + }); + + testWidgets('scrollView is false and secure screen is true', + (tester) async { + await tester.pumpApp( + BasePage( + body: Container(), + useSafeArea: false, + scrollView: false, + secureScreen: true, + ), + ); + expect(find.byType(SafeArea), findsNothing); + }); }); }); }); diff --git a/test/app/shared/widget/base/text_field_test.dart b/test/app/shared/widget/base/text_field_test.dart new file mode 100644 index 000000000..7cb47c1c9 --- /dev/null +++ b/test/app/shared/widget/base/text_field_test.dart @@ -0,0 +1,126 @@ +import 'package:altme/app/app.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../../../../helpers/helpers.dart'; + +void main() { + group('BaseTextField Widget Tests', () { + late TextEditingController controller; + + setUp(() { + controller = TextEditingController(); + }); + + testWidgets('renders BaseTextField with default properties', + (WidgetTester tester) async { + await tester.pumpApp( + Scaffold( + body: BaseTextField( + controller: controller, + ), + ), + ); + + expect(find.byType(TextFormField), findsOneWidget); + expect(find.byType(SizedBox), findsNWidgets(2)); + }); + + testWidgets('renders BaseTextField with label', + (WidgetTester tester) async { + const label = 'Test Label'; + + await tester.pumpApp( + Scaffold( + body: BaseTextField( + controller: controller, + label: label, + ), + ), + ); + + expect(find.text(label), findsOneWidget); + }); + + testWidgets('renders BaseTextField with hint', (WidgetTester tester) async { + const hint = 'Test Hint'; + + await tester.pumpApp( + Scaffold( + body: BaseTextField( + controller: controller, + hint: hint, + ), + ), + ); + + expect(find.text(hint), findsOneWidget); + }); + + testWidgets('renders BaseTextField with error text', + (WidgetTester tester) async { + const error = 'Test Error'; + + await tester.pumpApp( + Scaffold( + body: BaseTextField( + controller: controller, + error: error, + ), + ), + ); + + expect(find.text(error), findsOneWidget); + }); + + testWidgets('renders BaseTextField with prefix and suffix icons', + (WidgetTester tester) async { + const prefixIcon = Icon(Icons.email); + const suffixIcon = Icon(Icons.visibility); + + await tester.pumpApp( + Scaffold( + body: BaseTextField( + controller: controller, + prefixIcon: prefixIcon, + suffixIcon: suffixIcon, + ), + ), + ); + + expect(find.byIcon(Icons.email), findsOneWidget); + expect(find.byIcon(Icons.visibility), findsOneWidget); + }); + + testWidgets('input text updates the controller', + (WidgetTester tester) async { + const inputText = 'Hello, Flutter!'; + + await tester.pumpApp( + Scaffold( + body: BaseTextField( + controller: controller, + ), + ), + ); + + await tester.enterText(find.byType(TextFormField), inputText); + expect(controller.text, inputText); + }); + + testWidgets('obscureText property works correctly', + (WidgetTester tester) async { + await tester.pumpApp( + Scaffold( + body: BaseTextField( + controller: controller, + obscureText: true, + ), + ), + ); + + final TextField textField = tester.widget(find.byType(TextField)); + expect(textField.obscureText, isTrue); + }); + }); +} diff --git a/test/app/shared/widget/copy_button_test.dart b/test/app/shared/widget/copy_button_test.dart new file mode 100644 index 000000000..ee810631c --- /dev/null +++ b/test/app/shared/widget/copy_button_test.dart @@ -0,0 +1,32 @@ +import 'package:altme/app/app.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../../../helpers/helpers.dart'; + +void main() { + group('CopyButton', () { + testWidgets('CopyButton displays correctly', (tester) async { + await tester.pumpApp(const Scaffold(body: CopyButton())); + await tester.pumpAndSettle(); + expect(find.byType(Text), findsOneWidget); + expect(find.text('Copy'), findsOneWidget); + }); + + testWidgets('CopyButton responds to tap correctly', (tester) async { + bool triggerred = false; + await tester.pumpApp( + Scaffold( + body: CopyButton( + onTap: () { + triggerred = true; + }, + ), + ), + ); + await tester.tap(find.byType(InkWell)); + await tester.pumpAndSettle(); + expect(triggerred, isTrue); + }); + }); +} diff --git a/test/app/shared/widget/custom_listtile_card_test.dart b/test/app/shared/widget/custom_listtile_card_test.dart new file mode 100644 index 000000000..11a91f2df --- /dev/null +++ b/test/app/shared/widget/custom_listtile_card_test.dart @@ -0,0 +1,55 @@ +import 'package:altme/app/app.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + testWidgets('CustomListTileCard displays correctly and responds to tap', + (WidgetTester tester) async { + bool triggerred = false; + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: CustomListTileCard( + title: 'Test Title', + subTitle: 'Test Subtitle', + imageAssetPath: 'assets/launcher_icon.png', + recommended: true, + onTap: () { + triggerred = true; + }, + ), + ), + ), + ); + + expect(find.text('Test Title'), findsOneWidget); + expect(find.text('Test Subtitle'), findsOneWidget); + expect(find.byType(Image), findsOneWidget); + expect(find.byType(Icon), findsOneWidget); + + expect(find.byIcon(Icons.thumb_up), findsOneWidget); + + await tester.tap(find.byType(ListTile)); + expect(triggerred, isTrue); + }); + + testWidgets( + 'CustomListTileCard does not display recommended icon when' + ' recommended is false', (WidgetTester tester) async { + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: CustomListTileCard( + title: 'Test Title', + subTitle: 'Test Subtitle', + imageAssetPath: 'assets/launcher_icon.png', + recommended: false, + onTap: () {}, + ), + ), + ), + ); + + expect(find.byIcon(Icons.thumb_up), findsNothing); + }); +} diff --git a/test/app/shared/widget/default_dialog_test.dart b/test/app/shared/widget/default_dialog_test.dart new file mode 100644 index 000000000..b2081c46d --- /dev/null +++ b/test/app/shared/widget/default_dialog_test.dart @@ -0,0 +1,53 @@ +import 'package:altme/app/app.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../../../helpers/helpers.dart'; + +void main() { + testWidgets('DefaultDialog displays title and description', + (WidgetTester tester) async { + const title = 'Test Title'; + const description = 'Test Description'; + + await tester.pumpApp( + const Scaffold( + body: DefaultDialog( + title: title, + description: description, + ), + ), + ); + + expect(find.text(title), findsOneWidget); + expect(find.text(description), findsOneWidget); + }); + + testWidgets( + 'DefaultDialog displays button with label and handles button click', + (WidgetTester tester) async { + bool triggerred = false; + const title = 'Test Title'; + const description = 'Test Description'; + const buttonLabel = 'Button'; + + await tester.pumpApp( + Scaffold( + body: DefaultDialog( + title: title, + description: description, + buttonLabel: buttonLabel, + onButtonClick: () { + triggerred = true; + }, + ), + ), + ); + + expect(find.byType(MyElevatedButton), findsOneWidget); + + await tester.tap(find.byType(MyElevatedButton)); + await tester.pumpAndSettle(); + expect(triggerred, isTrue); + }); +} diff --git a/test/app/shared/widget/dialog/becareful_dialog_test.dart b/test/app/shared/widget/dialog/becareful_dialog_test.dart new file mode 100644 index 000000000..1afd7dc66 --- /dev/null +++ b/test/app/shared/widget/dialog/becareful_dialog_test.dart @@ -0,0 +1,140 @@ +import 'package:altme/app/app.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../../../../helpers/helpers.dart'; + +void main() { + group('BeCarefulDialog Widget Tests', () { + testWidgets('renders BeCarefulDialog with title', + (WidgetTester tester) async { + const title = 'Test Title'; + + await tester.pumpApp( + const Scaffold( + body: BeCarefulDialog( + title: title, + ), + ), + ); + + expect(find.text(title), findsOneWidget); + }); + + testWidgets('renders BeCarefulDialog with subtitle', + (WidgetTester tester) async { + const title = 'Test Title'; + const subtitle = 'Test Subtitle'; + + await tester.pumpApp( + const Scaffold( + body: BeCarefulDialog( + title: title, + subtitle: subtitle, + ), + ), + ); + + expect(find.text(title), findsOneWidget); + expect(find.text(subtitle), findsOneWidget); + }); + + testWidgets('renders BeCarefulDialog with yes and no buttons', + (WidgetTester tester) async { + const title = 'Test Title'; + const yesText = 'Yes'; + const noText = 'No'; + + await tester.pumpApp( + const Scaffold( + body: BeCarefulDialog( + title: title, + yes: yesText, + no: noText, + ), + ), + ); + + expect(find.text(yesText.toUpperCase()), findsOneWidget); + expect(find.text(noText.toUpperCase()), findsOneWidget); + }); + + testWidgets('clicking no button closes the dialog', + (WidgetTester tester) async { + const title = 'Test Title'; + const noText = 'No'; + + await tester.pumpApp( + Scaffold( + body: Builder( + builder: (context) { + return ElevatedButton( + onPressed: () { + BeCarefulDialog.show( + context: context, + title: title, + no: noText, + ); + }, + child: const Text('Show Dialog'), + ); + }, + ), + ), + ); + + await tester.tap(find.text('Show Dialog')); + await tester.pumpAndSettle(); + + expect(find.text(title), findsOneWidget); + expect(find.text(noText.toUpperCase()), findsOneWidget); + + await tester.tap(find.text(noText.toUpperCase())); + await tester.pumpAndSettle(); + + expect(find.text(title), findsNothing); + }); + + testWidgets( + 'clicking yes button triggers onContinueClick and closes the dialog', + (WidgetTester tester) async { + const title = 'Test Title'; + const yesText = 'Yes'; + bool onContinueClicked = false; + + await tester.pumpApp( + Scaffold( + body: Builder( + builder: (context) { + return ElevatedButton( + onPressed: () { + BeCarefulDialog.show( + context: context, + title: title, + yes: yesText, + onContinueClick: () { + onContinueClicked = true; + }, + ); + }, + child: const Text('Show Dialog'), + ); + }, + ), + ), + ); + + await tester.tap(find.text('Show Dialog')); + await tester.pumpAndSettle(); + + expect(find.text(title), findsOneWidget); + expect(find.text(yesText.toUpperCase()), findsOneWidget); + + await tester.tap(find.text(yesText.toUpperCase())); + await tester.pumpAndSettle(); + + expect(find.text(title), findsNothing); + expect(onContinueClicked, isTrue); + }); + }); +} diff --git a/test/app/shared/widget/dialog/confirm_dialog_test.dart b/test/app/shared/widget/dialog/confirm_dialog_test.dart new file mode 100644 index 000000000..5cf963e05 --- /dev/null +++ b/test/app/shared/widget/dialog/confirm_dialog_test.dart @@ -0,0 +1,152 @@ +import 'package:altme/app/app.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../../../../helpers/helpers.dart'; + +void main() { + group('ConfirmDialog Widget Tests', () { + testWidgets('renders ConfirmDialog with title', + (WidgetTester tester) async { + const title = 'Test Title'; + + await tester.pumpApp( + const ConfirmDialog( + title: title, + ), + ); + + expect(find.text(title), findsOneWidget); + }); + + testWidgets('renders ConfirmDialog with subtitle', + (WidgetTester tester) async { + const title = 'Test Title'; + const subtitle = 'Test Subtitle'; + + await tester.pumpApp( + const ConfirmDialog( + title: title, + subtitle: subtitle, + ), + ); + + expect(find.text(title), findsOneWidget); + expect(find.text(subtitle), findsOneWidget); + }); + + testWidgets('renders ConfirmDialog with yes and no buttons', + (WidgetTester tester) async { + const title = 'Test Title'; + const yesText = 'Yes'; + const noText = 'No'; + + await tester.pumpApp( + const ConfirmDialog( + title: title, + yes: yesText, + no: noText, + ), + ); + + expect(find.text(yesText.toUpperCase()), findsOneWidget); + expect(find.text(noText.toUpperCase()), findsOneWidget); + }); + + testWidgets('clicking no button closes the dialog with false', + (WidgetTester tester) async { + const title = 'Test Title'; + const noText = 'No'; + + await tester.pumpApp( + Builder( + builder: (context) { + return ElevatedButton( + onPressed: () { + showDialog( + context: context, + builder: (_) => const ConfirmDialog( + title: title, + no: noText, + ), + ); + }, + child: const Text('Show Dialog'), + ); + }, + ), + ); + + await tester.tap(find.text('Show Dialog')); + await tester.pumpAndSettle(); + + expect(find.text(title), findsOneWidget); + expect(find.text(noText.toUpperCase()), findsOneWidget); + + await tester + .tap(find.widgetWithText(MyOutlinedButton, noText.toUpperCase())); + await tester.pumpAndSettle(); + + expect(find.text(title), findsNothing); + }); + + testWidgets('clicking yes button closes the dialog with true', + (WidgetTester tester) async { + const title = 'Test Title'; + const yesText = 'Yes'; + bool? result; + + await tester.pumpApp( + Builder( + builder: (context) { + return ElevatedButton( + onPressed: () async { + result = await showDialog( + context: context, + builder: (_) => const ConfirmDialog( + title: title, + yes: yesText, + ), + ); + }, + child: const Text('Show Dialog'), + ); + }, + ), + ); + + await tester.tap(find.text('Show Dialog')); + await tester.pumpAndSettle(); + + expect(find.text(title), findsOneWidget); + expect(find.text(yesText.toUpperCase()), findsOneWidget); + + await tester + .tap(find.widgetWithText(MyElevatedButton, yesText.toUpperCase())); + await tester.pumpAndSettle(); + + expect(find.text(title), findsNothing); + expect(result, isTrue); + }); + + testWidgets( + 'ConfirmDialog shows only yes button when showNoButton is false', + (WidgetTester tester) async { + const title = 'Test Title'; + const yesText = 'Yes'; + const noText = 'No'; + + await tester.pumpApp( + const ConfirmDialog( + title: title, + yes: yesText, + no: noText, + showNoButton: false, + ), + ); + + expect(find.text(yesText.toUpperCase()), findsOneWidget); + expect(find.text(noText.toUpperCase()), findsNothing); + }); + }); +} diff --git a/test/app/shared/widget/dialog/error_details_dialog_test.dart b/test/app/shared/widget/dialog/error_details_dialog_test.dart new file mode 100644 index 000000000..9518cc48f --- /dev/null +++ b/test/app/shared/widget/dialog/error_details_dialog_test.dart @@ -0,0 +1,71 @@ +import 'package:altme/app/app.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../../../../helpers/helpers.dart'; + +void main() { + group('ErrorDetailsDialog Widget Tests', () { + testWidgets('renders ErrorDetailsDialog with error description', + (WidgetTester tester) async { + const errorDescription = 'An error occurred'; + + await tester.pumpApp( + const ErrorDetailsDialog( + erroDescription: errorDescription, + ), + ); + + expect(find.text(errorDescription), findsOneWidget); + }); + + testWidgets('renders ErrorDetailsDialog with more details link', + (WidgetTester tester) async { + const errorDescription = 'An error occurred'; + const errorUrl = 'https://example.com'; + + await tester.pumpApp( + const ErrorDetailsDialog( + erroDescription: errorDescription, + erroUrl: errorUrl, + ), + ); + + expect(find.text(errorDescription), findsOneWidget); + expect(find.text('More Details'), findsOneWidget); + }); + + testWidgets('clicking OK button closes the dialog', + (WidgetTester tester) async { + const errorDescription = 'An error occurred'; + + await tester.pumpApp( + Builder( + builder: (context) { + return ElevatedButton( + onPressed: () { + showDialog( + context: context, + builder: (_) => const ErrorDetailsDialog( + erroDescription: errorDescription, + ), + ); + }, + child: const Text('Show Dialog'), + ); + }, + ), + ); + + await tester.tap(find.text('Show Dialog')); + await tester.pumpAndSettle(); + + expect(find.text(errorDescription), findsOneWidget); + + await tester.tap(find.text('OK')); + await tester.pumpAndSettle(); + + expect(find.text(errorDescription), findsNothing); + }); + }); +} diff --git a/test/app/shared/widget/dialog/error_dialog_test.dart b/test/app/shared/widget/dialog/error_dialog_test.dart new file mode 100644 index 000000000..1e383c976 --- /dev/null +++ b/test/app/shared/widget/dialog/error_dialog_test.dart @@ -0,0 +1,56 @@ +import 'package:altme/app/app.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../../../../helpers/helpers.dart'; + +void main() { + group('ErrorDialog Widget Tests', () { + testWidgets( + 'displays the ErrorDialog with the correct title and description', + (WidgetTester tester) async { + const title = 'Error Title'; + const description = 'An error occurred'; + + await tester.pumpApp( + const Scaffold( + body: ErrorDialog( + title: title, + erroDescription: description, + ), + ), + ); + + expect(find.text(title), findsOneWidget); + expect(find.text('More Details'.toUpperCase()), findsOneWidget); + + await tester.tap(find.text('More Details'.toUpperCase())); + await tester.pumpAndSettle(); + + expect(find.byType(ErrorDetailsDialog), findsOneWidget); + expect(find.text(description), findsOneWidget); + }); + + testWidgets('Popup works correctly', (WidgetTester tester) async { + const title = 'Error Title'; + const description = 'An error occurred'; + + await tester.pumpApp( + const Scaffold( + body: ErrorDialog( + title: title, + erroDescription: description, + ), + ), + ); + + expect(find.byType(AlertDialog), findsOneWidget); + expect(find.text('OK'), findsOneWidget); + + await tester.tap(find.text('OK')); + await tester.pumpAndSettle(); + + expect(find.byType(AlertDialog), findsNothing); + }); + }); +} diff --git a/test/app/shared/widget/dialog/text_field_dialog_test.dart b/test/app/shared/widget/dialog/text_field_dialog_test.dart new file mode 100644 index 000000000..4844bf3a0 --- /dev/null +++ b/test/app/shared/widget/dialog/text_field_dialog_test.dart @@ -0,0 +1,55 @@ +import 'package:altme/app/app.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../../../../helpers/helpers.dart'; + +void main() { + group('Text Field Dialog', () { + testWidgets('TextFieldDialog widget test', (WidgetTester tester) async { + await tester.pumpApp( + const TextFieldDialog( + title: 'Title', + label: 'Label', + subtitle: 'Subtitle', + initialValue: 'Initial Value', + yes: 'Yes', + no: 'No', + ), + ); + // Verify that the title, label, subtitle, + // and initial value are displayed. + expect(find.text('Title'), findsOneWidget); + expect(find.text('Label'), findsOneWidget); + expect(find.text('Subtitle'), findsOneWidget); + expect(find.text('Initial Value'), findsOneWidget); + + // Verify that the Yes and No buttons are displayed. + expect(find.text('Yes'.toUpperCase()), findsOneWidget); + expect(find.text('No'.toUpperCase()), findsOneWidget); + + // Tap on the Yes button and verify that the text is popped. + await tester.tap(find.text('Yes'.toUpperCase())); + await tester.pumpAndSettle(); + expect(find.text('Initial Value'), findsNothing); + }); + + testWidgets('Popup works correctly', (WidgetTester tester) async { + await tester.pumpApp( + const TextFieldDialog( + title: 'Title', + label: 'Label', + subtitle: 'Subtitle', + initialValue: 'Initial Value', + yes: 'Yes', + no: 'No', + ), + ); + + expect(find.text('No'.toUpperCase()), findsOneWidget); + + await tester.tap(find.text('No'.toUpperCase())); + await tester.pumpAndSettle(); + expect(find.text('No'.toUpperCase()), findsNothing); + }); + }); +} diff --git a/test/app/shared/widget/dialog_close_button_test.dart b/test/app/shared/widget/dialog_close_button_test.dart new file mode 100644 index 000000000..6d1dc47d9 --- /dev/null +++ b/test/app/shared/widget/dialog_close_button_test.dart @@ -0,0 +1,62 @@ +import 'package:altme/app/app.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../../../helpers/helpers.dart'; + +void main() { + testWidgets('DialogCloseButton displays text and icon', + (WidgetTester tester) async { + await tester.pumpApp( + const Scaffold(body: DialogCloseButton()), + ); + + expect(find.text('Close'), findsOneWidget); + expect(find.byIcon(Icons.close), findsOneWidget); + }); + + testWidgets('DialogCloseButton does not display text when showText is false', + (WidgetTester tester) async { + await tester.pumpApp( + const Scaffold(body: DialogCloseButton(showText: false)), + ); + + expect(find.text('Close'), findsNothing); + expect(find.byIcon(Icons.close), findsOneWidget); + }); + + testWidgets('DialogCloseButton closes the dialog when tapped', + (WidgetTester tester) async { + await tester.pumpApp( + Scaffold( + body: Builder( + builder: (context) => Column( + children: [ + ElevatedButton( + onPressed: () { + showDialog( + context: context, + builder: (context) => const AlertDialog( + content: DialogCloseButton(), + ), + ); + }, + child: const Text('Open Dialog'), + ), + ], + ), + ), + ), + ); + + await tester.tap(find.text('Open Dialog')); + await tester.pumpAndSettle(); + + expect(find.byType(DialogCloseButton), findsOneWidget); + + await tester.tap(find.text('Close')); + await tester.pumpAndSettle(); + + expect(find.byType(DialogCloseButton), findsNothing); + }); +} diff --git a/test/app/shared/widget/display_terms_test.dart b/test/app/shared/widget/display_terms_test.dart index 599668ba8..11e263fb2 100644 --- a/test/app/shared/widget/display_terms_test.dart +++ b/test/app/shared/widget/display_terms_test.dart @@ -16,21 +16,12 @@ void main() { expect(appState.log, isNotNull); }); - /// we disabled display of privacy asset in phone language for now - testWidgets('path of privacy policy matches with device language', + testWidgets('returns privacy policies and terms for empty string', (tester) async { await tester.pumpApp(const DisplayTermsofUse()); final dynamic appState = tester.state(find.byType(DisplayTermsofUse)); - appState.setPath('it'); - expect(appState.path, 'assets/privacy/privacy_en.md'); - }); - - testWidgets('returns english privacy policy for empty string', - (tester) async { - await tester.pumpApp(const DisplayTermsofUse()); - final dynamic appState = tester.state(find.byType(DisplayTermsofUse)); - appState.setPath(''); - expect(appState.path, 'assets/privacy/privacy_en.md'); + final list = await appState.getBodyData(''); + expect(list, isA>()); }); }); } diff --git a/test/app/shared/widget/error_view_test.dart b/test/app/shared/widget/error_view_test.dart new file mode 100644 index 000000000..49788ac30 --- /dev/null +++ b/test/app/shared/widget/error_view_test.dart @@ -0,0 +1,30 @@ +import 'package:altme/app/app.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../../../helpers/helpers.dart'; + +void main() { + testWidgets('ErrorView displays message and try again button', + (WidgetTester tester) async { + const testMessage = 'An error occurred'; + bool wasButtonTapped = false; + + await tester.pumpApp( + ErrorView( + message: testMessage, + onTap: () { + wasButtonTapped = true; + }, + ), + ); + + expect(find.text(testMessage), findsOneWidget); + + expect(find.byType(MyOutlinedButton), findsOneWidget); + + await tester.tap(find.byType(MyOutlinedButton)); + await tester.pumpAndSettle(); + + expect(wasButtonTapped, isTrue); + }); +} diff --git a/test/app/shared/widget/grouped_section_test.dart b/test/app/shared/widget/grouped_section_test.dart new file mode 100644 index 000000000..00c38100e --- /dev/null +++ b/test/app/shared/widget/grouped_section_test.dart @@ -0,0 +1,29 @@ +import 'package:altme/app/app.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../../../helpers/helpers.dart'; + +void main() { + testWidgets('GroupedSection renders children correctly', + (WidgetTester tester) async { + final testChildren = [ + const Text('Child 1'), + const Text('Child 2'), + const Text('Child 3'), + ]; + + await tester.pumpApp( + Scaffold( + body: GroupedSection( + children: testChildren, + ), + ), + ); + + // Verify the children are displayed + for (final child in testChildren) { + expect(find.byWidget(child), findsOneWidget); + } + }); +} diff --git a/test/app/shared/widget/image_from_network_test.dart b/test/app/shared/widget/image_from_network_test.dart index 738b1a902..174719839 100644 --- a/test/app/shared/widget/image_from_network_test.dart +++ b/test/app/shared/widget/image_from_network_test.dart @@ -34,12 +34,12 @@ void main() { final imageFromNetwork = tester .widget(find.byType(CachedImageFromNetwork)); expect(imageFromNetwork.url, networkImageUrl); - expect(imageFromNetwork.fit, null); + expect(imageFromNetwork.fit, BoxFit.cover); expect(imageFromNetwork.key, widgetKey); }); }); - testWidgets('get SizedBox on errorBuilder', (tester) async { + testWidgets('get ColoredBox on errorBuilder', (tester) async { await mockNetworkImagesFor(() async { await tester.pumpWidget(makeTestableWidget()); final image = tester.widget(find.byType(Image)); @@ -49,7 +49,7 @@ void main() { Object(), StackTrace.fromString('stackTraceString'), ); - expect(result, isA()); + expect(result, isA()); }); }); } diff --git a/test/app/shared/widget/imported_tag_test.dart b/test/app/shared/widget/imported_tag_test.dart new file mode 100644 index 000000000..139023b41 --- /dev/null +++ b/test/app/shared/widget/imported_tag_test.dart @@ -0,0 +1,19 @@ +import 'package:altme/app/app.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../../../helpers/helpers.dart'; + +void main() { + group('ImportedTag', () { + testWidgets('renders importedTag', (tester) async { + await tester.pumpApp(const ImportedTag()); + expect(find.byType(Text), findsOneWidget); + }); + + testWidgets('renders value correctly', (tester) async { + await tester.pumpApp(const ImportedTag()); + expect(find.text('IMPORTED'), findsOneWidget); + }); + }); +} diff --git a/test/app/shared/widget/m_webview/cubit/m_webview_cubit_test.dart b/test/app/shared/widget/m_webview/cubit/m_webview_cubit_test.dart new file mode 100644 index 000000000..c8c9953db --- /dev/null +++ b/test/app/shared/widget/m_webview/cubit/m_webview_cubit_test.dart @@ -0,0 +1,28 @@ +import 'package:altme/app/app.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('MWebViewCubit', () { + late MWebViewCubit cubit; + + setUp(() { + cubit = MWebViewCubit(); + }); + + tearDown(() { + cubit.close(); + }); + + test('initial state is true', () { + expect(cubit.state, true); + }); + + test('emits new state when setLoading is called', () { + expect(cubit.state, true); + cubit.setLoading(isLoading: false); + expect(cubit.state, false); + cubit.setLoading(isLoading: true); + expect(cubit.state, true); + }); + }); +} diff --git a/test/app/shared/widget/mnemonic_test.dart b/test/app/shared/widget/mnemonic_test.dart new file mode 100644 index 000000000..52f17dbed --- /dev/null +++ b/test/app/shared/widget/mnemonic_test.dart @@ -0,0 +1,37 @@ +import 'package:altme/app/app.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../../../helpers/helpers.dart'; + +void main() { + testWidgets('MnemonicDisplay shows the correct words', + (WidgetTester tester) async { + final mnemonic = [ + 'apple', + 'banana', + 'cherry', + 'date', + 'elderberry', + 'fig', + 'grape', + 'honeydew', + 'kiwi', + 'lemon', + 'mango', + 'nectarine', + ]; + + await tester.pumpApp( + Scaffold( + body: MnemonicDisplay(mnemonic: mnemonic), + ), + ); + + await tester.pumpAndSettle(); + + for (int i = 0; i < mnemonic.length; i++) { + expect(find.text('${i + 1}. ${mnemonic[i]}'), findsOneWidget); + } + }); +} diff --git a/test/app/shared/widget/my_rich_text_test.dart b/test/app/shared/widget/my_rich_text_test.dart new file mode 100644 index 000000000..bdd263d26 --- /dev/null +++ b/test/app/shared/widget/my_rich_text_test.dart @@ -0,0 +1,51 @@ +import 'package:altme/app/app.dart'; +import 'package:auto_size_text/auto_size_text.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../../../helpers/helpers.dart'; + +void main() { + testWidgets('MyRichText displays the correct rich text', + (WidgetTester tester) async { + const textSpan = TextSpan( + text: 'Hello ', + children: [ + TextSpan( + text: 'World', + style: TextStyle(fontWeight: FontWeight.bold), + ), + ], + ); + + await tester + .pumpApp(const Scaffold(body: MyRichText(text: textSpan, maxLines: 1))); + + await tester.pumpAndSettle(); + + expect(find.text('Hello World'), findsOneWidget); + }); + + testWidgets('MyRichText respects maxLines', (WidgetTester tester) async { + const longTextSpan = TextSpan( + text: 'This is a long text that should be truncated', + ); + + // Build the widget + await tester.pumpApp( + const Scaffold( + body: SizedBox( + width: 100, + child: MyRichText( + text: longTextSpan, + maxLines: 1, + ), + ), + ), + ); + + final richTextWidget = + tester.widget(find.byType(AutoSizeText)); + expect(richTextWidget.maxLines, 1); + }); +} diff --git a/test/app/shared/widget/numeric_keyboard_test.dart b/test/app/shared/widget/numeric_keyboard_test.dart new file mode 100644 index 000000000..4c44b452c --- /dev/null +++ b/test/app/shared/widget/numeric_keyboard_test.dart @@ -0,0 +1,162 @@ +import 'package:altme/app/app.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; +import '../../../helpers/helpers.dart'; + +class MockKeyboardTapCallback extends Mock { + void call(String text); +} + +void main() { + group('NumericKeyboard', () { + testWidgets('renders keyboard buttons correctly', (tester) async { + final mockCallback = MockKeyboardTapCallback(); + + await tester.pumpApp( + Scaffold( + body: NumericKeyboard( + keyboardUIConfig: const KeyboardUIConfig(), + onKeyboardTap: mockCallback.call, + allowAction: true, + ), + ), + ); + + expect(find.byType(KeyboardButton), findsNWidgets(10)); + }); + + testWidgets('tapping a keyboard button triggers the callback', + (tester) async { + final mockCallback = MockKeyboardTapCallback(); + + await tester.pumpApp( + Scaffold( + body: NumericKeyboard( + keyboardUIConfig: const KeyboardUIConfig(), + onKeyboardTap: mockCallback.call, + allowAction: true, + ), + ), + ); + + await tester.tap(find.text('1')); + verify(() => mockCallback.call('1')).called(1); + }); + }); + + group('KeyboardButton', () { + testWidgets('renders label correctly', (tester) async { + await tester.pumpApp( + Scaffold( + body: Scaffold( + body: KeyboardButton( + semanticsLabel: '1', + label: '1', + onTap: (_) {}, + allowAction: true, + ), + ), + ), + ); + + expect(find.text('1'), findsOneWidget); + }); + + testWidgets('renders icon correctly', (tester) async { + await tester.pumpApp( + Scaffold( + body: Scaffold( + body: KeyboardButton( + semanticsLabel: 'delete', + icon: const Icon(Icons.delete), + onTap: (_) {}, + allowAction: true, + ), + ), + ), + ); + + expect(find.byIcon(Icons.delete), findsOneWidget); + }); + + testWidgets('triggers onTap callback when tapped', (tester) async { + final mockCallback = MockKeyboardTapCallback(); + await tester.pumpApp( + Scaffold( + body: Scaffold( + body: KeyboardButton( + semanticsLabel: '1', + label: '1', + onTap: mockCallback.call, + allowAction: true, + ), + ), + ), + ); + + await tester.tap(find.text('1')); + verify(() => mockCallback.call('1')).called(1); + }); + + testWidgets('does not trigger onTap callback when disabled', + (tester) async { + final mockCallback = MockKeyboardTapCallback(); + await tester.pumpApp( + Scaffold( + body: Scaffold( + body: KeyboardButton( + semanticsLabel: '1', + label: '1', + onTap: mockCallback.call, + allowAction: false, + ), + ), + ), + ); + + await tester.tap(find.text('1')); + verifyNever(() => mockCallback.call(any())); + }); + + testWidgets('triggers onLongPress callback when long pressed', + (tester) async { + final mockCallback = MockKeyboardTapCallback(); + await tester.pumpApp( + Scaffold( + body: Scaffold( + body: KeyboardButton( + semanticsLabel: '1', + label: '1', + onLongPress: mockCallback.call, + allowAction: true, + ), + ), + ), + ); + + await tester.longPress(find.text('1')); + verify(() => mockCallback.call('1')).called(1); + }); + + testWidgets('does not trigger onLongPress callback when disabled', + (tester) async { + final mockCallback = MockKeyboardTapCallback(); + await tester.pumpApp( + Scaffold( + body: Scaffold( + body: KeyboardButton( + semanticsLabel: '1', + label: '1', + onLongPress: mockCallback.call, + allowAction: false, + ), + ), + ), + ); + + await tester.longPress(find.text('1')); + verifyNever(() => mockCallback.call(any())); + }); + }); +} diff --git a/test/app/shared/widget/powered_by_text_test.dart b/test/app/shared/widget/powered_by_text_test.dart new file mode 100644 index 000000000..a745ec7cb --- /dev/null +++ b/test/app/shared/widget/powered_by_text_test.dart @@ -0,0 +1,19 @@ +import 'package:altme/app/app.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../../../helpers/helpers.dart'; + +void main() { + group('PoweredByText', () { + testWidgets('renders PoweredByText', (tester) async { + await tester.pumpApp(const PoweredByText()); + expect(find.byType(Text), findsOneWidget); + }); + + testWidgets('renders value correctly', (tester) async { + await tester.pumpApp(const PoweredByText()); + expect(find.text('Powered By ${Parameters.appName}'), findsOneWidget); + }); + }); +} diff --git a/test/app/shared/widget/share_button_test.dart b/test/app/shared/widget/share_button_test.dart new file mode 100644 index 000000000..af9532a79 --- /dev/null +++ b/test/app/shared/widget/share_button_test.dart @@ -0,0 +1,36 @@ +import 'package:altme/app/app.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../../../helpers/helpers.dart'; + +void main() { + group('ShareButton', () { + testWidgets('ShareButton displays the correct icon and text', + (WidgetTester tester) async { + await tester.pumpApp(const Scaffold(body: ShareButton())); + + expect(find.byType(Image), findsOneWidget); + expect(find.text('Share'), findsOneWidget); + }); + + testWidgets('ShareButton responds to tap', (WidgetTester tester) async { + bool tapped = false; + + await tester.pumpApp( + Scaffold( + body: ShareButton( + onTap: () { + tapped = true; + }, + ), + ), + ); + + await tester.tap(find.byType(InkWell)); + await tester.pumpAndSettle(); + + expect(tapped, true); + }); + }); +} diff --git a/test/app/shared/widget/shimmer_widget_test.dart b/test/app/shared/widget/shimmer_widget_test.dart new file mode 100644 index 000000000..7781dd4e0 --- /dev/null +++ b/test/app/shared/widget/shimmer_widget_test.dart @@ -0,0 +1,57 @@ +import 'package:altme/app/app.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:shimmer/shimmer.dart'; +import '../../../helpers/helpers.dart'; + +void main() { + group('ShimmerWidget', () { + testWidgets('ShimmerWidget.rectangular displays correctly', + (WidgetTester tester) async { + await tester.pumpApp( + const Scaffold( + body: ShimmerWidget.rectangular(height: 100), + ), + ); + + expect(find.byType(Shimmer), findsOneWidget); + + final constraints = + tester.firstWidget(find.byType(Container)).constraints; + expect(constraints!.minWidth, double.infinity); + expect(constraints.maxWidth, double.infinity); + expect(constraints.minHeight, 100.0); + expect(constraints.maxHeight, 100.0); + + final container = tester.widget(find.byType(Container)); + + final shapeDecoration = container.decoration! as ShapeDecoration; + expect(shapeDecoration.shape, isA()); + final roundedRectBorder = shapeDecoration.shape as RoundedRectangleBorder; + expect( + roundedRectBorder.borderRadius, + const BorderRadius.all(Radius.circular(10)), + ); + }); + + testWidgets('ShimmerWidget.circular displays correctly', + (WidgetTester tester) async { + await tester.pumpApp( + const Scaffold(body: ShimmerWidget.circular(height: 100)), + ); + + expect(find.byType(Shimmer), findsOneWidget); + + final constraints = + tester.firstWidget(find.byType(Container)).constraints; + expect(constraints!.minWidth, double.infinity); + expect(constraints.maxWidth, double.infinity); + expect(constraints.minHeight, 100.0); + expect(constraints.maxHeight, 100.0); + + final container = tester.widget(find.byType(Container)); + final shapeDecoration = container.decoration! as ShapeDecoration; + expect(shapeDecoration.shape, isA()); + }); + }); +} diff --git a/test/app/shared/widget/wallet_logo_test.dart b/test/app/shared/widget/wallet_logo_test.dart new file mode 100644 index 000000000..5821e5eb0 --- /dev/null +++ b/test/app/shared/widget/wallet_logo_test.dart @@ -0,0 +1,223 @@ +import 'package:altme/app/app.dart'; +import 'package:altme/dashboard/dashboard.dart'; +import 'package:altme/flavor/flavor.dart'; +import 'package:bloc_test/bloc_test.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../../../helpers/helpers.dart'; + +class MockFlavorCubit extends MockCubit implements FlavorCubit { + @override + final state = FlavorMode.development; +} + +void main() { + late FlavorCubit mockFlavorCubit; + + setUpAll(() { + mockFlavorCubit = MockFlavorCubit(); + }); + + group('WalletLogo widget', () { + testWidgets( + 'displays correct image for ProfileType.defaultOne in development', + (WidgetTester tester) async { + await tester.pumpApp( + Scaffold( + body: Builder( + builder: (context) { + return BlocProvider( + create: (context) => mockFlavorCubit, + child: WalletLogo( + height: 100, + width: 100, + profileModel: ProfileModel.defaultOne( + polygonIdNetwork: PolygonIdNetwork.PolygonMainnet, + walletType: WalletType.personal, + walletProtectionType: WalletProtectionType.FA2, + isDeveloperMode: true, + clientId: 'clientId', + clientSecret: 'clientSecret', + ), + ), + ); + }, + ), + ), + ); + await tester.pumpAndSettle(); + + expect(find.byType(Image), findsOneWidget); + }); + + testWidgets('displays correct image for ProfileType.custom in development', + (WidgetTester tester) async { + await tester.pumpApp( + Scaffold( + body: Builder( + builder: (context) { + return BlocProvider( + create: (context) => mockFlavorCubit, + child: WalletLogo( + height: 100, + width: 100, + profileModel: ProfileModel.empty(), + ), + ); + }, + ), + ), + ); + await tester.pumpAndSettle(); + + expect(find.byType(Image), findsOneWidget); + }); + + testWidgets('displays correct image for ProfileType.dutch in development', + (WidgetTester tester) async { + await tester.pumpApp( + Scaffold( + body: Builder( + builder: (context) { + return BlocProvider( + create: (context) => mockFlavorCubit, + child: WalletLogo( + height: 100, + width: 100, + profileModel: ProfileModel.dutch( + polygonIdNetwork: PolygonIdNetwork.PolygonMainnet, + walletType: WalletType.personal, + walletProtectionType: WalletProtectionType.FA2, + isDeveloperMode: true, + clientId: 'clientId', + clientSecret: 'clientSecret', + ), + ), + ); + }, + ), + ), + ); + await tester.pumpAndSettle(); + + expect(find.byType(Image), findsOneWidget); + }); + + testWidgets('displays correct image for ProfileType.ebsiV3 in development', + (WidgetTester tester) async { + await tester.pumpApp( + Scaffold( + body: Builder( + builder: (context) { + return BlocProvider( + create: (context) => mockFlavorCubit, + child: WalletLogo( + height: 100, + width: 100, + profileModel: ProfileModel.ebsiV3( + polygonIdNetwork: PolygonIdNetwork.PolygonMainnet, + walletType: WalletType.personal, + walletProtectionType: WalletProtectionType.FA2, + isDeveloperMode: true, + clientId: 'clientId', + clientSecret: 'clientSecret', + ), + ), + ); + }, + ), + ), + ); + await tester.pumpAndSettle(); + + expect(find.byType(Image), findsOneWidget); + }); + + testWidgets( + 'displays correct image for ' + 'ProfileType.owfBaselineProfile in development', + (WidgetTester tester) async { + await tester.pumpApp( + Scaffold( + body: Builder( + builder: (context) { + return BlocProvider( + create: (context) => mockFlavorCubit, + child: WalletLogo( + height: 100, + width: 100, + profileModel: ProfileModel.owfBaselineProfile( + polygonIdNetwork: PolygonIdNetwork.PolygonMainnet, + walletType: WalletType.personal, + walletProtectionType: WalletProtectionType.FA2, + isDeveloperMode: true, + clientId: 'clientId', + clientSecret: 'clientSecret', + ), + ), + ); + }, + ), + ), + ); + await tester.pumpAndSettle(); + + expect(find.byType(Image), findsOneWidget); + }); + + testWidgets( + 'displays correct image for ProfileType.enterprise in development', + (WidgetTester tester) async { + await tester.pumpApp( + Scaffold( + body: Builder( + builder: (context) { + return BlocProvider( + create: (context) => mockFlavorCubit, + child: WalletLogo( + height: 100, + width: 100, + profileModel: ProfileModel( + polygonIdNetwork: PolygonIdNetwork.PolygonMainnet, + walletType: WalletType.personal, + walletProtectionType: WalletProtectionType.pinCode, + isDeveloperMode: false, + profileType: ProfileType.enterprise, + profileSetting: ProfileSetting( + blockchainOptions: BlockchainOptions.initial(), + discoverCardsOptions: DiscoverCardsOptions.initial(), + generalOptions: GeneralOptions( + walletType: WalletAppType.altme, + companyName: '', + companyWebsite: '', + companyLogo: 'https://www.demo.com', + tagLine: '', + splashScreenTitle: '', + profileName: '', + profileVersion: '', + published: DateTime.now(), + profileId: '', + customerPlan: '', + ), + helpCenterOptions: HelpCenterOptions.initial(), + selfSovereignIdentityOptions: + SelfSovereignIdentityOptions.initial(), + settingsMenu: SettingsMenu.initial(), + version: '', + walletSecurityOptions: WalletSecurityOptions.initial(), + ), + ), + ), + ); + }, + ), + ), + ); + await tester.pumpAndSettle(); + + expect(find.byType(CachedImageFromNetwork), findsOneWidget); + }); + }); +} diff --git a/test/app/view/app_test.dart b/test/app/view/app_test.dart index 5e73f602f..846497f80 100644 --- a/test/app/view/app_test.dart +++ b/test/app/view/app_test.dart @@ -6,7 +6,6 @@ // https://opensource.org/licenses/MIT. import 'package:altme/app/app.dart'; -import 'package:altme/splash/view/splash_page.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { @@ -15,10 +14,10 @@ void main() { expect(const App().flavorMode, FlavorMode.production); }); - testWidgets('renders SplashPage', (tester) async { - await tester.pumpWidget(const App()); - await tester.pumpAndSettle(); - expect(find.byType(SplashPage), findsOneWidget); - }); + // testWidgets('renders SplashPage', (tester) async { + // await tester.pumpWidget(const App()); + // await tester.pumpAndSettle(); + // expect(find.byType(SplashPage), findsOneWidget); + // }); }); } diff --git a/test/credentials/models/author_test.dart b/test/credentials/models/author_test.dart index 8b959d0fd..c2fb62b5e 100644 --- a/test/credentials/models/author_test.dart +++ b/test/credentials/models/author_test.dart @@ -4,26 +4,25 @@ import 'package:flutter_test/flutter_test.dart'; void main() { group('Author model work properly', () { test('initialize Author with default constructor', () { - const author = Author('Taleb'); - expect(author.name, 'Taleb'); + const author = Author('Test'); + expect(author.name, 'Test'); }); test('initialize Author fromJson constructor', () { const authorJson = { - 'name': 'Taleb', + 'name': 'Test1', 'logo': 'logo', }; final author = Author.fromJson(authorJson); - expect(author.name, 'Taleb'); + expect(author.name, 'Test1'); }); test('Author toJson worked correct', () { - const authorJson = { - 'name': 'Taleb', - 'logo': 'logo', - }; - final author = Author.fromJson(authorJson); - expect(author.toJson(), authorJson); + const author = Author('Test'); + + final json = author.toJson(); + + expect(json['name'], 'Test'); }); }); } diff --git a/test/credentials/models/credential_status_field_test.dart b/test/credentials/models/credential_status_field_test.dart index 92c7a0686..5d1fec607 100644 --- a/test/credentials/models/credential_status_field_test.dart +++ b/test/credentials/models/credential_status_field_test.dart @@ -25,31 +25,50 @@ void main() { test('fromJson constructor work correctly', () { final credentialStatusField = CredentialStatusField.fromJson( { - 'id': 'id', + 'id': '123', 'type': 'type', 'revocationListIndex': 'revocationListIndex', 'revocationListCredential': 'revocationListCredential', + 'statusListCredential': 'statusListCredential', + 'statusListIndex': 'statusListIndex', + 'statusPurpose': 'statusPurpose', }, ); - expect(credentialStatusField.id, 'id'); + expect(credentialStatusField.id, '123'); expect(credentialStatusField.type, 'type'); expect(credentialStatusField.revocationListIndex, 'revocationListIndex'); expect( credentialStatusField.revocationListCredential, 'revocationListCredential', ); + expect( + credentialStatusField.statusListCredential, + 'statusListCredential', + ); + expect(credentialStatusField.statusListIndex, 'statusListIndex'); + expect(credentialStatusField.statusPurpose, 'statusPurpose'); }); test('toJson constructor work correctly', () { - final json = { - 'id': 'id', - 'type': 'type', - 'revocationListIndex': 'revocationListIndex', - 'revocationListCredential': 'revocationListCredential', - }; - final credentialStatusField = CredentialStatusField.fromJson(json); + final credentialStatusField = CredentialStatusField( + 'id', + 'type', + 'revocationListIndex', + 'revocationListCredential', + 'statusListCredential', + 'statusListIndex', + 'statusPurpose', + ); + + final json = credentialStatusField.toJson(); - expect(credentialStatusField.toJson(), json); + expect(json['id'], 'id'); + expect(json['type'], 'type'); + expect(json['revocationListIndex'], 'revocationListIndex'); + expect(json['revocationListCredential'], 'revocationListCredential'); + expect(json['statusListCredential'], 'statusListCredential'); + expect(json['statusListIndex'], 'statusListIndex'); + expect(json['statusPurpose'], 'statusPurpose'); }); test('empty constructor work properly', () { diff --git a/test/credentials/models/proof_test.dart b/test/credentials/models/proof_test.dart index 982eb9e0f..1ca044383 100644 --- a/test/credentials/models/proof_test.dart +++ b/test/credentials/models/proof_test.dart @@ -31,16 +31,21 @@ void main() { }); test('toJson constructor work correctly', () { - final json = { - 'type': 'type', - 'proofPurpose': 'proofPurpose', - 'verificationMethod': 'verificationMethod', - 'created': 'created', - 'jws': 'jws', - }; - final proof = Proof.fromJson(json); + final proof = Proof( + 'type', + 'proofPurpose', + 'verificationMethod', + 'created', + 'jws', + ); + + final json = proof.toJson(); - expect(proof.toJson(), json); + expect(json['type'], 'type'); + expect(json['proofPurpose'], 'proofPurpose'); + expect(json['verificationMethod'], 'verificationMethod'); + expect(json['created'], 'created'); + expect(json['jws'], 'jws'); }); test('dummy constructor work properly', () { diff --git a/test/credentials/widgets/display_issuer_test.dart b/test/credentials/widgets/display_issuer_test.dart index b25aaaa75..aded9f424 100644 --- a/test/credentials/widgets/display_issuer_test.dart +++ b/test/credentials/widgets/display_issuer_test.dart @@ -5,19 +5,14 @@ import 'package:flutter_test/flutter_test.dart'; void main() { group('DisplayIssuer Widget work correctly', () { - late Author issuerWithLogo; - late Author issuerWithoutLogo; const authorName = 'Taleb'; - setUp(() { - issuerWithLogo = const Author(authorName); - issuerWithoutLogo = const Author(authorName); - }); + const author = Author(authorName); testWidgets('find issuer name', (WidgetTester tester) async { await tester.pumpWidget( - MaterialApp( - home: DisplayIssuer(issuer: issuerWithLogo), + const MaterialApp( + home: DisplayIssuer(issuer: author), ), ); expect(find.text(authorName), findsOneWidget); @@ -25,46 +20,24 @@ void main() { testWidgets('find issuer model', (WidgetTester tester) async { await tester.pumpWidget( - MaterialApp( - home: DisplayIssuer(issuer: issuerWithLogo), + const MaterialApp( + home: DisplayIssuer(issuer: author), ), ); final displayIssuer = tester.widget(find.byType(DisplayIssuer)) as DisplayIssuer; - expect(displayIssuer.issuer, issuerWithLogo); - }); - - testWidgets('find CachedImageFromNetwork', (WidgetTester tester) async { - await tester.pumpWidget( - MaterialApp( - home: DisplayIssuer(issuer: issuerWithLogo), - ), - ); - expect(find.byType(CachedImageFromNetwork), findsOneWidget); - }); - - testWidgets('CachedImageFromNetwork gone when logo is empty', - (WidgetTester tester) async { - await tester.pumpWidget( - MaterialApp( - home: DisplayIssuer(issuer: issuerWithoutLogo), - ), - ); - expect(find.byType(CachedImageFromNetwork), findsNothing); + expect(displayIssuer.issuer, author); }); testWidgets('find all widget', (WidgetTester tester) async { await tester.pumpWidget( - MaterialApp( - home: DisplayIssuer(issuer: issuerWithLogo), + const MaterialApp( + home: DisplayIssuer(issuer: author), ), ); - expect(find.byType(Padding), findsOneWidget); - expect(find.byType(CachedImageFromNetwork), findsOneWidget); expect(find.byType(Row), findsOneWidget); - expect(find.byType(Text), findsOneWidget); - expect(find.byType(Spacer), findsOneWidget); - expect(find.byType(SizedBox), findsNWidgets(2)); + expect(find.byType(Expanded), findsOneWidget); + expect(find.byType(MyText), findsOneWidget); }); }); } diff --git a/test/onboarding/activate_biometircs/cubit/active_biometrics_cubit_test.dart b/test/onboarding/activate_biometircs/cubit/active_biometrics_cubit_test.dart new file mode 100644 index 000000000..15a5c1ee7 --- /dev/null +++ b/test/onboarding/activate_biometircs/cubit/active_biometrics_cubit_test.dart @@ -0,0 +1,77 @@ +import 'package:altme/app/app.dart'; +import 'package:altme/dashboard/dashboard.dart'; +import 'package:altme/onboarding/onboarding.dart'; + +import 'package:bloc_test/bloc_test.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; + +class MockProfileCubit extends MockCubit + implements ProfileCubit {} + +void main() { + group('ActiveBiometricsCubit', () { + late ActiveBiometricsCubit activeBiometricsCubit; + late MockProfileCubit mockProfileCubit; + + setUp(() { + mockProfileCubit = MockProfileCubit(); + + activeBiometricsCubit = + ActiveBiometricsCubit(profileCubit: mockProfileCubit); + }); + + test('initial state is false', () { + expect(activeBiometricsCubit.state, false); + }); + + blocTest( + 'load emits true if walletProtectionType is FA2 or biometrics', + build: () { + when(() => mockProfileCubit.state).thenReturn( + ProfileState( + model: ProfileModel( + polygonIdNetwork: PolygonIdNetwork.PolygonMainnet, + walletType: WalletType.personal, + walletProtectionType: WalletProtectionType.biometrics, + isDeveloperMode: false, + profileType: ProfileType.custom, + profileSetting: ProfileSetting.initial(), + ), + ), + ); + return activeBiometricsCubit; + }, + act: (cubit) => cubit.load(), + expect: () => [true], + ); + + blocTest( + 'load emits false if walletProtectionType is not FA2 or biometrics', + build: () { + when(() => mockProfileCubit.state).thenReturn( + ProfileState( + model: ProfileModel( + polygonIdNetwork: PolygonIdNetwork.PolygonMainnet, + walletType: WalletType.personal, + walletProtectionType: WalletProtectionType.pinCode, + isDeveloperMode: false, + profileType: ProfileType.custom, + profileSetting: ProfileSetting.initial(), + ), + ), + ); + return activeBiometricsCubit; + }, + act: (cubit) => cubit.load(), + expect: () => [false], + ); + + blocTest( + 'updateSwitch emits the provided value', + build: () => activeBiometricsCubit, + act: (cubit) => cubit.updateSwitch(value: true), + expect: () => [true], + ); + }); +} diff --git a/test/onboarding/activate_biometircs/view/activate_biometrics_page_test.dart b/test/onboarding/activate_biometircs/view/activate_biometrics_page_test.dart new file mode 100644 index 000000000..060745264 --- /dev/null +++ b/test/onboarding/activate_biometircs/view/activate_biometrics_page_test.dart @@ -0,0 +1,208 @@ +import 'package:altme/app/app.dart'; +import 'package:altme/dashboard/dashboard.dart'; +import 'package:altme/onboarding/onboarding.dart'; +import 'package:bloc_test/bloc_test.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockingjay/mockingjay.dart'; + +import '../../../helpers/helpers.dart'; + +class MockProfileCubit extends MockCubit implements ProfileCubit { + @override + final state = ProfileState( + model: ProfileModel( + polygonIdNetwork: PolygonIdNetwork.PolygonMainnet, + walletType: WalletType.personal, + walletProtectionType: WalletProtectionType.FA2, + isDeveloperMode: false, + profileType: ProfileType.custom, + profileSetting: ProfileSetting.initial(), + ), + ); +} + +class MockLocalAuthApi extends Mock implements LocalAuthApi {} + +void main() { + late ActiveBiometricsCubit activeBiometricsCubit; + late MockLocalAuthApi localAuthApi; + late MockProfileCubit mockProfileCubit; + + setUpAll(() { + WidgetsFlutterBinding.ensureInitialized(); + mockProfileCubit = MockProfileCubit(); + activeBiometricsCubit = + ActiveBiometricsCubit(profileCubit: mockProfileCubit); + localAuthApi = MockLocalAuthApi(); + }); + + group('OnBoarding GenPhrase Page', () { + late MockNavigator navigator; + + setUpAll(() { + navigator = MockNavigator(); + when(navigator.canPop).thenReturn(true); + + when(() => navigator.push(any())).thenAnswer((_) async {}); + }); + + testWidgets('is routable', (tester) async { + await tester.pumpApp( + MockNavigatorProvider( + navigator: navigator, + child: Builder( + builder: (context) => Scaffold( + floatingActionButton: FloatingActionButton( + onPressed: () { + Navigator.of(context).push( + ActiviateBiometricsPage.route( + isFromOnboarding: true, + onAction: ({required bool isEnabled}) {}, + localAuthApi: localAuthApi, + ), + ); + }, + ), + ), + ), + ), + ); + await tester.tap(find.byType(FloatingActionButton)); + await tester.pumpAndSettle(); + + verify( + () => navigator.push( + any( + that: isRoute( + whereName: equals('/activiateBiometricsPage'), + ), + ), + ), + ).called(1); + }); + + testWidgets('renders ActivateBiometricsView', (tester) async { + await tester.pumpApp( + MultiBlocProvider( + providers: [ + BlocProvider.value( + value: activeBiometricsCubit, + ), + BlocProvider.value(value: mockProfileCubit), + ], + child: ActiviateBiometricsPage( + isFromOnboarding: true, + onAction: ({required bool isEnabled}) {}, + localAuthApi: localAuthApi, + ), + ), + ); + + expect(find.byType(ActivateBiometricsView), findsOneWidget); + }); + + testWidgets('renders UI correctly', (tester) async { + await tester.pumpApp( + BlocProvider.value( + value: activeBiometricsCubit, + child: ActivateBiometricsView( + isFromOnboarding: true, + onAction: ({required bool isEnabled}) {}, + localAuthApi: localAuthApi, + ), + ), + ); + + expect(find.byType(BasePage), findsOneWidget); + expect(find.byType(BiometricsSwitch), findsOneWidget); + expect( + find.text('Activate Biometrics\nto add a securtiy layer'), + findsOneWidget, + ); + }); + + testWidgets( + 'MyElevatedButton should be enabled and trigger onAction callback', + (tester) async { + bool callbackCalled = false; + + await tester.pumpApp( + MockNavigatorProvider( + navigator: navigator, + child: BlocProvider.value( + value: activeBiometricsCubit, + child: ActivateBiometricsView( + isFromOnboarding: true, + localAuthApi: localAuthApi, + onAction: ({required bool isEnabled}) { + callbackCalled = isEnabled; + }, + ), + ), + ), + ); + + await tester.tap(find.text('Next'.toUpperCase())); + + expect(callbackCalled, true); + }); + + group('BiometricsSwitch', () { + testWidgets('show Diloag when biometrics is not available', + (tester) async { + when(() => localAuthApi.hasBiometrics()) + .thenAnswer((_) => Future.value(false)); + + await tester.pumpApp( + MockNavigatorProvider( + navigator: navigator, + child: BlocProvider.value( + value: activeBiometricsCubit, + child: ActivateBiometricsView( + isFromOnboarding: true, + localAuthApi: localAuthApi, + onAction: ({required bool isEnabled}) {}, + ), + ), + ), + ); + + await tester.tap(find.byType(CupertinoSwitch)); + await tester.pump(); + expect(find.byType(ConfirmDialog), findsOneWidget); + expect(find.text('Biometrics not supported'), findsOneWidget); + }); + + testWidgets('show Diloag when biometrics is available', (tester) async { + when(() => localAuthApi.hasBiometrics()) + .thenAnswer((_) => Future.value(true)); + when( + () => localAuthApi.authenticate( + localizedReason: 'Scan Fingerprint to Authenticate', + ), + ).thenAnswer((_) => Future.value(true)); + + await tester.pumpApp( + MockNavigatorProvider( + navigator: navigator, + child: BlocProvider.value( + value: activeBiometricsCubit, + child: ActivateBiometricsView( + isFromOnboarding: true, + localAuthApi: localAuthApi, + onAction: ({required bool isEnabled}) {}, + ), + ), + ), + ); + + await tester.tap(find.byType(CupertinoSwitch)); + expect(find.byType(ConfirmDialog), findsNothing); + expect(activeBiometricsCubit.state, false); + }); + }); + }); +} diff --git a/test/onboarding/activate_biometircs/widgets/biometrics_switch_test.dart b/test/onboarding/activate_biometircs/widgets/biometrics_switch_test.dart new file mode 100644 index 000000000..99b51d0e8 --- /dev/null +++ b/test/onboarding/activate_biometircs/widgets/biometrics_switch_test.dart @@ -0,0 +1,26 @@ +import 'package:altme/app/app.dart'; +import 'package:altme/onboarding/onboarding.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import '../../../helpers/helpers.dart'; + +void main() { + group('BiometricsSwitch', () { + testWidgets('renders value correctly', (tester) async { + await tester.pumpApp(const BiometricsSwitch(value: true)); + + expect( + find.byWidgetPredicate( + (widget) => + widget is Image && + widget.image is AssetImage && + (widget.image as AssetImage).assetName == + IconStrings.fingerprint2, + ), + findsOneWidget, + ); + expect(find.byType(CupertinoSwitch), findsOneWidget); + }); + }); +} diff --git a/test/onboarding/cubit/onboarding_cubit_test.dart b/test/onboarding/cubit/onboarding_cubit_test.dart new file mode 100644 index 000000000..374f128f9 --- /dev/null +++ b/test/onboarding/cubit/onboarding_cubit_test.dart @@ -0,0 +1,26 @@ +import 'package:altme/app/app.dart'; +import 'package:altme/onboarding/cubit/onboarding_cubit.dart'; +import 'package:bloc_test/bloc_test.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('OnboardingCubit', () { + test('initial state is correct', () { + expect(OnboardingCubit().state, const OnboardingState()); + }); + + blocTest( + 'emits [loading] when emitOnboardingProcessing is called', + build: OnboardingCubit.new, + act: (cubit) => cubit.emitOnboardingProcessing(), + expect: () => [const OnboardingState(status: AppStatus.loading)], + ); + + blocTest( + 'emits [success] when emitOnboardingDone is called', + build: OnboardingCubit.new, + act: (cubit) => cubit.emitOnboardingDone(), + expect: () => [const OnboardingState(status: AppStatus.success)], + ); + }); +} diff --git a/test/onboarding/cubit/onboarding_state_test.dart b/test/onboarding/cubit/onboarding_state_test.dart new file mode 100644 index 000000000..7c49892f9 --- /dev/null +++ b/test/onboarding/cubit/onboarding_state_test.dart @@ -0,0 +1,22 @@ +import 'package:altme/app/app.dart'; +import 'package:altme/onboarding/cubit/onboarding_cubit.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('OnboardingState', () { + test('can be instantiated from JSON', () { + final json = {'status': 'init'}; + + final onboardingState = OnboardingState.fromJson(json); + + expect(onboardingState.status, AppStatus.init); + }); + + test('can be converted to JSON', () { + const onboardingState = OnboardingState(status: AppStatus.init); + final json = onboardingState.toJson(); + + expect(json, {'status': 'init'}); + }); + }); +} diff --git a/test/onboarding/gen_phrase/cubit/onboarding_gen_phrase_cubit_test.dart b/test/onboarding/gen_phrase/cubit/onboarding_gen_phrase_cubit_test.dart deleted file mode 100644 index 6b2f2b246..000000000 --- a/test/onboarding/gen_phrase/cubit/onboarding_gen_phrase_cubit_test.dart +++ /dev/null @@ -1,62 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:key_generator/key_generator.dart'; - -Future main() async { - final keyGenerator = KeyGenerator(); - group('from mnemonic for ssi', () { - const String mnemonic = - '''brief hello carry loop squeeze unknown click abstract lounge figure logic oblige child ripple about vacant scheme magnet open enroll stuff valve hobby what'''; - - test('jwk', () async { - final jwk = await keyGenerator.jwkFromMnemonic( - mnemonic: mnemonic, - accountType: AccountType.ssi, - ); - const expectedJwk = - '''{"kty":"OKP","crv":"Ed25519","d":"cMGD8eAmjDn6MqvJoscsaPoyAMrjG41xbLDfE-uQkYw=","x":"-PeGBkVyMz2-yketwH2lbQqiflneee3jmaTafMCsURE="}'''; - expect(jwk, equals(expectedJwk)); - }); - - test('address', () async { - final address = await keyGenerator.walletAddressFromMnemonic( - mnemonic: mnemonic, - accountType: AccountType.ssi, - ); - const expectedAddress = 'tz1NvqicaUW7v6sEbM4UYi3Wes7GHDft4kqY'; - expect(address, equals(expectedAddress)); - }); - - test('secret key', () async { - final secretKey = await keyGenerator.secretKeyFromMnemonic( - mnemonic: mnemonic, - accountType: AccountType.ssi, - ); - const expectedSecretKey = - '''edskRrmNgPfAAvbZyzTptfvTju9X7ooLR5VVN9u8sXA42hXdMBd8CgrhykP7sZQf8hWLCYuqfEoWUFzL6Us3aKtMD9NsELGkuP'''; - expect(secretKey, equals(expectedSecretKey)); - }); - }); - group('from secretKey', () { - const String secretKey = - '''edskRrmNgPfAAvbZyzTptfvTju9X7ooLR5VVN9u8sXA42hXdMBd8CgrhykP7sZQf8hWLCYuqfEoWUFzL6Us3aKtMD9NsELGkuP'''; - - test('jwk', () async { - final jwk = await keyGenerator.jwkFromSecretKey( - secretKey: secretKey, - accountType: AccountType.tezos, - ); - const expectedJwk = - '''{"kty":"OKP","crv":"Ed25519","d":"cMGD8eAmjDn6MqvJoscsaPoyAMrjG41xbLDfE-uQkYw=","x":"-PeGBkVyMz2-yketwH2lbQqiflneee3jmaTafMCsURE="}'''; - expect(jwk, equals(expectedJwk)); - }); - - test('address', () async { - final address = await keyGenerator.walletAddressFromSecretKey( - secretKey: secretKey, - accountType: AccountType.ssi, - ); - const expectedAddress = 'tz1NvqicaUW7v6sEbM4UYi3Wes7GHDft4kqY'; - expect(address, equals(expectedAddress)); - }); - }); -} diff --git a/test/onboarding/gen_phrase/view/onboarding_gen_phrase_state_test.dart b/test/onboarding/gen_phrase/view/onboarding_gen_phrase_state_test.dart new file mode 100644 index 000000000..616c498b3 --- /dev/null +++ b/test/onboarding/gen_phrase/view/onboarding_gen_phrase_state_test.dart @@ -0,0 +1,51 @@ +import 'package:altme/app/app.dart'; +import 'package:altme/onboarding/onboarding.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('OnBoardingGenPhraseState', () { + test('toJson/fromJson works correctly', () { + const state = OnBoardingGenPhraseState( + status: AppStatus.loading, + isTicked: true, + ); + final json = state.toJson(); + final newState = OnBoardingGenPhraseState.fromJson(json); + + expect(newState, state); + }); + + test('loading returns correct state', () { + final state = const OnBoardingGenPhraseState().loading(); + expect(state.status, AppStatus.loading); + expect(state.isTicked, false); + }); + + test('error returns correct state', () { + final messageHandler = ResponseMessage( + message: ResponseString + .RESPONSE_STRING_AN_ERROR_OCCURRED_WHILE_CONNECTING_TO_THE_SERVER, + ); + + final state = const OnBoardingGenPhraseState() + .error(messageHandler: messageHandler); + + expect(state.status, AppStatus.error); + expect(state.message, StateMessage.error(messageHandler: messageHandler)); + expect(state.isTicked, false); + }); + + test('success returns correct state', () { + final messageHandler = ResponseMessage(); + final state = const OnBoardingGenPhraseState() + .success(messageHandler: messageHandler); + + expect(state.status, AppStatus.success); + expect( + state.message, + StateMessage.success(messageHandler: messageHandler), + ); + expect(state.isTicked, false); + }); + }); +} diff --git a/test/onboarding/gen_phrase/view/onboarding_gen_phrase_test.dart b/test/onboarding/gen_phrase/view/onboarding_gen_phrase_test.dart new file mode 100644 index 000000000..7bc14632e --- /dev/null +++ b/test/onboarding/gen_phrase/view/onboarding_gen_phrase_test.dart @@ -0,0 +1,226 @@ +import 'package:altme/app/app.dart'; +import 'package:altme/chat_room/chat_room.dart'; +import 'package:altme/dashboard/dashboard.dart'; +import 'package:altme/onboarding/cubit/onboarding_cubit.dart'; +import 'package:altme/onboarding/onboarding.dart'; +import 'package:altme/splash/splash.dart'; +import 'package:altme/wallet/wallet.dart'; +import 'package:bloc_test/bloc_test.dart'; +import 'package:did_kit/did_kit.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:key_generator/key_generator.dart'; +import 'package:mockingjay/mockingjay.dart'; + +import '../../../helpers/helpers.dart'; + +class MockDIDKitProvider extends Mock implements DIDKitProvider {} + +class MockKeyGenerator extends Mock implements KeyGenerator {} + +class MockHomeCubit extends MockCubit implements HomeCubit { + @override + Future emitHasWallet() async {} +} + +class MockWalletCubit extends MockCubit implements WalletCubit { + @override + final state = WalletState( + cryptoAccount: CryptoAccount( + data: [ + CryptoAccountData( + name: '', + secretKey: '', + blockchainType: BlockchainType.tezos, + walletAddress: '', + ), + ], + ), + ); + + @override + Future createCryptoWallet({ + String? accountName, + required String mnemonicOrKey, + required bool isImported, + required bool isFromOnboarding, + BlockchainType? blockchainType, + bool showStatus = true, + void Function({ + required CryptoAccount cryptoAccount, + required MessageHandler messageHandler, + })? onComplete, + }) async {} +} + +class MockSplashCubit extends MockCubit implements SplashCubit {} + +class MockAltmeChatSupportCubit extends MockCubit + implements AltmeChatSupportCubit {} + +class MockProfileCubit extends MockCubit implements ProfileCubit { + @override + final state = ProfileState(model: ProfileModel.empty()); +} + +void main() { + late DIDKitProvider didKitProvider; + late KeyGenerator keyGenerator; + late HomeCubit homeCubit; + late WalletCubit walletCubit; + late SplashCubit splashCubit; + late AltmeChatSupportCubit altmeChatSupportCubit; + late ProfileCubit profileCubit; + late OnboardingCubit onboardingCubit; + + setUpAll(() { + WidgetsFlutterBinding.ensureInitialized(); + didKitProvider = MockDIDKitProvider(); + keyGenerator = MockKeyGenerator(); + homeCubit = MockHomeCubit(); + splashCubit = MockSplashCubit(); + walletCubit = MockWalletCubit(); + altmeChatSupportCubit = MockAltmeChatSupportCubit(); + profileCubit = MockProfileCubit(); + onboardingCubit = OnboardingCubit(); + }); + + group('OnBoarding GenPhrase Page', () { + late OnBoardingGenPhraseCubit onBoardingGenPhraseCubit; + + late MockNavigator navigator; + + setUpAll(() { + onBoardingGenPhraseCubit = OnBoardingGenPhraseCubit( + didKitProvider: didKitProvider, + keyGenerator: keyGenerator, + homeCubit: homeCubit, + walletCubit: walletCubit, + splashCubit: splashCubit, + altmeChatSupportCubit: altmeChatSupportCubit, + profileCubit: profileCubit, + ); + navigator = MockNavigator(); + when(navigator.canPop).thenReturn(true); + + when(() => navigator.push(any())).thenAnswer((_) async {}); + }); + + testWidgets('is routable', (tester) async { + await tester.pumpApp( + MockNavigatorProvider( + navigator: navigator, + child: Builder( + builder: (context) => Scaffold( + floatingActionButton: FloatingActionButton( + onPressed: () { + Navigator.of(context).push( + OnBoardingGenPhrasePage.route(), + ); + }, + ), + ), + ), + ), + ); + await tester.tap(find.byType(FloatingActionButton)); + await tester.pumpAndSettle(); + + verify( + () => navigator.push( + any( + that: isRoute( + whereName: equals('/onBoardingGenPhrasePage'), + ), + ), + ), + ).called(1); + }); + + testWidgets('renders OnBoardingGenPhraseView', (tester) async { + await tester.pumpApp( + MultiBlocProvider( + providers: [ + BlocProvider.value( + value: onBoardingGenPhraseCubit, + ), + BlocProvider.value(value: homeCubit), + BlocProvider.value(value: walletCubit), + BlocProvider.value(value: splashCubit), + BlocProvider.value( + value: altmeChatSupportCubit, + ), + BlocProvider.value(value: profileCubit), + ], + child: const OnBoardingGenPhrasePage(), + ), + ); + + expect(find.byType(OnBoardingGenPhraseView), findsOneWidget); + }); + + testWidgets('renders UI correctly', (tester) async { + await tester.pumpApp( + BlocProvider.value( + value: onBoardingGenPhraseCubit, + child: const OnBoardingGenPhraseView(), + ), + ); + + expect(find.byType(BasePage), findsOneWidget); + expect(find.byType(MStepper), findsOneWidget); + expect(find.byType(MnemonicDisplay), findsOneWidget); + expect(find.byType(MyElevatedButton), findsOneWidget); + expect(find.byType(MyGradientButton), findsOneWidget); + }); + + testWidgets( + 'navigates to OnBoardingVerifyPhrasePage when Verify Now button' + ' is tapped', (tester) async { + await tester.pumpApp( + MockNavigatorProvider( + navigator: navigator, + child: BlocProvider.value( + value: onBoardingGenPhraseCubit, + child: const OnBoardingGenPhraseView(), + ), + ), + ); + + await tester.tap(find.text('Verify Now'.toUpperCase())); + + verify( + () => navigator.push( + any( + that: isRoute( + whereName: equals('/OnBoardingVerifyPhrasePage'), + ), + ), + ), + ).called(1); + }); + + testWidgets('Verify Later button triggers onboarding processing', + (WidgetTester tester) async { + await tester.pumpApp( + MultiBlocProvider( + providers: [ + BlocProvider.value( + value: onboardingCubit, + ), + BlocProvider.value( + value: onBoardingGenPhraseCubit, + ), + ], + child: const OnBoardingGenPhraseView(), + ), + ); + await tester.tap(find.text('Verify Later'.toUpperCase())); + + // Verify the real Cubit method call + expect(onBoardingGenPhraseCubit.state.message, isNotNull); + expect(onboardingCubit.state.status, AppStatus.loading); + }); + }); +} diff --git a/test/onboarding/helper_function/helper_function_test.dart b/test/onboarding/helper_function/helper_function_test.dart new file mode 100644 index 000000000..8b06a4ca4 --- /dev/null +++ b/test/onboarding/helper_function/helper_function_test.dart @@ -0,0 +1,154 @@ +import 'package:altme/app/app.dart'; +import 'package:altme/chat_room/chat_room.dart'; +import 'package:altme/dashboard/dashboard.dart'; +import 'package:altme/lang/cubit/lang_cubit.dart'; +import 'package:altme/lang/cubit/lang_state.dart'; +import 'package:altme/onboarding/onboarding.dart'; +import 'package:altme/splash/splash.dart'; +import 'package:altme/wallet/wallet.dart'; +import 'package:bloc_test/bloc_test.dart'; +import 'package:did_kit/did_kit.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:jwt_decode/jwt_decode.dart'; +import 'package:key_generator/key_generator.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:oidc4vc/oidc4vc.dart'; + +import 'package:secure_storage/secure_storage.dart'; + +class MockDIDKitProvider extends Mock implements DIDKitProvider {} + +class MockHomeCubit extends MockCubit implements HomeCubit {} + +class MockWalletCubit extends MockCubit implements WalletCubit { + @override + final state = WalletState( + cryptoAccount: CryptoAccount( + data: [ + CryptoAccountData( + name: '', + secretKey: '', + blockchainType: BlockchainType.tezos, + walletAddress: '', + ), + ], + ), + ); + + @override + Future createCryptoWallet({ + String? accountName, + required String mnemonicOrKey, + required bool isImported, + required bool isFromOnboarding, + BlockchainType? blockchainType, + bool showStatus = true, + void Function({ + required CryptoAccount cryptoAccount, + required MessageHandler messageHandler, + })? onComplete, + }) async {} +} + +class MockSplashCubit extends MockCubit implements SplashCubit {} + +class MockAltmeChatSupportCubit extends MockCubit + implements AltmeChatSupportCubit {} + +class MockSecureStorageProvider extends Mock implements SecureStorageProvider {} + +class MockLangCubit extends MockCubit implements LangCubit {} + +class MockOIDC4VC extends Mock implements OIDC4VC {} + +void main() { + group('generateAccount', () { + late KeyGenerator keyGenerator; + late MockDIDKitProvider didKitProvider; + late MockHomeCubit homeCubit; + late MockWalletCubit walletCubit; + late MockSplashCubit splashCubit; + late MockAltmeChatSupportCubit altmeChatSupportCubit; + late ProfileCubit profileCubit; + late MockSecureStorageProvider secureStorageProvider; + + setUp(() { + keyGenerator = KeyGenerator(); + didKitProvider = MockDIDKitProvider(); + homeCubit = MockHomeCubit(); + walletCubit = MockWalletCubit(); + splashCubit = MockSplashCubit(); + altmeChatSupportCubit = MockAltmeChatSupportCubit(); + secureStorageProvider = MockSecureStorageProvider(); + + when(() => secureStorageProvider.get(any())).thenAnswer((_) async => ''); + + when(() => secureStorageProvider.set(any(), any())) + .thenAnswer((_) async => Future.value()); + + profileCubit = ProfileCubit( + didKitProvider: didKitProvider, + jwtDecode: JWTDecode(), + oidc4vc: MockOIDC4VC(), + secureStorageProvider: secureStorageProvider, + langCubit: MockLangCubit(), + ); + }); + + const mnemonicString = 'notice photo opera keen climb' + ' agent soft parrot best joke field devote'; + + test('should generate account correctly', () async { + when(() => homeCubit.emitHasWallet()).thenAnswer((_) async => {}); + await generateAccount( + mnemonic: mnemonicString.split(' '), + keyGenerator: keyGenerator, + didKitProvider: didKitProvider, + homeCubit: homeCubit, + walletCubit: walletCubit, + splashCubit: splashCubit, + altmeChatSupportCubit: altmeChatSupportCubit, + profileCubit: profileCubit, + ); + + verify( + () => secureStorageProvider.set( + SecureStorageKeys.ssiMnemonic, + mnemonicString, + ), + ).called(1); + + verify(() => homeCubit.emitHasWallet()).called(1); + }); + + test('should initiate chat if enterprise wallet', () async { + when(() => homeCubit.emitHasWallet()).thenAnswer((_) async => {}); + when(() => altmeChatSupportCubit.init()).thenAnswer((_) async => {}); + + await profileCubit.update( + ProfileModel( + polygonIdNetwork: PolygonIdNetwork.PolygonMainnet, + walletType: WalletType.enterprise, + walletProtectionType: WalletProtectionType.pinCode, + isDeveloperMode: false, + profileType: ProfileType.custom, + profileSetting: ProfileSetting.initial(), + ), + ); + + await generateAccount( + mnemonic: mnemonicString.split(' '), + keyGenerator: keyGenerator, + didKitProvider: didKitProvider, + homeCubit: homeCubit, + walletCubit: walletCubit, + splashCubit: splashCubit, + altmeChatSupportCubit: altmeChatSupportCubit, + profileCubit: profileCubit, + ); + + expect(profileCubit.state.model.walletType, WalletType.enterprise); + verify(() => altmeChatSupportCubit.init()).called(1); + }); + }); +} diff --git a/test/onboarding/protect_wallet/view/protect_wallet_page_test.dart b/test/onboarding/protect_wallet/view/protect_wallet_page_test.dart new file mode 100644 index 000000000..ced9e2d88 --- /dev/null +++ b/test/onboarding/protect_wallet/view/protect_wallet_page_test.dart @@ -0,0 +1,699 @@ +import 'dart:convert'; + +import 'package:altme/app/app.dart'; +import 'package:altme/chat_room/chat_room.dart'; +import 'package:altme/dashboard/dashboard.dart'; +import 'package:altme/lang/cubit/lang_state.dart'; +import 'package:altme/lang/lang.dart'; +import 'package:altme/onboarding/cubit/onboarding_cubit.dart'; +import 'package:altme/onboarding/onboarding.dart'; +import 'package:altme/splash/splash.dart'; +import 'package:altme/wallet/wallet.dart'; +import 'package:bloc_test/bloc_test.dart'; +import 'package:did_kit/did_kit.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:jwt_decode/jwt_decode.dart'; +import 'package:key_generator/key_generator.dart'; +import 'package:mockingjay/mockingjay.dart'; +import 'package:oidc4vc/oidc4vc.dart'; +import 'package:secure_storage/secure_storage.dart'; + +import '../../../helpers/helpers.dart'; + +class MockDIDKitProvider extends Mock implements DIDKitProvider {} + +class MockHomeCubit extends MockCubit implements HomeCubit { + @override + Future emitHasWallet() async {} +} + +class MockWalletCubit extends MockCubit implements WalletCubit { + @override + final state = WalletState( + cryptoAccount: CryptoAccount( + data: [ + CryptoAccountData( + name: '', + secretKey: '', + blockchainType: BlockchainType.tezos, + walletAddress: '', + ), + ], + ), + ); + + @override + Future createCryptoWallet({ + String? accountName, + required String mnemonicOrKey, + required bool isImported, + required bool isFromOnboarding, + BlockchainType? blockchainType, + bool showStatus = true, + void Function({ + required CryptoAccount cryptoAccount, + required MessageHandler messageHandler, + })? onComplete, + }) async {} +} + +class MockSplashCubit extends MockCubit implements SplashCubit {} + +class MockAltmeChatSupportCubit extends MockCubit + implements AltmeChatSupportCubit {} + +class MockSecureStorageProvider extends Mock implements SecureStorageProvider {} + +class MockLangCubit extends MockCubit implements LangCubit {} + +class MockOIDC4VC extends Mock implements OIDC4VC {} + +void main() { + late DIDKitProvider didKitProvider; + late KeyGenerator keyGenerator; + late HomeCubit homeCubit; + late WalletCubit walletCubit; + late SplashCubit splashCubit; + late AltmeChatSupportCubit altmeChatSupportCubit; + late OnboardingCubit onboardingCubit; + late MockSecureStorageProvider secureStorageProvider; + late MockOIDC4VC oidc4vc; + + const mnemonicString = + 'notice photo opera keen climb agent soft parrot best joke field devote'; + + final expectedP256Jwk = { + 'kty': 'EC', + 'crv': 'P-256', + 'd': 's_wb6Ef1ardGsT5Il6WLRvQ9Zu0lp7I2OVwtzT5iQpo', + 'x': 'MZZjpNhZGGxqBcPXq499FVC2iu5FcZWwti5u8hgMUaI', + 'y': 'KD4zffV54PZUsQzTzVgoVlWHwKqogRF3JpKQnIGwIRM', + }; + + setUpAll(() { + WidgetsFlutterBinding.ensureInitialized(); + didKitProvider = MockDIDKitProvider(); + keyGenerator = KeyGenerator(); + homeCubit = MockHomeCubit(); + splashCubit = MockSplashCubit(); + walletCubit = MockWalletCubit(); + altmeChatSupportCubit = MockAltmeChatSupportCubit(); + onboardingCubit = OnboardingCubit(); + secureStorageProvider = MockSecureStorageProvider(); + oidc4vc = MockOIDC4VC(); + + when(() => secureStorageProvider.get(any())).thenAnswer((_) async => ''); + + when(() => secureStorageProvider.set(any(), any())) + .thenAnswer((_) async => Future.value()); + + when( + () => secureStorageProvider.get( + SecureStorageKeys.profileType, + ), + ).thenAnswer((_) async => 'ProfileType.ebsiV3'); + + when( + () => secureStorageProvider.get( + SecureStorageKeys.enterpriseProfileSetting, + ), + ).thenAnswer((_) async => jsonEncode(ProfileSetting.initial())); + + when( + () => secureStorageProvider.get( + SecureStorageKeys.ssiMnemonic, + ), + ).thenAnswer((_) async => mnemonicString); + + when( + () => oidc4vc.p256PrivateKeyFromMnemonics( + mnemonic: mnemonicString, + indexValue: 5, + ), + ).thenAnswer((_) => jsonEncode(expectedP256Jwk)); + }); + + group('ProtectWallet Page', () { + late MockNavigator navigator; + + setUpAll(() { + navigator = MockNavigator(); + + when(navigator.canPop).thenReturn(true); + + when(() => navigator.push(any())).thenAnswer((_) async {}); + + when(() => navigator.pushAndRemoveUntil(any(), any())) + .thenAnswer((_) async {}); + }); + + testWidgets('is routable', (tester) async { + await tester.pumpApp( + MockNavigatorProvider( + navigator: navigator, + child: Builder( + builder: (context) => Scaffold( + floatingActionButton: FloatingActionButton( + onPressed: () { + Navigator.of(context).push( + ProtectWalletPage.route(), + ); + }, + ), + ), + ), + ), + ); + await tester.tap(find.byType(FloatingActionButton)); + await tester.pumpAndSettle(); + + verify( + () => navigator.push( + any( + that: isRoute( + whereName: equals('/ProtectWalletPage'), + ), + ), + ), + ).called(1); + }); + + testWidgets('renders ProtectWalletView', (tester) async { + await tester.pumpApp( + MultiBlocProvider( + providers: [ + BlocProvider.value( + value: OnBoardingGenPhraseCubit( + didKitProvider: didKitProvider, + keyGenerator: keyGenerator, + homeCubit: homeCubit, + walletCubit: walletCubit, + splashCubit: splashCubit, + altmeChatSupportCubit: altmeChatSupportCubit, + profileCubit: ProfileCubit( + didKitProvider: didKitProvider, + jwtDecode: JWTDecode(), + oidc4vc: oidc4vc, + secureStorageProvider: secureStorageProvider, + langCubit: MockLangCubit(), + ), + ), + ), + BlocProvider.value(value: homeCubit), + BlocProvider.value(value: walletCubit), + BlocProvider.value(value: splashCubit), + BlocProvider.value( + value: altmeChatSupportCubit, + ), + BlocProvider.value( + value: ProfileCubit( + didKitProvider: didKitProvider, + jwtDecode: JWTDecode(), + oidc4vc: oidc4vc, + secureStorageProvider: secureStorageProvider, + langCubit: MockLangCubit(), + ), + ), + BlocProvider.value(value: onboardingCubit), + ], + child: const ProtectWalletPage(), + ), + ); + + expect(find.byType(ProtectWalletView), findsOneWidget); + }); + + testWidgets('renders UI correctly', (tester) async { + final profileCubit = ProfileCubit( + didKitProvider: didKitProvider, + jwtDecode: JWTDecode(), + oidc4vc: oidc4vc, + secureStorageProvider: secureStorageProvider, + langCubit: MockLangCubit(), + ); + + final onBoardingGenPhraseCubit = OnBoardingGenPhraseCubit( + didKitProvider: didKitProvider, + keyGenerator: keyGenerator, + homeCubit: homeCubit, + walletCubit: walletCubit, + splashCubit: splashCubit, + altmeChatSupportCubit: altmeChatSupportCubit, + profileCubit: profileCubit, + ); + + await tester.pumpApp( + MultiBlocProvider( + providers: [ + BlocProvider.value( + value: onBoardingGenPhraseCubit, + ), + BlocProvider( + create: (context) => profileCubit, + ), + ], + child: ProtectWalletView( + profileCubit: profileCubit, + onBoardingGenPhraseCubit: onBoardingGenPhraseCubit, + onboardingCubit: onboardingCubit, + ), + ), + ); + + expect(find.byType(BasePage), findsOneWidget); + expect(find.byType(ProtectWidget), findsNWidgets(3)); + }); + + testWidgets( + 'navigates to OnBoardingVerifyPhrasePage when Verify Now button' + ' is tapped', (tester) async { + final profileCubit = ProfileCubit( + didKitProvider: didKitProvider, + jwtDecode: JWTDecode(), + oidc4vc: oidc4vc, + secureStorageProvider: secureStorageProvider, + langCubit: MockLangCubit(), + ); + + final onBoardingGenPhraseCubit = OnBoardingGenPhraseCubit( + didKitProvider: didKitProvider, + keyGenerator: keyGenerator, + homeCubit: homeCubit, + walletCubit: walletCubit, + splashCubit: splashCubit, + altmeChatSupportCubit: altmeChatSupportCubit, + profileCubit: profileCubit, + ); + + await tester.pumpApp( + MockNavigatorProvider( + navigator: navigator, + child: BlocProvider.value( + value: onBoardingGenPhraseCubit, + child: const OnBoardingGenPhraseView(), + ), + ), + ); + + await tester.tap(find.text('Verify Now'.toUpperCase())); + + verify( + () => navigator.push( + any( + that: isRoute( + whereName: equals('/OnBoardingVerifyPhrasePage'), + ), + ), + ), + ).called(1); + }); + + testWidgets('should navigate to EnterNewPinCodePage on PIN Unlock tap', + (WidgetTester tester) async { + final profileCubit = ProfileCubit( + didKitProvider: didKitProvider, + jwtDecode: JWTDecode(), + oidc4vc: oidc4vc, + secureStorageProvider: secureStorageProvider, + langCubit: MockLangCubit(), + ); + + final onBoardingGenPhraseCubit = OnBoardingGenPhraseCubit( + didKitProvider: didKitProvider, + keyGenerator: keyGenerator, + homeCubit: homeCubit, + walletCubit: walletCubit, + splashCubit: splashCubit, + altmeChatSupportCubit: altmeChatSupportCubit, + profileCubit: profileCubit, + ); + + await tester.pumpApp( + MockNavigatorProvider( + navigator: navigator, + child: MultiBlocProvider( + providers: [ + BlocProvider.value( + value: onBoardingGenPhraseCubit, + ), + BlocProvider( + create: (context) => profileCubit, + ), + ], + child: ProtectWalletView( + profileCubit: profileCubit, + onBoardingGenPhraseCubit: onBoardingGenPhraseCubit, + onboardingCubit: onboardingCubit, + ), + ), + ), + ); + + await tester.tap(find.text('PIN unlock')); + + verify( + () => navigator.push( + any( + that: isRoute( + whereName: equals('/enterPinCodePage'), + ), + ), + ), + ).called(1); + }); + + testWidgets( + 'should navigate to ActiviateBiometricsPage on Biometric unlock tap', + (WidgetTester tester) async { + final profileCubit = ProfileCubit( + didKitProvider: didKitProvider, + jwtDecode: JWTDecode(), + oidc4vc: oidc4vc, + secureStorageProvider: secureStorageProvider, + langCubit: MockLangCubit(), + ); + + final onBoardingGenPhraseCubit = OnBoardingGenPhraseCubit( + didKitProvider: didKitProvider, + keyGenerator: keyGenerator, + homeCubit: homeCubit, + walletCubit: walletCubit, + splashCubit: splashCubit, + altmeChatSupportCubit: altmeChatSupportCubit, + profileCubit: profileCubit, + ); + + await tester.pumpApp( + MockNavigatorProvider( + navigator: navigator, + child: MultiBlocProvider( + providers: [ + BlocProvider.value( + value: onBoardingGenPhraseCubit, + ), + BlocProvider( + create: (context) => profileCubit, + ), + ], + child: ProtectWalletView( + profileCubit: profileCubit, + onBoardingGenPhraseCubit: onBoardingGenPhraseCubit, + onboardingCubit: onboardingCubit, + ), + ), + ), + ); + + await tester.tap(find.text('Biometric unlock')); + + verify( + () => navigator.push( + any( + that: isRoute( + whereName: equals('/activiateBiometricsPage'), + ), + ), + ), + ).called(1); + }); + + testWidgets( + 'should navigate to ActiviateBiometricsPage on' + ' PIN unlock + Biometric (2FA) tap', (WidgetTester tester) async { + final profileCubit = ProfileCubit( + didKitProvider: didKitProvider, + jwtDecode: JWTDecode(), + oidc4vc: oidc4vc, + secureStorageProvider: secureStorageProvider, + langCubit: MockLangCubit(), + ); + + final onBoardingGenPhraseCubit = OnBoardingGenPhraseCubit( + didKitProvider: didKitProvider, + keyGenerator: keyGenerator, + homeCubit: homeCubit, + walletCubit: walletCubit, + splashCubit: splashCubit, + altmeChatSupportCubit: altmeChatSupportCubit, + profileCubit: profileCubit, + ); + + await tester.pumpApp( + MockNavigatorProvider( + navigator: navigator, + child: MultiBlocProvider( + providers: [ + BlocProvider.value( + value: onBoardingGenPhraseCubit, + ), + BlocProvider( + create: (context) => profileCubit, + ), + ], + child: ProtectWalletView( + profileCubit: profileCubit, + onBoardingGenPhraseCubit: onBoardingGenPhraseCubit, + onboardingCubit: onboardingCubit, + ), + ), + ), + ); + + await tester.tap(find.text('PIN unlock + Biometric (2FA)')); + + verify( + () => navigator.push( + any( + that: isRoute( + whereName: equals('/enterPinCodePage'), + ), + ), + ), + ).called(1); + }); + + testWidgets( + 'createImportAccount when routeType is WalletRouteType.create' + ' and byPassScreen is false', (WidgetTester tester) async { + final profileCubit = ProfileCubit( + didKitProvider: didKitProvider, + jwtDecode: JWTDecode(), + oidc4vc: oidc4vc, + secureStorageProvider: secureStorageProvider, + langCubit: MockLangCubit(), + ); + + final onBoardingGenPhraseCubit = OnBoardingGenPhraseCubit( + didKitProvider: didKitProvider, + keyGenerator: keyGenerator, + homeCubit: homeCubit, + walletCubit: walletCubit, + splashCubit: splashCubit, + altmeChatSupportCubit: altmeChatSupportCubit, + profileCubit: profileCubit, + ); + + await tester.pumpApp( + MockNavigatorProvider( + navigator: navigator, + child: MultiBlocProvider( + providers: [ + BlocProvider.value( + value: onBoardingGenPhraseCubit, + ), + BlocProvider( + create: (context) => profileCubit, + ), + ], + child: ProtectWalletView( + profileCubit: profileCubit, + onBoardingGenPhraseCubit: onBoardingGenPhraseCubit, + onboardingCubit: onboardingCubit, + routeType: WalletRouteType.create, + ), + ), + ), + ); + + final dynamic state = tester.state(find.byType(ProtectWalletView)); + await state.createImportAccount(byPassScreen: false); + verify( + () => navigator.push( + any( + that: isRoute( + whereName: equals('/onBoardingGenPhrasePage'), + ), + ), + ), + ).called(1); + }); + + testWidgets( + 'createImportAccount when routeType is WalletRouteType.create' + ' and byPassScreen is true', (WidgetTester tester) async { + final profileCubit = ProfileCubit( + didKitProvider: didKitProvider, + jwtDecode: JWTDecode(), + oidc4vc: oidc4vc, + secureStorageProvider: secureStorageProvider, + langCubit: MockLangCubit(), + ); + + final onBoardingGenPhraseCubit = OnBoardingGenPhraseCubit( + didKitProvider: didKitProvider, + keyGenerator: keyGenerator, + homeCubit: homeCubit, + walletCubit: walletCubit, + splashCubit: splashCubit, + altmeChatSupportCubit: altmeChatSupportCubit, + profileCubit: profileCubit, + ); + + await tester.pumpApp( + MockNavigatorProvider( + navigator: navigator, + child: MultiBlocProvider( + providers: [ + BlocProvider.value( + value: onBoardingGenPhraseCubit, + ), + BlocProvider( + create: (context) => ProfileCubit( + didKitProvider: didKitProvider, + jwtDecode: JWTDecode(), + oidc4vc: oidc4vc, + secureStorageProvider: secureStorageProvider, + langCubit: MockLangCubit(), + ), + ), + ], + child: ProtectWalletView( + profileCubit: profileCubit, + onBoardingGenPhraseCubit: onBoardingGenPhraseCubit, + onboardingCubit: onboardingCubit, + routeType: WalletRouteType.create, + ), + ), + ), + ); + + final dynamic state = tester.state(find.byType(ProtectWalletView)); + await state.createImportAccount(byPassScreen: true); + expect(onBoardingGenPhraseCubit.state.status, AppStatus.success); + + await tester.pumpAndSettle(); + verify( + () => navigator.pushAndRemoveUntil( + any( + that: isRoute( + whereName: equals('/walletReadyPage'), + ), + ), + any(that: isA()), + ), + ).called(1); + }); + + testWidgets('createImportAccount when routeType is WalletRouteType.import', + (WidgetTester tester) async { + final profileCubit = ProfileCubit( + didKitProvider: didKitProvider, + jwtDecode: JWTDecode(), + oidc4vc: oidc4vc, + secureStorageProvider: secureStorageProvider, + langCubit: MockLangCubit(), + ); + + final onBoardingGenPhraseCubit = OnBoardingGenPhraseCubit( + didKitProvider: didKitProvider, + keyGenerator: keyGenerator, + homeCubit: homeCubit, + walletCubit: walletCubit, + splashCubit: splashCubit, + altmeChatSupportCubit: altmeChatSupportCubit, + profileCubit: profileCubit, + ); + + await tester.pumpApp( + MockNavigatorProvider( + navigator: navigator, + child: MultiBlocProvider( + providers: [ + BlocProvider.value( + value: onBoardingGenPhraseCubit, + ), + BlocProvider( + create: (context) => profileCubit, + ), + ], + child: ProtectWalletView( + profileCubit: profileCubit, + onBoardingGenPhraseCubit: onBoardingGenPhraseCubit, + onboardingCubit: onboardingCubit, + routeType: WalletRouteType.import, + ), + ), + ), + ); + + final dynamic state = tester.state(find.byType(ProtectWalletView)); + await state.createImportAccount(byPassScreen: false); + verify( + () => navigator.push( + any( + that: isRoute( + whereName: equals('/ImportWalletPage'), + ), + ), + ), + ).called(1); + }); + + testWidgets('correct value of byPassscreen', (WidgetTester tester) async { + final profileCubit = ProfileCubit( + didKitProvider: didKitProvider, + jwtDecode: JWTDecode(), + oidc4vc: oidc4vc, + secureStorageProvider: secureStorageProvider, + langCubit: MockLangCubit(), + ); + + final onBoardingGenPhraseCubit = OnBoardingGenPhraseCubit( + didKitProvider: didKitProvider, + keyGenerator: keyGenerator, + homeCubit: homeCubit, + walletCubit: walletCubit, + splashCubit: splashCubit, + altmeChatSupportCubit: altmeChatSupportCubit, + profileCubit: profileCubit, + ); + + await tester.pumpApp( + MockNavigatorProvider( + navigator: navigator, + child: MultiBlocProvider( + providers: [ + BlocProvider.value( + value: onBoardingGenPhraseCubit, + ), + BlocProvider( + create: (context) => profileCubit, + ), + ], + child: ProtectWalletView( + profileCubit: profileCubit, + onBoardingGenPhraseCubit: onBoardingGenPhraseCubit, + onboardingCubit: onboardingCubit, + routeType: WalletRouteType.import, + ), + ), + ), + ); + + final dynamic state = tester.state(find.byType(ProtectWalletView)); + expect(state.byPassScreen, !Parameters.walletHandlesCrypto); + }); + }); +} diff --git a/test/onboarding/starter/view/starter_page_test.dart b/test/onboarding/starter/view/starter_page_test.dart new file mode 100644 index 000000000..e594d6a2a --- /dev/null +++ b/test/onboarding/starter/view/starter_page_test.dart @@ -0,0 +1,149 @@ +import 'package:altme/app/app.dart'; + +import 'package:altme/dashboard/dashboard.dart'; +import 'package:altme/flavor/flavor.dart'; +import 'package:altme/onboarding/onboarding.dart'; +import 'package:bloc_test/bloc_test.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockingjay/mockingjay.dart'; + +import '../../../helpers/helpers.dart'; + +class MockProfileCubit extends MockCubit implements ProfileCubit { + @override + Future setProfileSetting({ + required ProfileSetting profileSetting, + required ProfileType profileType, + }) async {} +} + +class MockFlavorCubit extends MockCubit implements FlavorCubit { + @override + final state = FlavorMode.development; +} + +void main() { + group('Starter Page Test', () { + late FlavorCubit mockFlavorCubit; + late MockProfileCubit profileCubit; + late MockNavigator navigator; + + setUpAll(() { + mockFlavorCubit = MockFlavorCubit(); + profileCubit = MockProfileCubit(); + navigator = MockNavigator(); + when(navigator.canPop).thenReturn(true); + + when(() => navigator.push(any())).thenAnswer((_) async {}); + + when(() => profileCubit.state) + .thenReturn(ProfileState(model: ProfileModel.empty())); + + when( + () => profileCubit.setWalletType( + walletType: WalletType.personal, + ), + ).thenAnswer((_) async {}); + + // when( + // () => profileCubit.setProfileSetting( + // profileSetting: profileSetting, + // profileType: ProfileType.defaultOne, + // ), + // ).thenAnswer((_) async {}); + }); + + testWidgets( + 'acctions are performed correctly when Create account is pressed', + (tester) async { + await tester.pumpApp( + MockNavigatorProvider( + navigator: navigator, + child: MultiBlocProvider( + providers: [ + BlocProvider.value( + value: profileCubit, + ), + BlocProvider( + create: (context) => mockFlavorCubit, + ), + ], + child: StarterView(profileCubit: profileCubit), + ), + ), + ); + + await tester.tap(find.text('Create account'.toUpperCase())); + + verify( + () => profileCubit.setWalletType( + walletType: WalletType.personal, + ), + ).called(1); + + // verify( + // () => profileCubit.setProfileSetting( + // profileSetting: profileSetting, + // profileType: ProfileType.defaultOne, + // ), + // ).called(1); + + verify( + () => navigator.push( + any( + that: isRoute( + whereName: equals('/ProtectWalletPage'), + ), + ), + ), + ).called(1); + }); + + testWidgets( + 'acctions are performed correctly when Import account is pressed', + (tester) async { + await tester.pumpApp( + MockNavigatorProvider( + navigator: navigator, + child: MultiBlocProvider( + providers: [ + BlocProvider.value( + value: profileCubit, + ), + BlocProvider( + create: (context) => mockFlavorCubit, + ), + ], + child: StarterView(profileCubit: profileCubit), + ), + ), + ); + + await tester.tap(find.text('Import account'.toUpperCase())); + + verify( + () => profileCubit.setWalletType( + walletType: WalletType.personal, + ), + ).called(1); + + // verify( + // () => profileCubit.setProfileSetting( + // profileSetting: profileSetting, + // profileType: ProfileType.defaultOne, + // ), + // ).called(1); + + verify( + () => navigator.push( + any( + that: isRoute( + whereName: equals('/ProtectWalletPage'), + ), + ), + ), + ).called(1); + }); + }); +} diff --git a/test/onboarding/tos/cubit/onboarding_tos_cubit_test.dart b/test/onboarding/tos/cubit/onboarding_tos_cubit_test.dart new file mode 100644 index 000000000..58ae82d34 --- /dev/null +++ b/test/onboarding/tos/cubit/onboarding_tos_cubit_test.dart @@ -0,0 +1,51 @@ +import 'package:altme/onboarding/onboarding.dart'; +import 'package:bloc_test/bloc_test.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('OnBoardingTosCubit', () { + late OnBoardingTosCubit onBoardingTosCubit; + + setUp(() { + onBoardingTosCubit = OnBoardingTosCubit(); + }); + + tearDown(() { + onBoardingTosCubit.close(); + }); + + test('initial state is correct', () { + expect(onBoardingTosCubit.state, equals(const OnBoardingTosState())); + }); + + blocTest( + 'emits [scrollIsOver] when setScrolledIsOver is called', + build: () => onBoardingTosCubit, + act: (cubit) => cubit.setScrolledIsOver(scrollIsOver: true), + expect: () => [const OnBoardingTosState(scrollIsOver: true)], + ); + + blocTest( + 'emits [agreeTerms] when setAgreeTerms is called', + build: () => onBoardingTosCubit, + act: (cubit) => cubit.setAgreeTerms(agreeTerms: true), + expect: () => [const OnBoardingTosState(agreeTerms: true)], + ); + + blocTest( + 'emits [readTerms] when setReadTerms is called', + build: () => onBoardingTosCubit, + act: (cubit) => cubit.setReadTerms(readTerms: true), + expect: () => [const OnBoardingTosState(readTerms: true)], + ); + + blocTest( + 'emits [acceptanceButtonEnabled] ' + 'when setAcceptanceButtonEnabled is called', + build: () => onBoardingTosCubit, + act: (cubit) => + cubit.setAcceptanceButtonEnabled(acceptanceButtonEnabled: true), + expect: () => [const OnBoardingTosState(acceptanceButtonEnabled: true)], + ); + }); +} diff --git a/test/onboarding/tos/cubit/onboarding_tos_state_test.dart b/test/onboarding/tos/cubit/onboarding_tos_state_test.dart new file mode 100644 index 000000000..0e4394b28 --- /dev/null +++ b/test/onboarding/tos/cubit/onboarding_tos_state_test.dart @@ -0,0 +1,52 @@ +import 'package:altme/onboarding/onboarding.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('OnBoardingTosState', () { + test('copyWith method should create a new instance with updated values', + () { + const state = OnBoardingTosState( + agreeTerms: true, + scrollIsOver: false, + readTerms: true, + acceptanceButtonEnabled: false, + ); + + final newState = + state.copyWith(scrollIsOver: true, acceptanceButtonEnabled: true); + + expect(newState, isNot(same(state))); + expect(newState.scrollIsOver, true); + expect(newState.acceptanceButtonEnabled, true); + expect(newState.agreeTerms, true); + expect(newState.readTerms, true); + }); + + test( + 'toJson and fromJson methods should serialize ' + 'and deserialize correctly', () { + const state = OnBoardingTosState( + agreeTerms: true, + scrollIsOver: false, + readTerms: true, + acceptanceButtonEnabled: false, + ); + + final json = state.toJson(); + final newState = OnBoardingTosState.fromJson(json); + + expect(newState, equals(state)); + }); + + test('props should return correct list of properties', () { + const state = OnBoardingTosState( + agreeTerms: true, + scrollIsOver: false, + readTerms: true, + acceptanceButtonEnabled: false, + ); + + expect(state.props, [true, false, true, false]); + }); + }); +} diff --git a/test/onboarding/tos/view/onboarding_tos_page_test.dart b/test/onboarding/tos/view/onboarding_tos_page_test.dart index 898f76fca..074ccb6a1 100644 --- a/test/onboarding/tos/view/onboarding_tos_page_test.dart +++ b/test/onboarding/tos/view/onboarding_tos_page_test.dart @@ -1,59 +1,210 @@ import 'package:altme/app/app.dart'; import 'package:altme/onboarding/onboarding.dart'; +import 'package:bloc_test/bloc_test.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:mockingjay/mockingjay.dart'; import '../../../helpers/helpers.dart'; +class MockOnBoardingTosCubit extends MockCubit + implements OnBoardingTosCubit {} + void main() { group('OnBoarding Terms Page', () { + late MockOnBoardingTosCubit onBoardingTosCubit; + late MockNavigator navigator; + + setUpAll(() { + onBoardingTosCubit = MockOnBoardingTosCubit(); + navigator = MockNavigator(); + when(navigator.canPop).thenReturn(true); + + when(() => navigator.push(any())).thenAnswer((_) async {}); + when(() => navigator.pushAndRemoveUntil(any(), any())) + .thenAnswer((_) async {}); + }); + testWidgets('is routable', (tester) async { await tester.pumpApp( - Builder( - builder: (context) => Scaffold( - floatingActionButton: FloatingActionButton( - onPressed: () { - //Navigator.of(context).push(OnBoardingTosPage.route()); - }, + MockNavigatorProvider( + navigator: navigator, + child: Builder( + builder: (context) => Scaffold( + floatingActionButton: FloatingActionButton( + onPressed: () { + Navigator.of(context).push( + OnBoardingTosPage.route(), + ); + }, + ), ), ), ), ); await tester.tap(find.byType(FloatingActionButton)); await tester.pumpAndSettle(); - expect(find.byType(OnBoardingTosPage), findsOneWidget); + verify( + () => navigator.push( + any( + that: isRoute( + whereName: equals('/onBoardingTermsPage'), + ), + ), + ), + ).called(1); }); testWidgets('renders OnBoardingTosPage', (tester) async { - await tester - .pumpApp(const OnBoardingTosPage()); + when(() => onBoardingTosCubit.state) + .thenReturn(const OnBoardingTosState()); + await tester.pumpApp( + BlocProvider.value( + value: onBoardingTosCubit, + child: const OnBoardingTosPage(), + ), + ); await tester.pumpAndSettle(); - expect(find.byType(OnBoardingTosPage), findsOneWidget); + expect(find.byType(OnBoardingTosView), findsOneWidget); }); testWidgets('nothing happens when button is pressed', (tester) async { - await tester - .pumpApp(const OnBoardingTosPage()); - await tester.tap(find.byType(MyElevatedButton)); + when(() => onBoardingTosCubit.state) + .thenReturn(const OnBoardingTosState()); + + await tester.pumpApp( + BlocProvider( + create: (context) => onBoardingTosCubit, + child: OnBoardingTosView(onBoardingTosCubit: onBoardingTosCubit), + ), + ); + + await tester.tap(find.byType(MyGradientButton)); + await tester.pumpAndSettle(); + expect(find.byType(OnBoardingTosView), findsOneWidget); + }); + + testWidgets('checkboxes toggle state', (WidgetTester tester) async { + when(() => onBoardingTosCubit.state) + .thenReturn(const OnBoardingTosState()); + + when( + () => onBoardingTosCubit.setAgreeTerms( + agreeTerms: any(named: 'agreeTerms'), + ), + ).thenAnswer((_) async {}); + + when( + () => + onBoardingTosCubit.setReadTerms(readTerms: any(named: 'readTerms')), + ).thenAnswer((_) async {}); + + await tester.pumpApp( + BlocProvider( + create: (context) => onBoardingTosCubit, + child: OnBoardingTosView(onBoardingTosCubit: onBoardingTosCubit), + ), + ); + final agreeCheckbox = find.text('I agree to the terms and conditions.'); + final readCheckbox = find.text('I have read the terms of use.'); + + expect(agreeCheckbox, findsOneWidget); + expect(readCheckbox, findsOneWidget); + + await tester.tap(agreeCheckbox); + await tester.pumpAndSettle(); + + verify(() => onBoardingTosCubit.setAgreeTerms(agreeTerms: true)) + .called(1); + + await tester.tap(readCheckbox); await tester.pumpAndSettle(); - expect(find.byType(OnBoardingTosPage), findsOneWidget); + + verify(() => onBoardingTosCubit.setReadTerms(readTerms: true)).called(1); }); testWidgets('blocks going back from OnBoardingTosPage start page', (tester) async { - await tester - .pumpApp(const OnBoardingTosPage()); + when(() => onBoardingTosCubit.state) + .thenReturn(const OnBoardingTosState()); + await tester.pumpApp( + BlocProvider( + create: (context) => onBoardingTosCubit, + child: OnBoardingTosView(onBoardingTosCubit: onBoardingTosCubit), + ), + ); final dynamic appState = tester.state(find.byType(WidgetsApp)); expect(await appState.didPopRoute(), true); await tester.pumpAndSettle(); - expect(find.byType(OnBoardingTosPage), findsOneWidget); + expect(find.byType(OnBoardingTosView), findsOneWidget); }); + testWidgets( + 'MyGradientButton press calls onAcceptancePressed', + (WidgetTester tester) async { + when(() => onBoardingTosCubit.state) + .thenReturn(const OnBoardingTosState()); + await tester.pumpApp( + BlocProvider( + create: (context) => onBoardingTosCubit, + child: OnBoardingTosView(onBoardingTosCubit: onBoardingTosCubit), + ), + ); + + when(() => onBoardingTosCubit.state).thenReturn( + const OnBoardingTosState( + agreeTerms: true, + readTerms: true, + ), + ); + await tester.pump(); + + final button = find.text('Start'.toUpperCase()); + expect(button, findsOneWidget); + }, + ); + testWidgets('renders DisplayTerms', (tester) async { - await tester - .pumpApp(const OnBoardingTosPage()); + when(() => onBoardingTosCubit.state) + .thenReturn(const OnBoardingTosState()); + await tester.pumpApp( + BlocProvider( + create: (context) => onBoardingTosCubit, + child: OnBoardingTosView(onBoardingTosCubit: onBoardingTosCubit), + ), + ); await tester.pumpAndSettle(); expect(find.byType(DisplayTermsofUse), findsOneWidget); }); + + testWidgets( + 'navigated to DashboardPage when Start is pressed' + ' when agreeTerms and readTerms are true.', (tester) async { + when(() => onBoardingTosCubit.state).thenReturn( + const OnBoardingTosState(agreeTerms: true, readTerms: true), + ); + await tester.pumpApp( + MockNavigatorProvider( + navigator: navigator, + child: BlocProvider( + create: (context) => onBoardingTosCubit, + child: OnBoardingTosView(onBoardingTosCubit: onBoardingTosCubit), + ), + ), + ); + await tester.tap(find.text('Start'.toUpperCase())); + await tester.pumpAndSettle(); + verify( + () => navigator.pushAndRemoveUntil( + any( + that: isRoute( + whereName: equals(AltMeStrings.dashBoardPage), + ), + ), + any(that: isA()), + ), + ).called(1); + }); }); } diff --git a/test/onboarding/verify_phrase/cubit/onboarding_verify_phrase_state_test.dart b/test/onboarding/verify_phrase/cubit/onboarding_verify_phrase_state_test.dart new file mode 100644 index 000000000..80d397ab9 --- /dev/null +++ b/test/onboarding/verify_phrase/cubit/onboarding_verify_phrase_state_test.dart @@ -0,0 +1,44 @@ +import 'package:altme/app/app.dart'; +import 'package:altme/onboarding/onboarding.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('OnBoardingVerifyPhraseState', () { + test('success method returns a new state with updated status and message', + () { + final state = OnBoardingVerifyPhraseState(); + final newState = state.success(messageHandler: ResponseMessage()); + + expect(newState.status, AppStatus.success); + expect(newState.message, isNotNull); + expect(newState.isVerified, state.isVerified); + expect(newState.mnemonicStates, state.mnemonicStates); + }); + + test('toJson and fromJson work correctly', () { + final state = OnBoardingVerifyPhraseState( + status: AppStatus.success, + isVerified: true, + ); + + final json = state.toJson(); + final newState = OnBoardingVerifyPhraseState.fromJson(json); + + expect(state, newState); + }); + }); + + group('MnemonicState', () { + test('toJson and fromJson work correctly', () { + const mnemonicState = MnemonicState( + mnemonicStatus: MnemonicStatus.selected, + order: 1, + ); + + final json = mnemonicState.toJson(); + final newState = MnemonicState.fromJson(json); + + expect(newState, mnemonicState); + }); + }); +} diff --git a/test/onboarding/verify_phrase/view/onboarding_verify_phrase_test.dart b/test/onboarding/verify_phrase/view/onboarding_verify_phrase_test.dart new file mode 100644 index 000000000..f10ca0792 --- /dev/null +++ b/test/onboarding/verify_phrase/view/onboarding_verify_phrase_test.dart @@ -0,0 +1,520 @@ +import 'package:altme/app/app.dart'; +import 'package:altme/chat_room/chat_room.dart'; +import 'package:altme/dashboard/dashboard.dart'; +import 'package:altme/flavor/flavor.dart'; +import 'package:altme/lang/cubit/lang_state.dart'; +import 'package:altme/lang/lang.dart'; +import 'package:altme/onboarding/cubit/onboarding_cubit.dart'; +import 'package:altme/onboarding/onboarding.dart'; +import 'package:altme/splash/splash.dart'; +import 'package:altme/wallet/wallet.dart'; +import 'package:bloc_test/bloc_test.dart'; +import 'package:did_kit/did_kit.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:jwt_decode/jwt_decode.dart'; +import 'package:key_generator/key_generator.dart'; +import 'package:mockingjay/mockingjay.dart'; +import 'package:oidc4vc/oidc4vc.dart'; +import 'package:secure_storage/secure_storage.dart'; + +import '../../../helpers/helpers.dart'; + +class MockDIDKitProvider extends Mock implements DIDKitProvider {} + +class MockHomeCubit extends MockCubit implements HomeCubit { + @override + Future emitHasWallet() async {} +} + +class MockWalletCubit extends MockCubit implements WalletCubit { + @override + final state = WalletState( + cryptoAccount: CryptoAccount( + data: [ + CryptoAccountData( + name: '', + secretKey: '', + blockchainType: BlockchainType.tezos, + walletAddress: '', + ), + ], + ), + ); + + @override + Future createCryptoWallet({ + String? accountName, + required String mnemonicOrKey, + required bool isImported, + required bool isFromOnboarding, + BlockchainType? blockchainType, + bool showStatus = true, + void Function({ + required CryptoAccount cryptoAccount, + required MessageHandler messageHandler, + })? onComplete, + }) async {} +} + +class MockSplashCubit extends MockCubit implements SplashCubit {} + +class MockFlavorCubit extends MockCubit implements FlavorCubit {} + +class MockAltmeChatSupportCubit extends MockCubit + implements AltmeChatSupportCubit {} + +class MockOnboardingCubit extends MockCubit + implements OnboardingCubit {} + +class MockSecureStorageProvider extends Mock implements SecureStorageProvider {} + +class MockLangCubit extends MockCubit implements LangCubit {} + +class MockOIDC4VC extends Mock implements OIDC4VC {} + +void main() { + late MockDIDKitProvider didKitProvider; + late KeyGenerator keyGenerator; + late MockHomeCubit homeCubit; + late MockWalletCubit walletCubit; + late MockSplashCubit splashCubit; + late MockFlavorCubit flavorCubit; + late AltmeChatSupportCubit altmeChatSupportCubit; + late MockOnboardingCubit onboardingCubit; + late MockNavigator navigator; + late MockSecureStorageProvider secureStorageProvider; + late MockOIDC4VC oidc4vc; + + const mnemonicString = + 'notice photo opera keen climb agent soft parrot best joke field devote'; + + setUpAll(() { + WidgetsFlutterBinding.ensureInitialized(); + didKitProvider = MockDIDKitProvider(); + keyGenerator = KeyGenerator(); + homeCubit = MockHomeCubit(); + walletCubit = MockWalletCubit(); + splashCubit = MockSplashCubit(); + flavorCubit = MockFlavorCubit(); + altmeChatSupportCubit = MockAltmeChatSupportCubit(); + onboardingCubit = MockOnboardingCubit(); + navigator = MockNavigator(); + secureStorageProvider = MockSecureStorageProvider(); + oidc4vc = MockOIDC4VC(); + }); + + group('Onboarding Verify Phrase Test', () { + setUpAll(() { + when(() => secureStorageProvider.get(any())).thenAnswer((_) async => ''); + + when(() => secureStorageProvider.set(any(), any())) + .thenAnswer((_) async => Future.value()); + + when(navigator.canPop).thenReturn(true); + when(() => navigator.push(any())).thenAnswer((_) async {}); + when(() => navigator.pushAndRemoveUntil(any(), any())) + .thenAnswer((_) async {}); + when(() => navigator.pushReplacement(any())) + .thenAnswer((_) async {}); + }); + + testWidgets('is routable', (tester) async { + when(() => flavorCubit.state).thenAnswer((_) => FlavorMode.development); + await tester.pumpApp( + MockNavigatorProvider( + navigator: navigator, + child: Builder( + builder: (context) => Scaffold( + floatingActionButton: FloatingActionButton( + onPressed: () { + Navigator.of(context).push( + OnBoardingVerifyPhrasePage.route( + mnemonic: [], + isFromOnboarding: true, + ), + ); + }, + ), + ), + ), + ), + ); + await tester.tap(find.byType(FloatingActionButton)); + await tester.pumpAndSettle(); + + verify( + () => navigator.push( + any( + that: isRoute( + whereName: equals('/OnBoardingVerifyPhrasePage'), + ), + ), + ), + ).called(1); + }); + + testWidgets('renders ProtectWalletView', (tester) async { + when(() => flavorCubit.state).thenAnswer((_) => FlavorMode.development); + await tester.pumpApp( + MultiBlocProvider( + providers: [ + BlocProvider.value( + value: OnBoardingVerifyPhraseCubit( + didKitProvider: didKitProvider, + keyGenerator: keyGenerator, + homeCubit: homeCubit, + walletCubit: walletCubit, + splashCubit: splashCubit, + altmeChatSupportCubit: altmeChatSupportCubit, + profileCubit: ProfileCubit( + didKitProvider: didKitProvider, + jwtDecode: JWTDecode(), + oidc4vc: oidc4vc, + secureStorageProvider: secureStorageProvider, + langCubit: MockLangCubit(), + ), + flavorCubit: flavorCubit, + ), + ), + BlocProvider.value(value: homeCubit), + BlocProvider.value(value: walletCubit), + BlocProvider.value(value: splashCubit), + BlocProvider.value( + value: altmeChatSupportCubit, + ), + BlocProvider.value(value: flavorCubit), + BlocProvider.value(value: onboardingCubit), + BlocProvider.value( + value: ProfileCubit( + didKitProvider: didKitProvider, + jwtDecode: JWTDecode(), + oidc4vc: oidc4vc, + secureStorageProvider: secureStorageProvider, + langCubit: MockLangCubit(), + ), + ), + ], + child: const OnBoardingVerifyPhrasePage( + mnemonic: [], + isFromOnboarding: true, + ), + ), + ); + expect(find.byType(OnBoardingVerifyPhraseView), findsOneWidget); + }); + + testWidgets('renders UI correctly', (tester) async { + when(() => flavorCubit.state).thenAnswer((_) => FlavorMode.development); + final onBoardingVerifyPhraseCubit = OnBoardingVerifyPhraseCubit( + didKitProvider: didKitProvider, + keyGenerator: keyGenerator, + homeCubit: homeCubit, + walletCubit: walletCubit, + splashCubit: splashCubit, + altmeChatSupportCubit: altmeChatSupportCubit, + profileCubit: ProfileCubit( + didKitProvider: didKitProvider, + jwtDecode: JWTDecode(), + oidc4vc: oidc4vc, + secureStorageProvider: secureStorageProvider, + langCubit: MockLangCubit(), + ), + flavorCubit: flavorCubit, + ); + + await tester.pumpApp( + MultiBlocProvider( + providers: [ + BlocProvider.value( + value: onBoardingVerifyPhraseCubit, + ), + BlocProvider.value(value: onboardingCubit), + ], + child: OnBoardingVerifyPhraseView( + mnemonic: mnemonicString.split(' '), + isFromOnboarding: true, + onBoardingVerifyPhraseCubit: onBoardingVerifyPhraseCubit, + onboardingCubit: onboardingCubit, + ), + ), + ); + await tester.pumpAndSettle(); + expect(find.byType(BasePage), findsOneWidget); + expect(find.byType(MStepper), findsOneWidget); + expect(find.byType(PhraseWord), findsNWidgets(12)); + }); + + testWidgets( + 'selects the phrase words correctly and state is verified and' + ' emits Success when button is pressed and navigates to correct screen' + ' when isFromOnboarding is false', (tester) async { + when(() => flavorCubit.state).thenAnswer((_) => FlavorMode.development); + when(() => onboardingCubit.emitOnboardingProcessing()) + .thenAnswer((_) async {}); + final onBoardingVerifyPhraseCubit = OnBoardingVerifyPhraseCubit( + didKitProvider: didKitProvider, + keyGenerator: keyGenerator, + homeCubit: homeCubit, + walletCubit: walletCubit, + splashCubit: splashCubit, + altmeChatSupportCubit: altmeChatSupportCubit, + profileCubit: ProfileCubit( + didKitProvider: didKitProvider, + jwtDecode: JWTDecode(), + oidc4vc: oidc4vc, + secureStorageProvider: secureStorageProvider, + langCubit: MockLangCubit(), + ), + flavorCubit: flavorCubit, + ); + + await tester.pumpApp( + MockNavigatorProvider( + navigator: navigator, + child: MultiBlocProvider( + providers: [ + BlocProvider.value( + value: onBoardingVerifyPhraseCubit, + ), + BlocProvider.value(value: onboardingCubit), + ], + child: OnBoardingVerifyPhraseView( + mnemonic: mnemonicString.split(' '), + isFromOnboarding: false, + onBoardingVerifyPhraseCubit: onBoardingVerifyPhraseCubit, + onboardingCubit: onboardingCubit, + ), + ), + ), + ); + await tester.pumpAndSettle(); + + // make sure all the phrases are selected + for (int i = 0; i < 12; i++) { + expect( + onBoardingVerifyPhraseCubit.state.mnemonicStates[i].mnemonicStatus, + MnemonicStatus.unselected, + ); + await tester.tap(find.byKey(Key((i + 1).toString()))); + expect( + onBoardingVerifyPhraseCubit.state.mnemonicStates[i].mnemonicStatus, + MnemonicStatus.selected, + ); + } + + expect( + onBoardingVerifyPhraseCubit.state.isVerified, + true, + ); + + await tester.pumpAndSettle(); + + await tester.tap(find.text('Continue'.toUpperCase())); + verify(() => onboardingCubit.emitOnboardingProcessing()); + + verify( + () => navigator.pushReplacement( + any( + that: isRoute( + whereName: equals('/KeyVerifiedPage'), + ), + ), + ), + ).called(1); + }); + + testWidgets( + 'emits Success when button is pressed and navigates to correct screen' + ' when isFromOnboarding is false', (tester) async { + when(() => flavorCubit.state).thenAnswer((_) => FlavorMode.development); + when(() => onboardingCubit.emitOnboardingProcessing()) + .thenAnswer((_) async {}); + final onBoardingVerifyPhraseCubit = OnBoardingVerifyPhraseCubit( + didKitProvider: didKitProvider, + keyGenerator: keyGenerator, + homeCubit: homeCubit, + walletCubit: walletCubit, + splashCubit: splashCubit, + altmeChatSupportCubit: altmeChatSupportCubit, + profileCubit: ProfileCubit( + didKitProvider: didKitProvider, + jwtDecode: JWTDecode(), + oidc4vc: oidc4vc, + secureStorageProvider: secureStorageProvider, + langCubit: MockLangCubit(), + ), + flavorCubit: flavorCubit, + ); + + await tester.pumpApp( + MockNavigatorProvider( + navigator: navigator, + child: MultiBlocProvider( + providers: [ + BlocProvider.value( + value: onBoardingVerifyPhraseCubit, + ), + BlocProvider.value(value: onboardingCubit), + ], + child: OnBoardingVerifyPhraseView( + mnemonic: mnemonicString.split(' '), + isFromOnboarding: true, + onBoardingVerifyPhraseCubit: onBoardingVerifyPhraseCubit, + onboardingCubit: onboardingCubit, + ), + ), + ), + ); + await tester.pumpAndSettle(); + + for (int i = 0; i < 12; i++) { + final key = Key((i + 1).toString()); + await tester.ensureVisible(find.byKey(key)); + await tester.tap(find.byKey(key)); + } + + await tester.pumpAndSettle(); + + await tester.tap(find.text('Continue'.toUpperCase())); + + await tester.pumpAndSettle(); + + verify( + () => navigator.pushAndRemoveUntil( + any( + that: isRoute( + whereName: equals('/walletReadyPage'), + ), + ), + any(that: isA()), + ), + ).called(1); + }); + testWidgets('emits Error when error occurs', (tester) async { + when(() => flavorCubit.state).thenAnswer((_) => FlavorMode.development); + when(() => onboardingCubit.emitOnboardingProcessing()) + .thenAnswer((_) async {}); + when( + () => secureStorageProvider.set( + SecureStorageKeys.hasVerifiedMnemonics, + 'yes', + ), + ).thenThrow(Exception('Failed to set value')); + + final onBoardingVerifyPhraseCubit = OnBoardingVerifyPhraseCubit( + didKitProvider: didKitProvider, + keyGenerator: keyGenerator, + homeCubit: homeCubit, + walletCubit: walletCubit, + splashCubit: splashCubit, + altmeChatSupportCubit: altmeChatSupportCubit, + profileCubit: ProfileCubit( + didKitProvider: didKitProvider, + jwtDecode: JWTDecode(), + oidc4vc: oidc4vc, + secureStorageProvider: secureStorageProvider, + langCubit: MockLangCubit(), + ), + flavorCubit: flavorCubit, + ); + + await tester.pumpApp( + MultiBlocProvider( + providers: [ + BlocProvider.value( + value: onBoardingVerifyPhraseCubit, + ), + BlocProvider.value(value: onboardingCubit), + ], + child: OnBoardingVerifyPhraseView( + mnemonic: mnemonicString.split(' '), + isFromOnboarding: true, + onBoardingVerifyPhraseCubit: onBoardingVerifyPhraseCubit, + onboardingCubit: onboardingCubit, + ), + ), + ); + await tester.pumpAndSettle(); + + for (int i = 0; i < 12; i++) { + final key = Key((i + 1).toString()); + await tester.ensureVisible(find.byKey(key)); + await tester.tap(find.byKey(key)); + } + + await tester.pumpAndSettle(); + + await tester.tap(find.text('Continue'.toUpperCase())); + + await tester.pumpAndSettle(); + + expect(onBoardingVerifyPhraseCubit.state.message, isNotNull); + }); + + testWidgets('choose wrong mnemonics in production mode', (tester) async { + when(() => flavorCubit.state).thenAnswer((_) => FlavorMode.production); + + final onBoardingVerifyPhraseCubit = OnBoardingVerifyPhraseCubit( + didKitProvider: didKitProvider, + keyGenerator: keyGenerator, + homeCubit: homeCubit, + walletCubit: walletCubit, + splashCubit: splashCubit, + altmeChatSupportCubit: altmeChatSupportCubit, + profileCubit: ProfileCubit( + didKitProvider: didKitProvider, + jwtDecode: JWTDecode(), + oidc4vc: oidc4vc, + secureStorageProvider: secureStorageProvider, + langCubit: MockLangCubit(), + ), + flavorCubit: flavorCubit, + ); + + await tester.pumpApp( + MultiBlocProvider( + providers: [ + BlocProvider.value( + value: onBoardingVerifyPhraseCubit, + ), + BlocProvider.value(value: onboardingCubit), + ], + child: OnBoardingVerifyPhraseView( + mnemonic: mnemonicString.split(' '), + isFromOnboarding: false, + onBoardingVerifyPhraseCubit: onBoardingVerifyPhraseCubit, + onboardingCubit: onboardingCubit, + ), + ), + ); + await tester.pumpAndSettle(); + + // to make sure that index is not 0 + final index = onBoardingVerifyPhraseCubit.state.mnemonicStates + .indexWhere((element) => element.order != 1); + + final mnemonicState = + onBoardingVerifyPhraseCubit.state.mnemonicStates[index]; + + expect(mnemonicState.mnemonicStatus, MnemonicStatus.unselected); + + await tester.tap(find.byKey(Key(mnemonicState.order.toString()))); + + await tester.pumpAndSettle(); + expect( + onBoardingVerifyPhraseCubit.state.mnemonicStates[index].mnemonicStatus, + MnemonicStatus.wrongSelection, + ); + + await tester.tap(find.byKey(Key(mnemonicState.order.toString()))); + + await tester.pumpAndSettle(); + expect( + onBoardingVerifyPhraseCubit.state.mnemonicStates[index].mnemonicStatus, + MnemonicStatus.unselected, + ); + }); + }); +} diff --git a/test/onboarding/wallet_ready/cubit/wallet_ready_state_test.dart b/test/onboarding/wallet_ready/cubit/wallet_ready_state_test.dart new file mode 100644 index 000000000..50589a9f6 --- /dev/null +++ b/test/onboarding/wallet_ready/cubit/wallet_ready_state_test.dart @@ -0,0 +1,42 @@ +import 'package:altme/onboarding/onboarding.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('WalletReadyState', () { + test('can be serialized to JSON', () { + const state = WalletReadyState(isAgreeWithTerms: true); + expect( + state.toJson(), + { + 'isAgreeWithTerms': true, + }, + ); + }); + + test('can be deserialized from JSON', () { + final json = { + 'isAgreeWithTerms': true, + }; + expect( + WalletReadyState.fromJson(json), + const WalletReadyState(isAgreeWithTerms: true), + ); + }); + + test('copyWith returns a new instance with updated values', () { + const state = WalletReadyState(isAgreeWithTerms: false); + final newState = state.copyWith(isAgreeWithTerms: true); + + expect(newState.isAgreeWithTerms, true); + expect(newState, isNot(state)); + }); + + test('copyWith maintains the same values if no parameters are passed', () { + const state = WalletReadyState(isAgreeWithTerms: false); + final newState = state.copyWith(); + + expect(newState.isAgreeWithTerms, false); + expect(newState, isNot(same(state))); + }); + }); +} diff --git a/test/onboarding/wallet_ready/view/wallet_ready_page_test.dart b/test/onboarding/wallet_ready/view/wallet_ready_page_test.dart new file mode 100644 index 000000000..86e86c6fa --- /dev/null +++ b/test/onboarding/wallet_ready/view/wallet_ready_page_test.dart @@ -0,0 +1,285 @@ +import 'package:altme/app/app.dart'; +import 'package:altme/dashboard/dashboard.dart'; +import 'package:altme/flavor/flavor.dart'; +import 'package:altme/onboarding/cubit/onboarding_cubit.dart'; +import 'package:altme/onboarding/onboarding.dart'; +import 'package:bloc_test/bloc_test.dart'; +import 'package:confetti/confetti.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockingjay/mockingjay.dart'; + +import '../../../helpers/helpers.dart'; + +class MockProfileCubit extends MockCubit implements ProfileCubit { + @override + final state = ProfileState(model: ProfileModel.empty()); +} + +class MockConfettiController extends Mock implements ConfettiController { + @override + final duration = const Duration(milliseconds: 100); + + @override + final state = ConfettiControllerState.stopped; +} + +void main() { + late MockProfileCubit profileCubit; + late WalletReadyCubit walletReadyCubit; + late MockNavigator navigator; + late MockConfettiController confettiController; + + setUpAll(() { + profileCubit = MockProfileCubit(); + walletReadyCubit = WalletReadyCubit(); + navigator = MockNavigator(); + confettiController = MockConfettiController(); + }); + + group('Wallet Ready Page Test', () { + setUpAll(() { + when(navigator.canPop).thenReturn(true); + when(() => navigator.push(any())).thenAnswer((_) async {}); + when(() => navigator.pushAndRemoveUntil(any(), any())) + .thenAnswer((_) async {}); + }); + + testWidgets('is routable', (tester) async { + await tester.pumpApp( + MockNavigatorProvider( + navigator: navigator, + child: Builder( + builder: (context) => Scaffold( + floatingActionButton: FloatingActionButton( + onPressed: () { + Navigator.of(context).push( + WalletReadyPage.route(), + ); + }, + ), + ), + ), + ), + ); + await tester.tap(find.byType(FloatingActionButton)); + await tester.pumpAndSettle(); + + verify( + () => navigator.push( + any( + that: isRoute( + whereName: equals('/walletReadyPage'), + ), + ), + ), + ).called(1); + }); + + testWidgets('renders WalletReadyView', (tester) async { + await tester.pumpApp( + MultiBlocProvider( + providers: [ + BlocProvider.value( + value: walletReadyCubit, + ), + BlocProvider.value(value: profileCubit), + BlocProvider.value( + value: FlavorCubit(FlavorMode.development), + ), + BlocProvider.value( + value: OnboardingCubit(), + ), + ], + child: const WalletReadyPage(), + ), + ); + + expect(find.byType(WalletReadyView), findsOneWidget); + }); + + testWidgets('renders UI correctly', (tester) async { + await tester.pumpApp( + MultiBlocProvider( + providers: [ + BlocProvider.value( + value: walletReadyCubit, + ), + BlocProvider.value(value: profileCubit), + BlocProvider.value( + value: FlavorCubit(FlavorMode.development), + ), + BlocProvider.value( + value: OnboardingCubit(), + ), + ], + child: WalletReadyView( + profileCubit: profileCubit, + walletReadyCubit: walletReadyCubit, + confettiController: confettiController, + ), + ), + ); + + expect(find.byType(BasePage), findsOneWidget); + expect(find.byType(ConfettiWidget), findsOneWidget); + }); + + testWidgets('toggles agreement value when checkbox is pressed', + (tester) async { + await tester.pumpApp( + MultiBlocProvider( + providers: [ + BlocProvider.value( + value: walletReadyCubit, + ), + BlocProvider.value(value: profileCubit), + BlocProvider.value( + value: FlavorCubit(FlavorMode.development), + ), + BlocProvider.value( + value: OnboardingCubit(), + ), + ], + child: WalletReadyView( + profileCubit: profileCubit, + walletReadyCubit: walletReadyCubit, + confettiController: confettiController, + ), + ), + ); + + final isAgreeWithTerms = walletReadyCubit.state.isAgreeWithTerms; + + await tester.tap(find.byType(Checkbox)); + + expect(walletReadyCubit.state.isAgreeWithTerms, !isAgreeWithTerms); + }); + + testWidgets( + 'toggles agreement value when text "I agree to the " is pressed', + (tester) async { + await tester.pumpApp( + MultiBlocProvider( + providers: [ + BlocProvider.value( + value: walletReadyCubit, + ), + BlocProvider.value(value: profileCubit), + BlocProvider.value( + value: FlavorCubit(FlavorMode.development), + ), + BlocProvider.value( + value: OnboardingCubit(), + ), + ], + child: WalletReadyView( + profileCubit: profileCubit, + walletReadyCubit: walletReadyCubit, + confettiController: confettiController, + ), + ), + ); + + final isAgreeWithTerms = walletReadyCubit.state.isAgreeWithTerms; + + await tester.tap(find.text('I agree to the ')); + + expect(walletReadyCubit.state.isAgreeWithTerms, !isAgreeWithTerms); + }); + + testWidgets( + 'navigatest to onboardingTosPage when text "termsAndConditions " is' + ' pressed', (tester) async { + await tester.pumpApp( + MockNavigatorProvider( + navigator: navigator, + child: MultiBlocProvider( + providers: [ + BlocProvider.value( + value: walletReadyCubit, + ), + BlocProvider.value(value: profileCubit), + BlocProvider.value( + value: FlavorCubit(FlavorMode.development), + ), + BlocProvider.value( + value: OnboardingCubit(), + ), + ], + child: WalletReadyView( + profileCubit: profileCubit, + walletReadyCubit: walletReadyCubit, + confettiController: confettiController, + ), + ), + ), + ); + + await tester.tap(find.text('Terms and conditions'.toLowerCase())); + + verify( + () => navigator.pushAndRemoveUntil( + any( + that: isRoute( + whereName: equals('/onBoardingTermsPage'), + ), + ), + any( + that: isA(), + ), + ), + ).called(1); + }); + + testWidgets( + 'navigatest to dashboardPage when text "start " is' + ' pressed', (tester) async { + await tester.pumpApp( + MockNavigatorProvider( + navigator: navigator, + child: MultiBlocProvider( + providers: [ + BlocProvider.value( + value: walletReadyCubit, + ), + BlocProvider.value(value: profileCubit), + BlocProvider.value( + value: FlavorCubit(FlavorMode.development), + ), + BlocProvider.value( + value: OnboardingCubit(), + ), + ], + child: WalletReadyView( + profileCubit: profileCubit, + walletReadyCubit: walletReadyCubit, + confettiController: confettiController, + ), + ), + ), + ); + + if (!walletReadyCubit.state.isAgreeWithTerms) { + await tester.tap(find.text('I agree to the ')); + await tester.pumpAndSettle(); + } + + await tester.tap(find.text('Start'.toUpperCase())); + + verify( + () => navigator.pushAndRemoveUntil( + any( + that: isRoute( + whereName: equals(AltMeStrings.dashBoardPage), + ), + ), + any( + that: isA(), + ), + ), + ).called(1); + }); + }); +} diff --git a/test/pin_code/view/pin_code_page_test.dart b/test/pin_code/view/pin_code_page_test.dart new file mode 100644 index 000000000..92192333d --- /dev/null +++ b/test/pin_code/view/pin_code_page_test.dart @@ -0,0 +1,126 @@ +// import 'package:altme/app/app.dart'; +// import 'package:altme/dashboard/dashboard.dart'; +// import 'package:altme/flavor/flavor.dart'; +// import 'package:altme/pin_code/pin_code.dart'; +// import 'package:bloc_test/bloc_test.dart'; +// import 'package:flutter/material.dart'; +// import 'package:flutter_bloc/flutter_bloc.dart'; +// import 'package:flutter_test/flutter_test.dart'; +// import 'package:mockingjay/mockingjay.dart'; +// import 'package:secure_storage/secure_storage.dart'; + +// import '../../helpers/helpers.dart'; + +// class MockProfleCubit extends MockCubit implements ProfileCubit { +// @override +// final state = ProfileState(model: ProfileModel.empty()); +// } + +// class MockSecureStorageProvider extends Mock implements SecureStorageProvider {} + +// class MockLocalAuthApi extends Mock implements LocalAuthApi {} + +// class MockFlavorCubit extends MockCubit implements FlavorCubit { +// @override +// final state = FlavorMode.development; +// } + +// void main() { +// group('Pincode Page', () { +// final PinCodeViewCubit pinCodeViewCubit = PinCodeViewCubit(); +// final MockNavigator navigator = MockNavigator(); +// final MockSecureStorageProvider secureStorageProvider = +// MockSecureStorageProvider(); +// final MockProfleCubit profleCubit = MockProfleCubit(); +// final MockLocalAuthApi localAuthApi = MockLocalAuthApi(); +// final MockFlavorCubit flavorCubit = MockFlavorCubit(); + +// setUpAll(() { +// when(() => secureStorageProvider.get(any())).thenAnswer((_) async => ''); + +// when(() => secureStorageProvider.set(any(), any())) +// .thenAnswer((_) async => Future.value()); + +// when(navigator.canPop).thenReturn(true); +// when(() => navigator.push(any())).thenAnswer((_) async {}); +// when(() => navigator.pushAndRemoveUntil(any(), any())) +// .thenAnswer((_) async {}); +// when(() => navigator.pushReplacement(any())) +// .thenAnswer((_) async {}); +// }); + +// testWidgets('is routable', (tester) async { +// await tester.pumpApp( +// MockNavigatorProvider( +// navigator: navigator, +// child: Builder( +// builder: (context) => Scaffold( +// floatingActionButton: FloatingActionButton( +// onPressed: () { +// Navigator.of(context).push( +// PinCodePage.route( +// isValidCallback: () {}, +// walletProtectionType: WalletProtectionType.FA2, +// ), +// ); +// }, +// ), +// ), +// ), +// ), +// ); +// await tester.tap(find.byType(FloatingActionButton)); +// await tester.pumpAndSettle(); + +// verify( +// () => navigator.push( +// any( +// that: isRoute( +// whereName: equals('/pinCodePage'), +// ), +// ), +// ), +// ).called(1); +// }); + +// testWidgets('renders PinCodePage', (tester) async { +// await tester.pumpApp( +// MultiBlocProvider( +// providers: [ +// BlocProvider.value(value: profleCubit), +// BlocProvider.value(value: pinCodeViewCubit), +// BlocProvider.value(value: flavorCubit), +// ], +// child: PinCodePage( +// isValidCallback: () {}, +// walletProtectionType: WalletProtectionType.FA2, +// secureStorageProvider: secureStorageProvider, +// localAuthApi: localAuthApi, +// ), +// ), +// ); +// expect(find.byType(PinCodeView), findsOneWidget); +// }); + +// testWidgets('renders UI correctly', (tester) async { +// await tester.pumpApp( +// MultiBlocProvider( +// providers: [ +// BlocProvider.value(value: profleCubit), +// BlocProvider.value(value: pinCodeViewCubit), +// BlocProvider.value(value: flavorCubit), +// ], +// child: PinCodeView( +// isValidCallback: () {}, +// walletProtectionType: WalletProtectionType.FA2, +// secureStorageProvider: secureStorageProvider, +// localAuthApi: localAuthApi, +// profileCubit: profleCubit, +// ), +// ), +// ); +// expect(find.byType(BasePage), findsOneWidget); +// expect(find.byType(PinCodeWidget), findsOneWidget); +// }); +// }); +// } diff --git a/test/route/cubit/route_cubit_test.dart b/test/route/cubit/route_cubit_test.dart new file mode 100644 index 000000000..da958227f --- /dev/null +++ b/test/route/cubit/route_cubit_test.dart @@ -0,0 +1,35 @@ +import 'package:altme/route/route.dart'; +import 'package:bloc_test/bloc_test.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('RouteCubit', () { + late RouteCubit routeCubit; + + setUp(() { + routeCubit = RouteCubit(); + }); + + tearDown(() { + routeCubit.close(); + }); + + test('initial state is an empty string', () { + expect(routeCubit.state, equals('')); + }); + + blocTest( + 'emits new screen name when setCurrentScreen is called', + build: () => routeCubit, + act: (cubit) => cubit.setCurrentScreen('HomeScreen'), + expect: () => ['HomeScreen'], + ); + + blocTest( + 'emits null when setCurrentScreen is called with null', + build: () => routeCubit, + act: (cubit) => cubit.setCurrentScreen(null), + expect: () => [null], + ); + }); +} diff --git a/test/route/helper_function_test.dart b/test/route/helper_function_test.dart new file mode 100644 index 000000000..36168544d --- /dev/null +++ b/test/route/helper_function_test.dart @@ -0,0 +1,86 @@ +import 'package:altme/onboarding/onboarding.dart'; +import 'package:altme/route/route.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockingjay/mockingjay.dart'; + +import '../helpers/helpers.dart'; + +void main() { + final navigator = MockNavigator(); + + setUpAll(() { + when(navigator.canPop).thenReturn(true); + when(() => navigator.pushReplacement(any())) + .thenAnswer((_) async {}); + when(() => navigator.push(any())).thenAnswer((_) async {}); + }); + + testWidgets('sensibleRoute pushes replacement route if isSameRoute is true', + (WidgetTester tester) async { + await tester.pumpApp( + MockNavigatorProvider( + navigator: navigator, + child: Builder( + builder: (context) => Scaffold( + floatingActionButton: FloatingActionButton( + onPressed: () { + sensibleRoute( + context: context, + route: StarterPage.route(), + isSameRoute: true, + ); + }, + ), + ), + ), + ), + ); + await tester.tap(find.byType(FloatingActionButton)); + await tester.pumpAndSettle(); + + verify( + () => navigator.pushReplacement( + any( + that: isRoute( + whereName: equals('/starterPage'), + ), + ), + ), + ).called(1); + }); + + testWidgets('sensibleRoute pushes replacement route if isSameRoute is false', + (WidgetTester tester) async { + await tester.pumpApp( + MockNavigatorProvider( + navigator: navigator, + child: Builder( + builder: (context) => Scaffold( + floatingActionButton: FloatingActionButton( + onPressed: () { + sensibleRoute( + context: context, + route: StarterPage.route(), + isSameRoute: false, + ); + }, + ), + ), + ), + ), + ); + await tester.tap(find.byType(FloatingActionButton)); + await tester.pumpAndSettle(); + + verify( + () => navigator.push( + any( + that: isRoute( + whereName: equals('/starterPage'), + ), + ), + ), + ).called(1); + }); +} diff --git a/test/route/router/router_test.dart b/test/route/router/router_test.dart new file mode 100644 index 000000000..98b1f95b0 --- /dev/null +++ b/test/route/router/router_test.dart @@ -0,0 +1,34 @@ +import 'dart:async'; + +import 'package:altme/route/route.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + testWidgets('RightToLeftRoute transition test', (WidgetTester tester) async { + const fromWidget = Scaffold(body: Center(child: Text('From Screen'))); + const toWidget = Scaffold(body: Center(child: Text('To Screen'))); + + final navigatorKey = GlobalKey(); + + await tester.pumpWidget( + MaterialApp( + navigatorKey: navigatorKey, + home: fromWidget, + ), + ); + + expect(find.text('From Screen'), findsOneWidget); + expect(find.text('To Screen'), findsNothing); + + unawaited( + navigatorKey.currentState! + .push(RightToLeftRoute(builder: (context) => toWidget)), + ); + + await tester.pumpAndSettle(); + + expect(find.text('From Screen'), findsNothing); + expect(find.text('To Screen'), findsOneWidget); + }); +} diff --git a/test/scan/cubit/scan_cubit.dart b/test/scan/cubit/scan_cubit.dart new file mode 100644 index 000000000..29daade2e --- /dev/null +++ b/test/scan/cubit/scan_cubit.dart @@ -0,0 +1,69 @@ +import 'package:altme/app/shared/shared.dart'; +import 'package:altme/credentials/credentials.dart'; +import 'package:altme/dashboard/profile/profile.dart'; +import 'package:altme/scan/cubit/scan_cubit.dart'; +import 'package:altme/wallet/cubit/wallet_cubit.dart'; +import 'package:did_kit/did_kit.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:jwt_decode/jwt_decode.dart'; +import 'package:mockito/mockito.dart'; +import 'package:oidc4vc/oidc4vc.dart'; +import 'package:secure_storage/secure_storage.dart'; + +class MockDioClient extends Mock implements DioClient {} + +class MockCredentialsCubit extends Mock implements CredentialsCubit {} + +class MockDIDKitProvider extends Mock implements DIDKitProvider {} + +class MockSecureStorageProvider extends Mock implements SecureStorageProvider {} + +class MockProfileCubit extends Mock implements ProfileCubit {} + +class MockWalletCubit extends Mock implements WalletCubit {} + +class MockOIDC4VC extends Mock implements OIDC4VC {} + +class MockJWTDecode extends Mock implements JWTDecode {} + +void main() { + group('ScanCubit', () { + late MockDioClient mockDioClient; + late MockCredentialsCubit mockCredentialsCubit; + late MockDIDKitProvider mockDIDKitProvider; + late MockSecureStorageProvider mockSecureStorageProvider; + late MockProfileCubit mockProfileCubit; + late MockWalletCubit mockWalletCubit; + late MockOIDC4VC mockOIDC4VC; + late MockJWTDecode mockJWTDecode; + late ScanCubit scanCubit; + + setUp(() { + mockDioClient = MockDioClient(); + mockCredentialsCubit = MockCredentialsCubit(); + mockDIDKitProvider = MockDIDKitProvider(); + mockSecureStorageProvider = MockSecureStorageProvider(); + mockProfileCubit = MockProfileCubit(); + mockWalletCubit = MockWalletCubit(); + mockOIDC4VC = MockOIDC4VC(); + mockJWTDecode = MockJWTDecode(); + + scanCubit = ScanCubit( + client: mockDioClient, + credentialsCubit: mockCredentialsCubit, + didKitProvider: mockDIDKitProvider, + secureStorageProvider: mockSecureStorageProvider, + profileCubit: mockProfileCubit, + walletCubit: mockWalletCubit, + oidc4vc: mockOIDC4VC, + jwtDecode: mockJWTDecode, + ); + }); + + test('initial state is ScanState', () { + expect(scanCubit.state, equals(const ScanState())); + }); + + // Add more tests here... + }); +} diff --git a/test/splash/cubit/splash_cubit_test.dart b/test/splash/cubit/splash_cubit_test.dart index 66d7d4ae1..62d261ab2 100644 --- a/test/splash/cubit/splash_cubit_test.dart +++ b/test/splash/cubit/splash_cubit_test.dart @@ -3,27 +3,68 @@ import 'package:altme/chat_room/chat_room.dart'; import 'package:altme/credentials/credentials.dart'; import 'package:altme/dashboard/dashboard.dart'; import 'package:altme/splash/cubit/splash_cubit.dart'; -import 'package:altme/wallet/cubit/wallet_cubit.dart'; +import 'package:altme/wallet/wallet.dart'; import 'package:bloc_test/bloc_test.dart'; import 'package:dio/dio.dart'; +import 'package:fake_async/fake_async.dart'; +import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; +import 'package:package_info_plus/package_info_plus.dart'; import 'package:secure_storage/secure_storage.dart'; class MockSecureStorage extends Mock implements SecureStorageProvider {} -class MockHomeCubit extends MockCubit implements HomeCubit {} +class MockHomeCubit extends MockCubit implements HomeCubit { + @override + Future emitHasWallet() async {} +} class MockAltmeChatSupportCubit extends MockCubit implements AltmeChatSupportCubit {} class MockCredentialsCubit extends MockCubit - implements CredentialsCubit {} + implements CredentialsCubit { + @override + Future loadAllCredentials({ + required BlockchainType blockchainType, + }) async {} +} -class MockWalletCubit extends MockCubit implements WalletCubit {} +class MockWalletCubit extends MockCubit implements WalletCubit { + @override + final state = WalletState( + cryptoAccount: CryptoAccount( + data: [ + CryptoAccountData( + name: '', + secretKey: '', + blockchainType: BlockchainType.tezos, + walletAddress: '', + ), + ], + ), + ); + + @override + Future createCryptoWallet({ + String? accountName, + required String mnemonicOrKey, + required bool isImported, + required bool isFromOnboarding, + BlockchainType? blockchainType, + bool showStatus = true, + void Function({ + required CryptoAccount cryptoAccount, + required MessageHandler messageHandler, + })? onComplete, + }) async {} +} -class MockProfileCubit extends MockCubit - implements ProfileCubit {} +class MockProfileCubit extends MockCubit implements ProfileCubit { + @override + final state = ProfileState(model: ProfileModel.empty()); +} void main() { late SecureStorageProvider mockSecureStorage; @@ -33,13 +74,30 @@ void main() { late AltmeChatSupportCubit altmeChatSupportCubit; late ProfileCubit profileCubit; + final packageInfo = PackageInfo( + appName: 'testApp', + packageName: 'com.example.test', + version: '1.0.0', + buildNumber: '1', + ); + setUp(() { + WidgetsFlutterBinding.ensureInitialized(); mockSecureStorage = MockSecureStorage(); homeCubit = MockHomeCubit(); credentialsCubit = MockCredentialsCubit(); walletCubit = MockWalletCubit(); altmeChatSupportCubit = MockAltmeChatSupportCubit(); profileCubit = MockProfileCubit(); + + when(() => mockSecureStorage.get(SecureStorageKeys.version)) + .thenAnswer((_) async => '1.0.0'); + when(() => mockSecureStorage.get(SecureStorageKeys.buildNumber)) + .thenAnswer((_) async => '1'); + when(() => mockSecureStorage.set(SecureStorageKeys.version, '1.0.0')) + .thenAnswer((_) async => {}); + when(() => mockSecureStorage.set(SecureStorageKeys.buildNumber, '1')) + .thenAnswer((_) async => {}); }); group('Splash Cubit', () { @@ -57,120 +115,67 @@ void main() { ), altmeChatSupportCubit: altmeChatSupportCubit, profileCubit: profileCubit, + packageInfo: packageInfo, ).state, - SplashStatus.init, + const SplashState( + status: SplashStatus.init, + versionNumber: '', + buildNumber: '', + isNewVersion: false, + loadedValue: 0, + ), ); }); group('initialiseApp', () { - group('SecureStorageKeys.ssiKey', () { - test( - '''emits SplashStatus.routeToPassCode when SecureStorageKeys.ssiKey is null''', - () async { - when(() => mockSecureStorage.get(SecureStorageKeys.ssiKey)) - .thenAnswer((_) => Future.value(null)); - - final SplashCubit splashCubit = SplashCubit( - credentialsCubit: credentialsCubit, - secureStorageProvider: mockSecureStorage, - homeCubit: homeCubit, - walletCubit: walletCubit, - client: DioClient( - baseUrl: Urls.checkIssuerTalaoUrl, - secureStorageProvider: mockSecureStorage, - dio: Dio(), - ), - altmeChatSupportCubit: altmeChatSupportCubit, - profileCubit: profileCubit, - ); - await splashCubit.initialiseApp(); - - expect(splashCubit.state, SplashStatus.routeToPassCode); - }); - - test( - '''emits SplashStatus.routeToPassCode when SecureStorageKeys.ssiKey is empty''', - () async { - when(() => mockSecureStorage.get(SecureStorageKeys.ssiKey)) - .thenAnswer((_) => Future.value('')); - - final SplashCubit splashCubit = SplashCubit( - credentialsCubit: credentialsCubit, + test('counter increases every 500 milliseconds and stops at 5', () { + final SplashCubit splashCubit = SplashCubit( + credentialsCubit: credentialsCubit, + secureStorageProvider: mockSecureStorage, + homeCubit: homeCubit, + walletCubit: walletCubit, + client: DioClient( + baseUrl: Urls.checkIssuerTalaoUrl, secureStorageProvider: mockSecureStorage, - homeCubit: homeCubit, - walletCubit: walletCubit, - client: DioClient( - baseUrl: Urls.checkIssuerTalaoUrl, - secureStorageProvider: mockSecureStorage, - dio: Dio(), - ), - altmeChatSupportCubit: altmeChatSupportCubit, - profileCubit: profileCubit, - ); - await splashCubit.initialiseApp(); - - expect(splashCubit.state, SplashStatus.routeToPassCode); - }); - }); + dio: Dio(), + ), + altmeChatSupportCubit: altmeChatSupportCubit, + profileCubit: profileCubit, + packageInfo: packageInfo, + ); + fakeAsync((async) { + splashCubit.initialiseApp(); + expect(splashCubit.state.loadedValue, equals(0.0)); - group('SecureStorageKeys.did', () { - setUp(() { - when(() => mockSecureStorage.get(SecureStorageKeys.ssiKey)) - .thenAnswer((_) => Future.value('key')); - }); + async.elapse(const Duration(milliseconds: 500)); + expect(splashCubit.state.loadedValue, equals(0.1)); - test( - '''emits SplashStatus.routeToPassCode when SecureStorageKeys.did is null''', - () async { - final SplashCubit splashCubit = SplashCubit( - credentialsCubit: credentialsCubit, - secureStorageProvider: mockSecureStorage, - homeCubit: homeCubit, - walletCubit: walletCubit, - client: DioClient( - baseUrl: Urls.checkIssuerTalaoUrl, - secureStorageProvider: mockSecureStorage, - dio: Dio(), - ), - altmeChatSupportCubit: altmeChatSupportCubit, - profileCubit: profileCubit, - ); - await splashCubit.initialiseApp(); + async.elapse(const Duration(milliseconds: 500)); + expect(splashCubit.state.loadedValue, equals(0.2)); - expect(splashCubit.state, SplashStatus.routeToPassCode); - }); + // Continue elapsing time until counter reaches 5 + while (splashCubit.state.loadedValue < 1.0) { + async.elapse(const Duration(milliseconds: 500)); + } - test( - '''emits SplashStatus.routeToPassCode when SecureStorageKeys.did is empty''', - () async { - final SplashCubit splashCubit = SplashCubit( - credentialsCubit: credentialsCubit, - secureStorageProvider: mockSecureStorage, - homeCubit: homeCubit, - walletCubit: walletCubit, - client: DioClient( - baseUrl: Urls.checkIssuerTalaoUrl, - secureStorageProvider: mockSecureStorage, - dio: Dio(), - ), - altmeChatSupportCubit: altmeChatSupportCubit, - profileCubit: profileCubit, - ); - await splashCubit.initialiseApp(); + // Check final state + expect(splashCubit.state.loadedValue, equals(1.0)); + expect(splashCubit.state.status, equals(SplashStatus.init)); - expect(splashCubit.state, SplashStatus.routeToPassCode); + async.flushMicrotasks(); }); }); - group('SecureStorageKeys.didMethod', () { - setUp(() { + group('Route to routeToPassCode', () { + test('when ssiKey and ssiMnemonics is not null', () async { + when( + () => mockSecureStorage.get(any()), + ).thenAnswer((_) => Future.value(null)); + when(() => mockSecureStorage.get(SecureStorageKeys.ssiMnemonic)) + .thenAnswer((_) => Future.value('xyz')); when(() => mockSecureStorage.get(SecureStorageKeys.ssiKey)) - .thenAnswer((_) => Future.value('key')); - }); + .thenAnswer((_) => Future.value('xyz')); - test( - '''emits SplashStatus.routeToPassCode when SecureStorageKeys.didMethod is null''', - () async { final SplashCubit splashCubit = SplashCubit( credentialsCubit: credentialsCubit, secureStorageProvider: mockSecureStorage, @@ -183,64 +188,23 @@ void main() { ), altmeChatSupportCubit: altmeChatSupportCubit, profileCubit: profileCubit, + packageInfo: packageInfo, ); - await splashCubit.initialiseApp(); - - expect(splashCubit.state, SplashStatus.routeToPassCode); - }); - - test( - '''emits SplashStatus.routeToPassCode when SecureStorageKeys.didMethod is empty''', - () async { - final SplashCubit splashCubit = SplashCubit( - credentialsCubit: credentialsCubit, - secureStorageProvider: mockSecureStorage, - homeCubit: homeCubit, - walletCubit: walletCubit, - client: DioClient( - baseUrl: Urls.checkIssuerTalaoUrl, - secureStorageProvider: mockSecureStorage, - dio: Dio(), - ), - altmeChatSupportCubit: altmeChatSupportCubit, - profileCubit: profileCubit, - ); - await splashCubit.initialiseApp(); - - expect(splashCubit.state, SplashStatus.routeToPassCode); + fakeAsync((async) { + splashCubit.initialiseApp(); + // complete timer + while (splashCubit.state.loadedValue <= 1.0) { + async.elapse(const Duration(milliseconds: 500)); + } + }); + expect(splashCubit.state.status, SplashStatus.routeToPassCode); }); }); - group('SecureStorageKeys.didMethodName', () { - setUp(() { - when(() => mockSecureStorage.get(SecureStorageKeys.ssiKey)) - .thenAnswer((_) => Future.value('key')); - }); - - test( - '''emits SplashStatus.routeToPassCode when SecureStorageKeys.didMethodName is null''', - () async { - final SplashCubit splashCubit = SplashCubit( - credentialsCubit: credentialsCubit, - secureStorageProvider: mockSecureStorage, - homeCubit: homeCubit, - walletCubit: walletCubit, - client: DioClient( - baseUrl: Urls.checkIssuerTalaoUrl, - secureStorageProvider: mockSecureStorage, - dio: Dio(), - ), - altmeChatSupportCubit: altmeChatSupportCubit, - profileCubit: profileCubit, - ); - await splashCubit.initialiseApp(); - - expect(splashCubit.state, SplashStatus.routeToPassCode); - }); - - test( - '''emits SplashStatus.routeToPassCode when SecureStorageKeys.didMethodName is empty''', - () async { + group('Route to onboarding', () { + test('when ssiMnemonic is null', () async { + when(() => mockSecureStorage.get(SecureStorageKeys.ssiMnemonic)) + .thenAnswer((_) => Future.value(null)); final SplashCubit splashCubit = SplashCubit( credentialsCubit: credentialsCubit, secureStorageProvider: mockSecureStorage, @@ -253,49 +217,23 @@ void main() { ), altmeChatSupportCubit: altmeChatSupportCubit, profileCubit: profileCubit, + packageInfo: packageInfo, ); - await splashCubit.initialiseApp(); - - expect(splashCubit.state, SplashStatus.routeToPassCode); + fakeAsync((async) { + splashCubit.initialiseApp(); + // complete timer + while (splashCubit.state.loadedValue <= 1.0) { + async.elapse(const Duration(milliseconds: 500)); + } + async.flushMicrotasks(); + }); + expect(splashCubit.state.status, SplashStatus.routeToOnboarding); }); - }); - - group('SecureStorageKeys.isEnterpriseUser', () { - setUp(() { + test('when ssiKey is null', () async { + when(() => mockSecureStorage.get(SecureStorageKeys.ssiMnemonic)) + .thenAnswer((_) => Future.value('xyz')); when(() => mockSecureStorage.get(SecureStorageKeys.ssiKey)) - .thenAnswer((_) => Future.value('key')); - }); - - test( - '''emits SplashStatus.routeToPassCode when SecureStorageKeys.isEnterpriseUser is null''', - () async { - when(() => mockSecureStorage.get(SecureStorageKeys.walletType)) .thenAnswer((_) => Future.value(null)); - - final SplashCubit splashCubit = SplashCubit( - credentialsCubit: credentialsCubit, - secureStorageProvider: mockSecureStorage, - homeCubit: homeCubit, - walletCubit: walletCubit, - client: DioClient( - baseUrl: Urls.checkIssuerTalaoUrl, - secureStorageProvider: mockSecureStorage, - dio: Dio(), - ), - altmeChatSupportCubit: altmeChatSupportCubit, - profileCubit: profileCubit, - ); - await splashCubit.initialiseApp(); - - expect(splashCubit.state, SplashStatus.routeToPassCode); - }); - - test( - '''emits SplashStatus.routeToPassCode when SecureStorageKeys.isEnterpriseUser is empty''', - () async { - when(() => mockSecureStorage.get(SecureStorageKeys.walletType)) - .thenAnswer((_) => Future.value('')); - final SplashCubit splashCubit = SplashCubit( credentialsCubit: credentialsCubit, secureStorageProvider: mockSecureStorage, @@ -308,90 +246,17 @@ void main() { ), altmeChatSupportCubit: altmeChatSupportCubit, profileCubit: profileCubit, + packageInfo: packageInfo, ); - await splashCubit.initialiseApp(); - - expect(splashCubit.state, SplashStatus.routeToPassCode); - }); - - group('when user is enterprise user', () { - setUp(() { - when( - () => mockSecureStorage.get(SecureStorageKeys.walletType), - ).thenAnswer((_) => Future.value('true')); - }); - - test( - '''emits SplashStatus.routeToPassCode when SecureStorageKeys.rsaKeyJson is null''', - () async { - when(() => mockSecureStorage.get(SecureStorageKeys.rsaKeyJson)) - .thenAnswer((_) => Future.value(null)); - - final SplashCubit splashCubit = SplashCubit( - credentialsCubit: credentialsCubit, - secureStorageProvider: mockSecureStorage, - homeCubit: homeCubit, - walletCubit: walletCubit, - client: DioClient( - baseUrl: Urls.checkIssuerTalaoUrl, - secureStorageProvider: mockSecureStorage, - dio: Dio(), - ), - altmeChatSupportCubit: altmeChatSupportCubit, - profileCubit: profileCubit, - ); - await splashCubit.initialiseApp(); - - expect(splashCubit.state, SplashStatus.routeToPassCode); - }); - - test( - '''emits SplashStatus.routeToPassCode when SecureStorageKeys.rsaKeyJson is empty''', - () async { - when(() => mockSecureStorage.get(SecureStorageKeys.rsaKeyJson)) - .thenAnswer((_) => Future.value('')); - - final SplashCubit splashCubit = SplashCubit( - credentialsCubit: credentialsCubit, - secureStorageProvider: mockSecureStorage, - homeCubit: homeCubit, - walletCubit: walletCubit, - client: DioClient( - baseUrl: Urls.checkIssuerTalaoUrl, - secureStorageProvider: mockSecureStorage, - dio: Dio(), - ), - altmeChatSupportCubit: altmeChatSupportCubit, - profileCubit: profileCubit, - ); - await splashCubit.initialiseApp(); - - expect(splashCubit.state, SplashStatus.routeToPassCode); - }); - - test( - '''emits SplashStatus.bypassOnBoarding when we have required values''', - () async { - when(() => mockSecureStorage.get(SecureStorageKeys.rsaKeyJson)) - .thenAnswer((_) => Future.value('{"key" : "value"}')); - - final SplashCubit splashCubit = SplashCubit( - credentialsCubit: credentialsCubit, - secureStorageProvider: mockSecureStorage, - homeCubit: homeCubit, - walletCubit: walletCubit, - client: DioClient( - baseUrl: Urls.checkIssuerTalaoUrl, - secureStorageProvider: mockSecureStorage, - dio: Dio(), - ), - altmeChatSupportCubit: altmeChatSupportCubit, - profileCubit: profileCubit, - ); - await splashCubit.initialiseApp(); - - expect(splashCubit.state, SplashStatus.routeToPassCode); + fakeAsync((async) { + splashCubit.initialiseApp(); + // complete timer + while (splashCubit.state.loadedValue <= 1.0) { + async.elapse(const Duration(milliseconds: 500)); + } + async.flushMicrotasks(); }); + expect(splashCubit.state.status, SplashStatus.routeToOnboarding); }); }); }); diff --git a/test/splash/view/splash_test.dart b/test/splash/view/splash_test.dart index e1e949674..3e6950baf 100644 --- a/test/splash/view/splash_test.dart +++ b/test/splash/view/splash_test.dart @@ -1,9 +1,19 @@ import 'package:altme/app/app.dart'; +import 'package:altme/connection_bridge/connection_bridge.dart'; +import 'package:altme/credentials/credentials.dart'; +import 'package:altme/dashboard/dashboard.dart'; +import 'package:altme/enterprise/cubit/enterprise_cubit.dart'; import 'package:altme/flavor/cubit/flavor_cubit.dart'; +import 'package:altme/l10n/l10n.dart'; +import 'package:altme/onboarding/starter/starter.dart'; +import 'package:altme/polygon_id/cubit/polygon_id_cubit.dart'; +import 'package:altme/scan/scan.dart'; import 'package:altme/splash/splash.dart'; +import 'package:altme/wallet/wallet.dart'; import 'package:bloc_test/bloc_test.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockingjay/mockingjay.dart'; import 'package:secure_storage/secure_storage.dart'; @@ -14,33 +24,168 @@ class MockSecureStorageProvider extends Mock implements SecureStorageProvider {} class MockSplashCubit extends MockCubit implements SplashCubit {} +class MockWalletCubit extends MockCubit implements WalletCubit { + @override + final state = WalletState( + cryptoAccount: CryptoAccount( + data: [ + CryptoAccountData( + name: '', + secretKey: '', + blockchainType: BlockchainType.tezos, + walletAddress: '', + ), + ], + ), + ); +} + class MockFlavorCubit extends MockCubit implements FlavorCubit {} +class MockProfileCubit extends MockCubit implements ProfileCubit { + @override + final state = ProfileState( + model: ProfileModel.empty(), + status: AppStatus.success, + ); +} + +class MockCredentialsCubit extends MockCubit + implements CredentialsCubit { + @override + final state = const CredentialsState(); + + @override + Future loadAllCredentials({ + required BlockchainType blockchainType, + }) async {} +} + +class MockQRCodeScanCubit extends MockCubit + implements QRCodeScanCubit { + @override + final state = const QRCodeScanState(); +} + +class MockScanCubit extends MockCubit implements ScanCubit { + @override + final state = const ScanState(); +} + +class MockBeaconCubit extends MockCubit implements BeaconCubit { + @override + final state = const BeaconState(); +} + +class MockWalletConnectCubit extends MockCubit + implements WalletConnectCubit { + @override + final state = const WalletConnectState(); +} + +class MockPolygonIdCubit extends MockCubit + implements PolygonIdCubit { + @override + final state = const PolygonIdState(); +} + +class MockEnterpriseCubit extends MockCubit + implements EnterpriseCubit { + @override + final state = const EnterpriseState(); +} + +class MockAdvanceSettingsCubit extends MockCubit + implements AdvanceSettingsCubit { + @override + final state = const AdvanceSettingsState( + isGamingEnabled: true, + isIdentityEnabled: true, + isProfessionalEnabled: true, + isBlockchainAccountsEnabled: true, + isEducationEnabled: true, + isPassEnabled: true, + isSocialMediaEnabled: true, + isCommunityEnabled: true, + isOtherEnabled: true, + isFinanceEnabled: true, + isHumanityProofEnabled: true, + isWalletIntegrityEnabled: true, + ); + + @override + Future initialise() async {} + + @override + Future setState(AdvanceSettingsState newState) async {} +} + void main() { late FlavorCubit flavorCubit; late SplashCubit splashCubit; + late WalletCubit walletCubit; + late ProfileCubit profileCubit; + late AdvanceSettingsCubit advanceSettingsCubit; + late CredentialsCubit credentialsCubit; + late ScanCubit scanCubit; + late QRCodeScanCubit qRCodeScanCubit; + late BeaconCubit beaconCubit; + late WalletConnectCubit walletConnectCubit; + late PolygonIdCubit polygonIdCubit; + late EnterpriseCubit enterpriseCubit; setUpAll(() async { flavorCubit = MockFlavorCubit(); splashCubit = MockSplashCubit(); + walletCubit = MockWalletCubit(); + profileCubit = MockProfileCubit(); + advanceSettingsCubit = MockAdvanceSettingsCubit(); + credentialsCubit = MockCredentialsCubit(); + scanCubit = MockScanCubit(); + qRCodeScanCubit = MockQRCodeScanCubit(); + beaconCubit = MockBeaconCubit(); + walletConnectCubit = MockWalletConnectCubit(); + polygonIdCubit = MockPolygonIdCubit(); + enterpriseCubit = MockEnterpriseCubit(); + when(() => splashCubit.state) + .thenReturn(const SplashState(status: SplashStatus.init)); + when(() => splashCubit.initialiseApp()).thenAnswer((_) async {}); + when(() => flavorCubit.state).thenReturn(FlavorMode.development); }); Widget makeTestableWidget() { return MultiBlocProvider( providers: [ - BlocProvider.value(value: flavorCubit), - BlocProvider.value(value: splashCubit), + BlocProvider(create: (context) => flavorCubit), + BlocProvider(create: (context) => beaconCubit), + BlocProvider( + create: (context) => walletConnectCubit, + ), + BlocProvider(create: (context) => profileCubit), + BlocProvider( + create: (context) => advanceSettingsCubit, + ), + BlocProvider(create: (context) => walletCubit), + BlocProvider(create: (context) => credentialsCubit), + BlocProvider(create: (context) => polygonIdCubit), + BlocProvider(create: (context) => enterpriseCubit), + BlocProvider(create: (context) => scanCubit), + BlocProvider(create: (context) => qRCodeScanCubit), + BlocProvider(create: (context) => splashCubit), ], - child: const SplashView(), + child: const MaterialApp( + localizationsDelegates: [ + AppLocalizations.delegate, + GlobalMaterialLocalizations.delegate, + ], + supportedLocales: AppLocalizations.supportedLocales, + home: SplashView(), + ), ); } group('SplashPage', () { testWidgets('renders SplashView', (tester) async { - when(() => flavorCubit.state).thenReturn(FlavorMode.development); - when(() => splashCubit.state) - .thenReturn(const SplashState(status: SplashStatus.init)); - when(() => splashCubit.initialiseApp()).thenAnswer((_) async {}); await tester.pumpApp(makeTestableWidget()); await tester.pumpAndSettle(); expect(find.byType(SplashView), findsOneWidget); @@ -49,14 +194,7 @@ void main() { group('SplashView', () { group('SplashStatus.init', () { - setUp(() { - when(() => splashCubit.state) - .thenReturn(const SplashState(status: SplashStatus.init)); - when(() => splashCubit.initialiseApp()).thenAnswer((_) async {}); - }); - testWidgets('only one BasePage widget is rendered', (tester) async { - when(() => flavorCubit.state).thenReturn(FlavorMode.development); await tester.pumpApp(makeTestableWidget()); await tester.pumpAndSettle(); expect(find.byType(BasePage), findsOneWidget); @@ -70,7 +208,7 @@ void main() { final Image image = find.byType(Image).evaluate().single.widget as Image; final AssetImage assetImage = image.image as AssetImage; - expect(assetImage.assetName, equals(ImageStrings.appLogo)); + expect(assetImage.assetName, equals(ImageStrings.appLogoDev)); }); testWidgets('correct image is rendered for staging flavor', @@ -95,52 +233,62 @@ void main() { expect(assetImage.assetName, equals(ImageStrings.appLogo)); }); - // this test fails which does not make sense - // testWidgets('there is only one ScaleTransition widget', (tester) async - // { - // await tester.pumpApp(const SplashView()); - // expect(find.byType(ScaleTransition), findsOneWidget); - // }); - - testWidgets('scaleAnimation Tween is animated correctly', (tester) async { - when(() => flavorCubit.state).thenReturn(FlavorMode.development); + testWidgets('loading progress is animated correctly', (tester) async { + whenListen( + splashCubit, + Stream.fromIterable( + [ + const SplashState( + status: SplashStatus.init, + loadedValue: 0, + ), + const SplashState( + status: SplashStatus.init, + loadedValue: 1, + ), + ], + ), + initialState: const SplashState( + status: SplashStatus.init, + loadedValue: 1, + ), + ); await tester.pumpApp(makeTestableWidget()); - await tester.pump(); + await tester.pump(const Duration(milliseconds: 250)); + + expect(find.byType(LoadingProgress), findsOneWidget); - final initialScale = tester - .widget(find.byKey(const Key('scaleTransition'))); - expect(initialScale.scale.value, 0.2); + final midScale = + tester.widget(find.byType(LoadingProgress)); + expect(midScale.value, 0.5); - final frames = await tester.pumpAndSettle(); - final finalScale = tester - .widget(find.byKey(const Key('scaleTransition'))); - expect(finalScale.scale.value, 1.0); + await tester.pump(const Duration(milliseconds: 250)); - const int animationSecond = 5; - const double expectedFrames = ((animationSecond * 1000) / 100) + 1; - expect(frames, expectedFrames); + final finalScale = + tester.widget(find.byType(LoadingProgress)); + expect(finalScale.value, 1.0); }); }); - group('SplashStatus.onboarding', () { + group('Routing', () { testWidgets( - '''navigates to OnBoardingFirstPage when state is SplashStatus.onboarding''', + '''navigates to StarterPage when state is SplashStatus.routeToOnboarding''', (tester) async { - when(() => flavorCubit.state).thenReturn(FlavorMode.development); - when(() => splashCubit.initialiseApp()).thenAnswer((_) async {}); - whenListen( splashCubit, Stream.fromIterable( - [SplashStatus.init, SplashStatus.routeToPassCode], + [ + const SplashState(status: SplashStatus.init), + const SplashState(status: SplashStatus.routeToOnboarding), + ], ), - initialState: SplashStatus.init, + initialState: const SplashState(status: SplashStatus.init), ); await tester.pumpApp(makeTestableWidget()); await tester.pumpAndSettle(); - //expect(find.byType(OnBoardingStartPage), findsOneWidget); + expect(find.byType(StarterPage), findsOneWidget); }); }); }); diff --git a/test/theme/app_theme_test.dart b/test/theme/app_theme_test.dart index 98b5e6ba0..836980483 100644 --- a/test/theme/app_theme_test.dart +++ b/test/theme/app_theme_test.dart @@ -1,450 +1,104 @@ -import 'package:altme/theme/theme.dart'; +import 'package:altme/theme/app_theme/app_theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:google_fonts/google_fonts.dart'; - -import '../helpers/helpers.dart'; void main() { + setUpAll(TestWidgetsFlutterBinding.ensureInitialized); test('can access AppTheme', () { expect(AppTheme, isNotNull); }); - group('Dark CustomColorScheme', () { - testWidgets('dark color is rendered correctly', (tester) async { - await tester.pumpApp( - MaterialApp( - theme: ThemeData(brightness: Brightness.dark), - home: Builder( - builder: (context) => Column( - children: [ - Container( - key: const Key('transparent'), - color: Theme.of(context).colorScheme.transparent, - ), - Container( - key: const Key('leadingButton'), - color: Theme.of(context).colorScheme.leadingButton, - ), - Container( - key: const Key('selectedBottomBar'), - color: Theme.of(context).colorScheme.selectedBottomBar, - ), - Container( - key: const Key('borderColor'), - color: Theme.of(context).colorScheme.borderColor, - ), - Container( - key: const Key('markDownH1'), - color: Theme.of(context).colorScheme.markDownH1, - ), - Container( - key: const Key('markDownH2'), - color: Theme.of(context).colorScheme.markDownH2, - ), - Container( - key: const Key('markDownP'), - color: Theme.of(context).colorScheme.markDownP, - ), - Container( - key: const Key('markDownA'), - color: Theme.of(context).colorScheme.markDownA, - ), - Container( - key: const Key('subtitle1'), - color: Theme.of(context).colorScheme.subtitle1, - ), - Container( - key: const Key('subtitle2'), - color: Theme.of(context).colorScheme.subtitle2, - ), - Container( - key: const Key('profileDummy'), - color: Theme.of(context).colorScheme.profileDummy, - ), - Container( - key: const Key('documentShadow'), - color: Theme.of(context).colorScheme.documentShadow, - ), - Container( - key: const Key('documentShape'), - color: Theme.of(context).colorScheme.documentShape, - ), - Container( - key: const Key('star'), - color: Theme.of(context).colorScheme.star, - ), - Container( - key: const Key('genderIcon'), - color: Theme.of(context).colorScheme.genderIcon, - ), - Container( - key: const Key('activeCredential'), - color: Theme.of(context).colorScheme.activeCredential, - ), - Container( - key: const Key('expiredCredential'), - color: Theme.of(context).colorScheme.expiredCredential, - ), - Container( - key: const Key('revokedCredential'), - color: Theme.of(context).colorScheme.revokedCredential, - ), - Container( - key: const Key('alertErrorMessage'), - color: Theme.of(context).colorScheme.alertErrorMessage, - ), - Container( - key: const Key('alertWarningMessage'), - color: Theme.of(context).colorScheme.alertWarningMessage, - ), - Container( - key: const Key('alertInfoMessage'), - color: Theme.of(context).colorScheme.alertInfoMessage, - ), - Container( - key: const Key('alertSuccessMessage'), - color: Theme.of(context).colorScheme.alertSuccessMessage, - ), - Container( - key: const Key('buttonDisabled'), - color: Theme.of(context).colorScheme.buttonDisabled, - ), - ], - ), - ), - ), - ); - final transparent = - tester.widget(find.byKey(const Key('transparent'))); - expect(transparent.color, Colors.transparent); - - final appBar = tester.widget(find.byKey(const Key('appBar'))); - expect(appBar.color, const Color(0xFF1D1D1D)); - - final backButton = - tester.widget(find.byKey(const Key('backButton'))); - expect(backButton.color, const Color(0xFFADACAC)); - - final selectedBottomBar = - tester.widget(find.byKey(const Key('selectedBottomBar'))); - expect(selectedBottomBar.color, AppTheme.darkOnSurface); - - final borderColor = - tester.widget(find.byKey(const Key('borderColor'))); - expect(borderColor.color, const Color(0xFF3B3A3A)); - - final markDownH1 = - tester.widget(find.byKey(const Key('markDownH1'))); - expect(markDownH1.color, const Color(0xFFDBD8D8)); - - final markDownH2 = - tester.widget(find.byKey(const Key('markDownH2'))); - expect(markDownH2.color, const Color(0xFFDBD8D8)); - - final markDownP = - tester.widget(find.byKey(const Key('markDownP'))); - expect(markDownP.color, const Color(0xFFADACAC)); - - final markDownA = - tester.widget(find.byKey(const Key('markDownA'))); - expect(markDownA.color, const Color(0xff517bff)); - - final subtitle1 = - tester.widget(find.byKey(const Key('subtitle1'))); - expect(subtitle1.color, const Color(0xFFFFFFFF)); - - final subtitle2 = - tester.widget(find.byKey(const Key('subtitle2'))); - expect(subtitle2.color, const Color(0xFF8B8C92)); - - final button = tester.widget(find.byKey(const Key('button'))); - expect(button.color, const Color(0xFFEEEAEA)); - - final profileDummy = - tester.widget(find.byKey(const Key('profileDummy'))); - expect(profileDummy.color, const Color(0xFF212121)); - - final documentShadow = - tester.widget(find.byKey(const Key('documentShadow'))); - expect(documentShadow.color, const Color(0xFF424242)); - - final documentShape = - tester.widget(find.byKey(const Key('documentShape'))); - expect( - documentShape.color, - const Color(0xff3700b3).withOpacity(0.5), - ); - - final star = tester.widget(find.byKey(const Key('star'))); - expect(star.color, const Color(0xFFFFB83D)); - - final genderIcon = - tester.widget(find.byKey(const Key('genderIcon'))); - expect(genderIcon.color, const Color(0xFF212121)); - - final activeCredential = - tester.widget(find.byKey(const Key('activeCredential'))); - expect(activeCredential.color, Colors.green); - - final expiredCredential = - tester.widget(find.byKey(const Key('expiredCredential'))); - expect(expiredCredential.color, Colors.orange); - - final revokedCredential = - tester.widget(find.byKey(const Key('revokedCredential'))); - expect(revokedCredential.color, Colors.red); - - final alertErrorMessage = - tester.widget(find.byKey(const Key('alertErrorMessage'))); - expect(alertErrorMessage.color, Colors.red); - - final alertWarningMessage = tester - .widget(find.byKey(const Key('alertWarningMessage'))); - expect(alertWarningMessage.color, Colors.yellow); - - final alertInfoMessage = - tester.widget(find.byKey(const Key('alertInfoMessage'))); - expect(alertInfoMessage.color, Colors.cyan); - - final alertSuccessMessage = tester - .widget(find.byKey(const Key('alertSuccessMessage'))); - expect(alertSuccessMessage.color, Colors.green); - - final buttonDisabled = - tester.widget(find.byKey(const Key('buttonDisabled'))); - expect(buttonDisabled.color, const Color(0xFF424242)); - }); - }); - - group('CustomTextTheme', () { - testWidgets('custom text theme rendered correctly', (tester) async { - await tester.pumpApp( - MaterialApp( - theme: ThemeData(brightness: Brightness.dark), - home: Builder( - builder: (context) => Column( - children: [ - Text( - 'credentialTitle', - key: const Key('credentialTitle'), - style: Theme.of(context).textTheme.credentialTitle, - ), - Text( - 'credentialDescription', - key: const Key('credentialDescription'), - style: Theme.of(context).textTheme.credentialDescription, - ), - Text( - 'credentialFieldTitle', - key: const Key('credentialFieldTitle'), - style: Theme.of(context).textTheme.credentialFieldTitle, - ), - Text( - 'credentialFieldDescription', - key: const Key('credentialFieldDescription'), - style: Theme.of(context).textTheme.credentialFieldDescription, - ), - Text( - 'learningAchievementTitle', - key: const Key('learningAchievementTitle'), - style: Theme.of(context).textTheme.learningAchievementTitle, - ), - Text( - 'learningAchievementDescription', - key: const Key('learningAchievementDescription'), - style: Theme.of(context) - .textTheme - .learningAchievementDescription, - ), - Text( - 'credentialIssuer', - key: const Key('credentialIssuer'), - style: Theme.of(context).textTheme.credentialIssuer, - ), - Text( - 'imageCard', - key: const Key('imageCard'), - style: Theme.of(context).textTheme.imageCard, - ), - Text( - 'loyaltyCard', - key: const Key('loyaltyCard'), - style: Theme.of(context).textTheme.loyaltyCard, - ), - Text( - 'professionalExperienceAssessmentRating', - key: const Key('professionalExperienceAssessmentRating'), - style: Theme.of(context) - .textTheme - .professionalExperienceAssessmentRating, - ), - Text( - 'voucherOverlay', - key: const Key('voucherOverlay'), - style: Theme.of(context).textTheme.voucherOverlay, - ), - ], - ), - ), - ), - ); - - final brand = tester.widget(find.byKey(const Key('brand'))); - expect( - brand.style, - GoogleFonts.poppins( - color: const Color(0xFFFFFFFF), - fontSize: 28, - fontWeight: FontWeight.w400, - ), - ); - - final credentialTitle = - tester.widget(find.byKey(const Key('credentialTitle'))); - expect( - credentialTitle.style, - GoogleFonts.poppins( - color: const Color(0xFF424242), - fontSize: 14, - fontWeight: FontWeight.bold, - ), - ); - - final credentialDescription = - tester.widget(find.byKey(const Key('credentialDescription'))); - expect( - credentialDescription.style, - GoogleFonts.poppins( - color: const Color(0xFF757575), - fontSize: 14, - fontWeight: FontWeight.bold, - ), - ); - - final credentialFieldTitle = - tester.widget(find.byKey(const Key('credentialFieldTitle'))); - expect( - credentialFieldTitle.style, - GoogleFonts.poppins( - color: const Color(0xff212121), - fontSize: 12, - fontWeight: FontWeight.w400, - ), - ); - - final credentialFieldDescription = tester - .widget(find.byKey(const Key('credentialFieldDescription'))); - expect( - credentialFieldDescription.style, - GoogleFonts.poppins( - color: const Color(0xff212121), - fontSize: 13, - fontWeight: FontWeight.w600, - ), - ); - - final learningAchievementTitle = tester - .widget(find.byKey(const Key('learningAchievementTitle'))); - expect( - learningAchievementTitle.style, - GoogleFonts.poppins( - color: const Color(0xff212121), - fontSize: 12, - fontWeight: FontWeight.w600, - ), - ); - - final learningAchievementDescription = tester.widget( - find.byKey(const Key('learningAchievementDescription')), - ); - expect( - learningAchievementDescription.style, - GoogleFonts.poppins( - color: const Color(0xff212121), - fontSize: 12, - fontWeight: FontWeight.w400, - ), - ); - - final credentialIssuer = - tester.widget(find.byKey(const Key('credentialIssuer'))); - expect( - credentialIssuer.style, - GoogleFonts.poppins( - color: const Color(0xff212121), - fontSize: 13, - fontWeight: FontWeight.w500, - ), - ); - - final imageCard = tester.widget(find.byKey(const Key('imageCard'))); - expect( - imageCard.style, - GoogleFonts.poppins( - color: const Color(0xff212121), - fontSize: 12, - fontWeight: FontWeight.w500, - ), - ); - - final loyaltyCard = - tester.widget(find.byKey(const Key('loyaltyCard'))); - expect( - loyaltyCard.style, - GoogleFonts.poppins( - color: const Color(0xffffffff), - fontSize: 13, - fontWeight: FontWeight.w600, - ), - ); - - final professionalExperienceAssessmentRating = tester.widget( - find.byKey(const Key('professionalExperienceAssessmentRating')), - ); - expect( - professionalExperienceAssessmentRating.style, - GoogleFonts.poppins( - color: const Color(0xff212121), - fontSize: 13, - fontWeight: FontWeight.w500, - ), - ); - - final voucherOverlay = - tester.widget(find.byKey(const Key('voucherOverlay'))); - expect( - voucherOverlay.style, - GoogleFonts.poppins( - color: const Color(0xffFFFFFF), - fontSize: 13, - fontWeight: FontWeight.w500, - ), - ); - - final ecole42LearningAchievementStudentIdentity = tester.widget( - find.byKey(const Key('ecole42LearningAchievementStudentIdentity')), - ); - expect( - ecole42LearningAchievementStudentIdentity.style, - GoogleFonts.poppins( - color: const Color(0xff212121), - fontSize: 6, - fontWeight: FontWeight.w700, - ), - ); - - final ecole42LearningAchievementLevel = tester.widget( - find.byKey(const Key('ecole42LearningAchievementLevel')), - ); - expect( - ecole42LearningAchievementLevel.style, - GoogleFonts.poppins( - color: const Color(0xff212121), - fontSize: 5, - fontWeight: FontWeight.w700, - ), - ); - }); + test('CustomColorScheme Test', () { + const colorScheme = ColorScheme.dark(); + + expect(colorScheme.error, const Color(0xFFFF0045)); + expect(colorScheme.onTertiary, const Color(0xFF00B267)); + expect(colorScheme.error, const Color(0xFFFF0045)); + expect(colorScheme.onErrorContainer, const Color(0xFFFF5F0A)); + expect(colorScheme.primary, const Color(0xFF2C7DF7)); + expect(colorScheme.onSurface.withOpacity(0.6), const Color(0xFFD1CCE3)); + expect(colorScheme.onSurface.withOpacity(0.6), const Color(0xFF86809D)); + expect(colorScheme.secondary, const Color(0xFF5F556F)); + expect(colorScheme.surface, const Color(0xff271C38)); + expect(colorScheme.surface, const Color(0xFF251F38)); + expect(colorScheme.surface, const Color(0xFF322643)); + expect(colorScheme.onSurface.withOpacity(0.6), const Color(0xFFA79ABA)); + expect(colorScheme.primary, const Color(0xFF0045FF)); + expect(colorScheme.onTertiary, const Color(0xFF00B267)); + expect(colorScheme.onSurface.withOpacity(0.12), equals(Colors.grey[200])); + expect(colorScheme.onSurface, equals(Colors.white)); + expect(Colors.transparent, equals(Colors.transparent)); + expect(colorScheme.onSurface.withOpacity(0.12), const Color(0xFF6A5F7B)); + expect(colorScheme.onSurface.withOpacity(0.38), const Color(0xFF000000)); + expect(colorScheme.surface, const Color(0xff0A0F19)); + expect(colorScheme.surface, const Color(0xff25095B)); + expect(Colors.transparent, equals(Colors.transparent)); + expect(colorScheme.primary, const Color(0xFF6600FF)); + expect(colorScheme.onPrimary, equals(Colors.white)); + expect(colorScheme.surface, const Color(0xff25095B)); + expect(colorScheme.surface, equals(colorScheme.surface)); + expect( + colorScheme.surface.withOpacity(0.07), + equals(const Color(0xff707070).withOpacity(0.07)), + ); + expect(colorScheme.surface, const Color(0xff232630)); + expect(colorScheme.onSurface, equals(Colors.white)); + expect(colorScheme.onSurface.withOpacity(0.6), const Color(0xff86809D)); + expect(colorScheme.onSurface, const Color(0xffF1EFF8)); + expect(colorScheme.surface, equals(colorScheme.surface)); + expect(colorScheme.surface, const Color(0xff0B0514)); + expect(colorScheme.onSurface.withOpacity(0.12), const Color(0xFFDDCEF4)); + expect( + colorScheme.onSurface.withOpacity(0.2), + const Color(0xFFFFFFFF).withOpacity(0.2), + ); + expect(colorScheme.onSurface, equals(Colors.white)); + expect(colorScheme.onSurface, equals(Colors.white)); + expect(colorScheme.onSurface.withOpacity(0.6), const Color(0xFFD1CCE3)); + expect(colorScheme.primary, const Color(0xff517bff)); + expect(colorScheme.onSurface, equals(Colors.white)); + expect(colorScheme.onSurface.withOpacity(0.6), const Color(0xFF8B8C92)); + expect(colorScheme.onSurface, const Color(0xFF212121)); + expect(colorScheme.onSurface.withOpacity(0.12), const Color(0xFF424242)); + expect( + colorScheme.primary.withOpacity(0.05), + equals(const Color(0xff3700b3).withOpacity(0.05)), + ); + expect(colorScheme.onErrorContainer, const Color(0xFFFFB83D)); + expect(colorScheme.onSurface, const Color(0xFF212121)); + expect(colorScheme.onTertiary, equals(Colors.green)); + expect(colorScheme.onErrorContainer, equals(Colors.orange)); + expect(colorScheme.error, equals(Colors.red)); + expect(colorScheme.onSurface.withOpacity(0.38), const Color(0xFF424242)); + expect(colorScheme.error, equals(Colors.red)); + expect(colorScheme.onErrorContainer, equals(Colors.yellow)); + expect(colorScheme.outline, equals(Colors.cyan)); + expect(colorScheme.onTertiary, equals(Colors.green)); + expect(colorScheme.surface, const Color(0xff2B1C48)); + expect( + colorScheme.shadow.withOpacity(0.16), + const Color(0xff000000).withOpacity(0.16), + ); + expect(colorScheme.primary, const Color(0xff430F91)); + expect(colorScheme.onSurface, const Color(0xffF5F5F5)); + expect(colorScheme.secondaryContainer, const Color(0xFF280164)); + expect(colorScheme.surface, const Color(0xFF211F33)); + expect( + colorScheme.onSurface.withOpacity(0.15), + equals(Colors.grey.withOpacity(0.15)), + ); + expect(colorScheme.primary, const Color(0xff18ACFF)); + expect(colorScheme.primaryContainer, const Color(0xff6600FF)); + expect(colorScheme.onSurface.withOpacity(0.12), const Color(0xff524B67)); + expect(colorScheme.surface, const Color(0xff322643)); + expect(colorScheme.surface, const Color(0xff322643)); + expect(colorScheme.onSurface, const Color(0xffD1CCE3)); + expect(colorScheme.onSurface, const Color(0xffFFFFFF)); + expect(colorScheme.onSurface.withOpacity(0.6), const Color(0xFF616161)); + expect(colorScheme.onSurface, const Color(0xFF212121)); + expect(colorScheme.onTertiary, const Color(0xFF08B530)); + expect(colorScheme.error, const Color(0xFFFF0045)); + expect(colorScheme.onSurface.withOpacity(0.6), const Color(0xff86809D)); + expect(colorScheme.surface, const Color(0xFF211F33)); }); }